实验报告
思考题
Thinking 1.1
1
2
3
4
5
6
7
8
|
#include <stdio.h>
int main() {
int a = 1;
int b = 2;
int c = a + b;
printf("%d\n",c);
return 0;
}
|
gcc 与 readelf



可以发现readelf在不使用mips交叉编译工具链的时候也可以正确阅读由mips交叉编译工具链编译得到的内容。
objdump
用原生x86的objdump反编译gccmain.o结果如下。
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
gccmain.o: 文件格式 elf64-x86-64
Disassembly of section .text:
0000000000000000 <main>:
0: f3 0f 1e fa endbr64
4: 55 push %rbp
5: 48 89 e5 mov %rsp,%rbp
8: 48 83 ec 10 sub $0x10,%rsp
c: c7 45 f4 01 00 00 00 movl $0x1,-0xc(%rbp)
13: c7 45 f8 02 00 00 00 movl $0x2,-0x8(%rbp)
1a: 8b 55 f4 mov -0xc(%rbp),%edx
1d: 8b 45 f8 mov -0x8(%rbp),%eax
20: 01 d0 add %edx,%eax
22: 89 45 fc mov %eax,-0x4(%rbp)
25: 8b 45 fc mov -0x4(%rbp),%eax
28: 89 c6 mov %eax,%esi
2a: 48 8d 05 00 00 00 00 lea 0x0(%rip),%rax # 31 <main+0x31>
31: 48 89 c7 mov %rax,%rdi
34: b8 00 00 00 00 mov $0x0,%eax
39: e8 00 00 00 00 call 3e <main+0x3e>
3e: b8 00 00 00 00 mov $0x0,%eax
43: c9 leave
44: c3 ret
Disassembly of section .rodata:
0000000000000000 <.rodata>:
0: 25 .byte 0x25
1: 64 0a 00 or %fs:(%rax),%al
Disassembly of section .comment:
0000000000000000 <.comment>:
0: 00 47 43 add %al,0x43(%rdi)
3: 43 3a 20 rex.XB cmp (%r8),%spl
6: 28 55 62 sub %dl,0x62(%rbp)
9: 75 6e jne 79 <main+0x79>
b: 74 75 je 82 <main+0x82>
d: 20 31 and %dh,(%rcx)
f: 33 2e xor (%rsi),%ebp
11: 33 2e xor (%rsi),%ebp
13: 30 2d 36 75 62 75 xor %ch,0x75627536(%rip) # 7562754f <main+0x7562754f>
19: 6e outsb %ds:(%rsi),(%dx)
1a: 74 75 je 91 <main+0x91>
1c: 32 7e 32 xor 0x32(%rsi),%bh
1f: 34 2e xor $0x2e,%al
21: 30 34 29 xor %dh,(%rcx,%rbp,1)
24: 20 31 and %dh,(%rcx)
26: 33 2e xor (%rsi),%ebp
28: 33 2e xor (%rsi),%ebp
2a: 30 00 xor %al,(%rax)
Disassembly of section .note.gnu.property:
0000000000000000 <.note.gnu.property>:
0: 04 00 add $0x0,%al
2: 00 00 add %al,(%rax)
4: 10 00 adc %al,(%rax)
6: 00 00 add %al,(%rax)
8: 05 00 00 00 47 add $0x47000000,%eax
d: 4e 55 rex.WRX push %rbp
f: 00 02 add %al,(%rdx)
11: 00 00 add %al,(%rax)
13: c0 04 00 00 rolb $0x0,(%rax,%rax,1)
17: 00 03 add %al,(%rbx)
19: 00 00 add %al,(%rax)
1b: 00 00 add %al,(%rax)
1d: 00 00 add %al,(%rax)
...
Disassembly of section .eh_frame:
0000000000000000 <.eh_frame>:
0: 14 00 adc $0x0,%al
2: 00 00 add %al,(%rax)
4: 00 00 add %al,(%rax)
6: 00 00 add %al,(%rax)
8: 01 7a 52 add %edi,0x52(%rdx)
b: 00 01 add %al,(%rcx)
d: 78 10 js 1f <.eh_frame+0x1f>
f: 01 1b add %ebx,(%rbx)
11: 0c 07 or $0x7,%al
13: 08 90 01 00 00 1c or %dl,0x1c000001(%rax)
19: 00 00 add %al,(%rax)
1b: 00 1c 00 add %bl,(%rax,%rax,1)
1e: 00 00 add %al,(%rax)
20: 00 00 add %al,(%rax)
22: 00 00 add %al,(%rax)
24: 45 00 00 add %r8b,(%r8)
27: 00 00 add %al,(%rax)
29: 45 0e rex.RB (bad)
2b: 10 86 02 43 0d 06 adc %al,0x60d4302(%rsi)
31: 7c 0c jl 3f <main+0x3f>
33: 07 (bad)
34: 08 00 or %al,(%rax)
...
|
用MIPS交叉编译工具链的objdump反编译mipsgccmain.o结果如下。
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
mipsgccmain.o: 文件格式 elf32-tradbigmips
Disassembly of section .text:
00000000 <main>:
0: 27bdffd0 addiu sp,sp,-48
4: afbf002c sw ra,44(sp)
8: afbe0028 sw s8,40(sp)
c: 03a0f025 move s8,sp
10: 3c1c0000 lui gp,0x0
14: 279c0000 addiu gp,gp,0
18: afbc0010 sw gp,16(sp)
1c: 24020001 li v0,1
20: afc2001c sw v0,28(s8)
24: 24020002 li v0,2
28: afc20020 sw v0,32(s8)
2c: 8fc3001c lw v1,28(s8)
30: 8fc20020 lw v0,32(s8)
34: 00621021 addu v0,v1,v0
38: afc20024 sw v0,36(s8)
3c: 8fc50024 lw a1,36(s8)
40: 3c020000 lui v0,0x0
44: 24440000 addiu a0,v0,0
48: 8f820000 lw v0,0(gp)
4c: 0040c825 move t9,v0
50: 0320f809 jalr t9
54: 00000000 nop
58: 8fdc0010 lw gp,16(s8)
5c: 00001025 move v0,zero
60: 03c0e825 move sp,s8
64: 8fbf002c lw ra,44(sp)
68: 8fbe0028 lw s8,40(sp)
6c: 27bd0030 addiu sp,sp,48
70: 03e00008 jr ra
74: 00000000 nop
...
Disassembly of section .reginfo:
00000000 <.reginfo>:
0: f200003c .word 0xf200003c
...
Disassembly of section .MIPS.abiflags:
00000000 <.MIPS.abiflags>:
0: 00002002 srl a0,zero,0x0
4: 01010005 lsa zero,t0,at,0x1
...
Disassembly of section .pdr:
00000000 <.pdr>:
0: 00000000 nop
4: c0000000 ll zero,0(zero)
8: fffffffc .word 0xfffffffc
...
14: 00000030 tge zero,zero
18: 0000001e .word 0x1e
1c: 0000001f .word 0x1f
Disassembly of section .rodata:
00000000 <.rodata>:
0: 25640a00 addiu a0,t3,2560
...
Disassembly of section .comment:
00000000 <.comment>:
0: 00474343 .word 0x474343
4: 3a202855 xori zero,s1,0x2855
8: 62756e74 .word 0x62756e74
c: 75203132 jalx 480c4c8 <main+0x480c4c8>
10: 2e332e30 sltiu s3,s1,11824
14: 2d313775 sltiu s1,t1,14197
18: 62756e74 .word 0x62756e74
1c: 75312920 jalx 4c4a480 <main+0x4c4a480>
20: 31322e33 andi s2,t1,0x2e33
24: Address 0x24 is out of bounds.
Disassembly of section .gnu.attributes:
00000000 <.gnu.attributes>:
0: 41000000 mftc0 zero,c0_index
4: 0f676e75 jal d9db9d4 <main+0xd9db9d4>
8: 00010000 sll zero,at,0x0
c: 00070405 .word 0x70405
|
由于CO课掌握的MIPS代码基础,本人阅读mips交叉编译工具链的反编译代码时更为流畅。
objdump的参数含义
通过阅读man objdump
给出的内容,我们可以得知一些参数信息

下面举出几例简单的:
参数 |
作用 |
-e或-g |
显示调试信息 |
-D |
反汇编所有section |
-r |
显示文件的重定位入口 |
-S |
尽可能反汇编出源代码 |
-z |
将0也进行反汇编 |
我传入的参数-DS便是反编译所有section,并且同时显示反编译代码与源代码
Thinking 1.2
用readelf解析mos
下图是用自己的readelf阅读mos的结果

用readelf解析文件本身
我们发现运行没有任何输出结果。
根据hint,我们使用readelf -h来研究文件头。
下图是可被自己的readelf
分析的mos与hello的信息。

下图是不可被自己的readelf
分析的readelf.o的信息。

可以发现他们的类别分别是ELF32与ELF64。
那么问题就迎刃而解了,我们清晰地记得,在完成readelf.c的书写时,我们采用的都是ELF32类型的数据结构。
因此我们的readelf不能用来解析身为ELF64的readelf.o文件。
Thinking 1.3
指导书上的启动过程:

为什么操作系统的内核入口并没有放在上电启动地址,还能保证内核入口被正确跳转到?
因为内核入口和上电启动地址本来就是没有关系的。
上电启动位置是用于启动bootloader,实现硬件初始化等内容。
而bootloader所加载的内容才决定了内核的入口,也就是说我们在start.S
中所写的_start函数即EXPORT(_start)才是mips_init的入口。

难点分析
本次实验的核心内容:
- 认识elf文件
- _start函数
- printk函数与vprintfmt函数
认识elf文件
什么是elf文件
.elf
包含.exe
,.o
和.so
(不过linux对扩展名要求并不严格,扩展名不能用来判断文件类型!)
准确来说应该是:
可执行文件
,可重定位文件
和共享对象文件
。
也就是说我们可以用readelf来解析我们在tools/readelf里的hello,readelf,readelf.o, main.o等等多种文件。
在elf.h中,我们对于ELF32的种种内容做出了定义。
具体来说便是Elf32_Ehdr
Elf32_Shdr
Elf32_Phdr
这三个结构体类型

Elf32_Ehdr描述ELF头,Elf32_Shdr描述节(Section)头表,Elf32_Phdr描述段(Segment)头表,又名程序(Program)头表。
如何实现readelf
我们自己实现的readelf,是只能解析ELF32的section地址的可执行程序。
功能很简单,只需要从elf文件本身拿到我们想要的信息并输出。

根据代码逻辑,你需要知道sh_entry_count,并通过shdr这一指针去定位到各个section,从而输出其地址。
现根据ELF头得到节头表的位置、节头数量等信息,然后让sh_table指针去指向节头表第一项的地址:binary + ehdr->e_shoff
。
接下来循环输出即可,其中得到节头表下项的方法,只需要将sh_table这一指针变为(Elf32_Shdr *)类型,然后加1即可。也可以用上sh_entry_size,直接加上这个数值实现地址空间的移动。
如何链接elf文件
只需要将所有的section放在一起,尤其是
- .text 代码段
- .data 需要被初始化的全局变量与静态变量
- .bss 未被初始化的全局变量与静态变量
放在一起的方法:
.
= 一个地址
.text:{*(.text)}
把所有.text
依次放在.
的位置,其他section同理
若不再修改.
则继续顺延放置。
若修改,就以新的地址继续顺延放置。
_start!
读这道题时会发现有很多宏定义的内容。

asm.h
通过阅读asm/asm.h
,其定义了EXPORT()

发现它真的只是一个标签而已;
其中又include了asm/cp0regdef.h
与asm/regdef.h
cp0regdef.h
CP0寄存器的一些define

regdef.h
寄存器堆的一些define

这就解释了很多看似是无厘头的简写,实则都有依据的内容
当然还有mmu.h
为我们的KSTACKTOP
提供了define
而mips_init可以直接使用,则是因为程序经过预处理编译等等后,函数名也会变为标签,并和start.S的.text段组合在一起,那自然就可以访问到了。
printk
本次实验代码量最大的一环,核心内容就是书写vprintfmt函数。
简单了解va变长数组的基本方法后,我们便可以开始了解这一函数。
了解vprintfmt
首先,vprintfmt的参数是out函数指针,data指针,fmt字符串,ap数组。
out函数指针能实现在同一函数(vprintfmt)中调用不同形式的out函数

out函数可以实现输出以buf为起点,长度为len的字符串。
data指针指向out紧挨的内存空间,用于存储additional output sink-specific data(尚不知道有何用)
fmt是格式字符串,ap则是%
对应的参数列表,通过va_arg(ap, type)来获取内容
实现vprintfmt
核心处理当然在于fmt的解析
在printf("%d + %d = %d\n", a, b, c);
中我们知道,fmt字符串中的非占位符
直接输出,%xxx
则要被变量所替换。那么分析的核心就是找到%并解析%后面的内容。
整体逻辑是一个大循环
然后需要一个小循环内寻找%,找不到就length++,
找到了就把之前length长的内容全部out出来。

s的作用是更新起始点到进入当前小循环时的fmt。
为了避免一直就没有%无法输出,走出小循环后要再执行一次out函数。如果有%的话也不必担心,因为length就已经更新到0了。
接下来就该结束大循环了。如果到达了\0,就break。
如果survive了这次break,说明你找到了%, 那接下来就是通过对%后内容的解析来改变参数。
后续的内容难度就不大了。
需要注意,10进制有符号数输出时会改动neg_flag
因为
它用的是unsigned long!
更基本的函数
也就是print_char
print_str
和 print_num
,他们是直接由out函数实现的,用于为vprintfmt提供方法,因此也需要简单了解。
实验体会
整个实验给我的感觉就是浩大,一时间突然接手了这么多文件,会不知道该看什么,谁是重点(因为每个文件都是必要的)
那么我们容易做到的,就是跟着实验指导书一点点去探索,从表面的应用层逐渐向内挖掘到实现层,从而了解各个文件的内容。
倘若研究完lab1,可以搞清楚整个目录下的主要文件的功能与服务关系,我想这对于操作系统体系结构的理解应该是有好处的。
笑传:笔者打完这行字之后自动输入了Esc :wq 丝滑小连招 