Featured image of post 北航OS Lab1 内核、启动与printk

北航OS Lab1 内核、启动与printk

实验报告

实验报告

思考题

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

原生x86

MIPS交叉编译工具链

MIPS交叉编译工具链

可以发现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给出的内容,我们可以得知一些参数信息

man objdump

下面举出几例简单的:

参数 作用
-e或-g 显示调试信息
-D 反汇编所有section
-r 显示文件的重定位入口
-S 尽可能反汇编出源代码
-z 将0也进行反汇编

我传入的参数-DS便是反编译所有section,并且同时显示反编译代码与源代码

Thinking 1.2

用readelf解析mos

下图是用自己的readelf阅读mos的结果

readelf mos

用readelf解析文件本身

我们发现运行没有任何输出结果。

根据hint,我们使用readelf -h来研究文件头。

下图是可被自己的readelf分析的mos与hello的信息。
alt text

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

可以发现他们的类别分别是ELF32与ELF64。

那么问题就迎刃而解了,我们清晰地记得,在完成readelf.c的书写时,我们采用的都是ELF32类型的数据结构。

因此我们的readelf不能用来解析身为ELF64的readelf.o文件。

Thinking 1.3

指导书上的启动过程:

启动

为什么操作系统的内核入口并没有放在上电启动地址,还能保证内核入口被正确跳转到?

因为内核入口和上电启动地址本来就是没有关系的。

上电启动位置是用于启动bootloader,实现硬件初始化等内容。
而bootloader所加载的内容才决定了内核的入口,也就是说我们在start.S中所写的_start函数即EXPORT(_start)才是mips_init的入口。

_start函数

难点分析

本次实验的核心内容:

  • 认识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这三个结构体类型

ELF文件结构

Elf32_Ehdr描述ELF头,Elf32_Shdr描述节(Section)头表,Elf32_Phdr描述段(Segment)头表,又名程序(Program)头表。

如何实现readelf

我们自己实现的readelf,是只能解析ELF32的section地址的可执行程序。
功能很简单,只需要从elf文件本身拿到我们想要的信息并输出。

exercise

根据代码逻辑,你需要知道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!

读这道题时会发现有很多宏定义的内容。

exercise

asm.h

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

EXPORT

发现它真的只是一个标签而已;

其中又include了asm/cp0regdef.hasm/regdef.h

cp0regdef.h

CP0寄存器的一些define

cp0

regdef.h

寄存器堆的一些define

reg

这就解释了很多看似是无厘头的简写,实则都有依据的内容

当然还有mmu.h为我们的KSTACKTOP提供了define

而mips_init可以直接使用,则是因为程序经过预处理编译等等后,函数名也会变为标签,并和start.S的.text段组合在一起,那自然就可以访问到了。

printk

本次实验代码量最大的一环,核心内容就是书写vprintfmt函数。

简单了解va变长数组的基本方法后,我们便可以开始了解这一函数。

了解vprintfmt

首先,vprintfmt的参数是out函数指针,data指针,fmt字符串,ap数组。

out函数指针能实现在同一函数(vprintfmt)中调用不同形式的out函数

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
因为
print_num
它用的是unsigned long!

更基本的函数

也就是print_char print_strprint_num,他们是直接由out函数实现的,用于为vprintfmt提供方法,因此也需要简单了解。

实验体会

整个实验给我的感觉就是浩大,一时间突然接手了这么多文件,会不知道该看什么,谁是重点(因为每个文件都是必要的)

那么我们容易做到的,就是跟着实验指导书一点点去探索,从表面的应用层逐渐向内挖掘到实现层,从而了解各个文件的内容。

倘若研究完lab1,可以搞清楚整个目录下的主要文件的功能与服务关系,我想这对于操作系统体系结构的理解应该是有好处的。

笑传:笔者打完这行字之后自动输入了Esc :wq 丝滑小连招 alt text

comments powered by Disqus
Easy Life and Easy Learning
使用 Hugo 构建
主题 StackJimmy 设计