OS-lab1

OWPETER Lv3

思考题

Thinking 1.1

objdump的常用参数有如下一些:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
-C
--demangle
将底层的符号名解码成用户级名字,除了去掉所开头的下划线之外,还使得C++函数名以可理解的方式显示出来。

--debugging
-g
显示调试信息。企图解析保存在文件中的调试信息并以C语言的语法显示出来。仅仅支持某些类型的调试信息。有些其他的格式被readelf -w支持。

-e
--debugging-tags
类似-g选项,但是生成的信息是和ctags工具相兼容的格式。

--disassemble
-d
从objfile中反汇编那些特定指令机器码的section。

-D
--disassemble-all
与 -d 类似,但反汇编所有section.

-f
--file-headers
显示objfile中每个文件的整体头部摘要信息。

-h
--section-headers
--headers
显示目标文件各个section的头部摘要信息。

-i
--info
显示对于 -b 或者 -m 选项可用的架构和目标格式列表。

-j name
--section=name
仅仅显示指定名称为name的section的信息

-l
--line-numbers
用文件名和行号标注相应的目标代码,仅仅和-d、-D或者-r一起使用使用-ld和使用-d的区别不是很大,在源码级调试的时候有用,要求编译时使用了-g之类的调试编译选项。

-m machine
--architecture=machine
指定反汇编目标文件时使用的架构,当待反汇编文件本身没描述架构信息的时候(比如S-records),这个选项很有用。可以用-i选项列出这里能够指定的架构.

-s
--full-contents
显示指定section的完整内容。默认所有的非空section都会被显示。

-S
--source
尽可能反汇编出源代码,尤其当编译的时候指定了-g这种调试参数时,效果比较明显。隐含了-d参数。

因此objdump -DS的含义是反汇编所有section的内容

我们先新建一个简单的t.c文件,其内容为

1
2
3
4
5
6
#include <stdio.h>

int main() {
printf("hello world");
return 0;
}

首先使用x86工具链对其进行编译与反汇编,得到如下代码

1
2
3
4
5
6
7
8
9
10
11
0000000000001149 <main>:
1149: f3 0f 1e fa endbr64
114d: 55 push %rbp
114e: 48 89 e5 mov %rsp,%rbp
1151: 48 8d 05 ac 0e 00 00 lea 0xeac(%rip),%rax # 2004 <_IO_stdin_used+0x4>
1158: 48 89 c7 mov %rax,%rdi
115b: b8 00 00 00 00 mov $0x0,%eax
1160: e8 eb fe ff ff call 1050 <printf@plt>
1165: b8 00 00 00 00 mov $0x0,%eax
116a: 5d pop %rbp
116b: c3 ret

再使用mips-linux-gnu-交叉编译工具编译与反汇编,得到如下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
00000000 <main>:
0: 27bdffe0 addiu sp,sp,-32
4: afbf001c sw ra,28(sp)
8: afbe0018 sw s8,24(sp)
c: 03a0f025 move s8,sp
10: 3c1c0000 lui gp,0x0
14: 279c0000 addiu gp,gp,0
18: afbc0010 sw gp,16(sp)
1c: 3c020000 lui v0,0x0
20: 24440000 addiu a0,v0,0
24: 8f820000 lw v0,0(gp)
28: 0040c825 move t9,v0
2c: 0320f809 jalr t9
30: 00000000 nop
34: 8fdc0010 lw gp,16(s8)
38: 00001025 move v0,zero
3c: 03c0e825 move sp,s8
40: 8fbf001c lw ra,28(sp)
44: 8fbe0018 lw s8,24(sp)
48: 27bd0020 addiu sp,sp,32
4c: 03e00008 jr ra
50: 00000000 nop
...

Thinking 1.2

使用自己编写的readelf程序解析mos

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
git@23373193:~/23373193/tools/readelf (lab1)$ ./readelf ~/23373193/target/mos
0:0x0
1:0x80020000
2:0x800220e0
3:0x800220f8
4:0x80022110
5:0x0
6:0x0
7:0x0
8:0x0
9:0x0
10:0x0
11:0x0
12:0x0
13:0x0
14:0x0
15:0x0
16:0x0
17:0x0
18:0x0

使用readelf工具解析分别解析我们自己编写的readelf以及hello文件,得到如下结果

1
2
3
4
5
6
7
8
9
10
11
12
13
git@23373193:~/23373193/tools/readelf (lab1)$ readelf -h readelf
ELF 头:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
类别: ELF64
数据: 2 补码,小端序 (little endian)
...

git@23373193:~/23373193/tools/readelf (lab1)$ readelf -h hello
ELF 头:
Magic: 7f 45 4c 46 01 01 01 03 00 00 00 00 00 00 00 00
类别: ELF32
数据: 2 补码,小端序 (little endian)
...

可以发现,hello文件是32位的,我们的readelf是64位的,而我们编写的readelf仅能解析32位的ELF文件

Thinking 1.3

启动第一阶段由bootloader运行,初始化硬件设备,第二阶段运行操作系统内核加载器,执行引导程序。在实验中,我们使用Linker Script控制加载地址,让各个section被加载到指定地址,同时,kernel.lds中指明了程序入口,保证能够正确跳转到操作系统内核

实验难点

ELF文件解析

通过阅读指导书我们发现,节头表和段头表指向相同的位置,因此我们选择更方便遍历的段头表开始进行遍历。通过阅读elf.h,我们发现可以ELF的文件头获得段头表的相对偏移、段头表项数量等信息。之后,我们将节头表指针指向节头表所在位置,根据以上信息开始遍历即可。

vprintfmt

pEw7Wge.png

vprintfmt这个函数的整体逻辑就是这样的

心得体会

本次实验带给我最大的收获应该是教会了我怎么在qemu中使用gdb调试代码。虽然vprintfmt的主题逻辑并不复杂,但是依然有一些小的点需要通过一步一步调试并且跟踪代码的运行过程才能彻底理解。

  • Title: OS-lab1
  • Author: OWPETER
  • Created at : 2025-04-05 22:23:57
  • Updated at : 2025-04-12 14:52:57
  • Link: https://owpeter.github.io/2025/04/05/OS-lab1/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments