zhuzilin's Blog

about

6.828 Homework boot xv6

date: 2019-02-14
tags: OS  6.828  

Boot xv6

没什么需要做的,都在配置的那片里面介绍过了。

Finding and breaking at an address

首先按照要求,运行:

$ nm kernel | grep _start
8010a48c D _binary_entryother_start
8010a460 D _binary_initcode_start
0010000c T _start

查manul可以知道,nm的作用是GNU nm lists the symbols from object files objfile. 所以上面的指令就是找到symbol表中含有_start的地方。对_start处设置断点,并运行到这个位置。注意加了断点之后的状态是还没运行这一行的状态!

(gdb) br * 0x0010000c
Breakpoint 1 at 0x10000c
(gdb) c
Continuing.
The target architecture is assumed to be i386
=> 0x10000c:	mov    %cr4,%eax
  • exercise: 栈上都有什么?

如下是寄存器存有的信息:

(gdb) info reg
eax            0x0      0
ecx            0x0      0
edx            0x1f0    496
ebx            0x10074  65652
esp            0x7bdc   0x7bdc
ebp            0x7bf8   0x7bf8
esi            0x10074  65652
edi            0x0      0
eip            0x10000c 0x10000c
eflags         0x46     [ PF ZF ]
cs             0x8      8
ss             0x10     16
ds             0x10     16
es             0x10     16
fs             0x0      0
gs             0x0      0

寄存器的信息为:

(gdb) x/24x $esp
0x7bdc: 0x00007d8d      0x00000000      0x00000000      0x00000000
0x7bec: 0x00000000      0x00000000      0x00000000      0x00000000
0x7bfc: 0x00007c4d      0x8ec031fa      0x8ec08ed8      0xa864e4d0
0x7c0c: 0xb0fa7502      0xe464e6d1      0x7502a864      0xe6dfb0fa
0x7c1c: 0x16010f60      0x200f7c78      0xc88366c0      0xc0220f01
0x7c2c: 0x087c31ea      0x10b86600      0x8ed88e00      0x66d08ec0

我们的任务是判断哪些部分是真正的栈,且分别是什么意思。

我们从0x7c00开始重新运行程序,在最开始的时候,寄存器的信息是:

(gdb) info reg
eax            0xaa55   43605
ecx            0x0      0
edx            0x80     128
ebx            0x0      0
esp            0x6f20   0x6f20
ebp            0x0      0x0
esi            0x0      0
edi            0x0      0
eip            0x7c00   0x7c00
eflags         0x202    [ IF ]
cs             0x0      0
ss             0x0      0
ds             0x0      0
es             0x0      0
fs             0x0      0
gs             0x0      0

也就是说,最开始的时候esp指向0x6f20。

第一次运行%esp相关的代码,在bootasm.Scall bootmain之上,把$start(具体值是0x7c00)付给%esp

movl    $start, %esp
call	bootmain

也就是在运行bootmain之前,%esp的值为0x7c000x7c00之后的地址都是无用的,所以实际上上面有用的栈是:

0x7bdc: 0x00007d8d      0x00000000      0x00000000      0x00000000
0x7bec: 0x00000000      0x00000000      0x00000000      0x00000000
0x7bfc: 0x00007c4d      

call指令把%eip push进来了。

0x7bfc: 0x00007c4d # bootmain的返回地址

之后进入bootmain,首先运行function prologue,也就是把main外面环境的的%ebp (frame address)存在%esp中,所以

0x7bf8: 0x00000000 # ebp,因为外面没有frame了,是0

以及bootmain的%ebp0x7bf8,info reg`可以证明这点

之后连续进行3个push

(gdb) si
=> 0x7d3e:      push   %edi
0x00007d3e in ?? ()
(gdb) si
=> 0x7d3f:      push   %esi
0x00007d3f in ?? ()
(gdb) si
=> 0x7d40:      push   %ebx
0x00007d40 in ?? ()
(gdb) w/4x $esp
Ambiguous command "w/4x $esp": .
(gdb) x/4x $esp
0x7bf0: 0x00000000      0x00000000      0x00000000      0x00007c4d

所以:

0x7bf4: 0x00000000 # edi
0x7bf0: 0x00000000 # esi
0x7bec: 0x00000000 # ebx

之后

(gdb) si
=> 0x7d41:      sub    $0xc,%esp
0x00007d41 in ?? ()

这之后%esp仅仅缩小了12

0x7be8: 0x00000000 # 空的
0x7be4: 0x00000000 # 空的
0x7be0: 0x00000000 # 空的

之后为readseg做准备,先把3个参数依倒叙传入,进行了三个push

(gdb) si
=> 0x7d44:      push   $0x0
0x00007d44 in ?? ()
(gdb) si
=> 0x7d46:      push   $0x1000
0x00007d46 in ?? ()
(gdb) si
=> 0x7d4b:      push   $0x10000

这时,

# readseg的3个参数
0x7bdc: 0x00000000 # offset
0x7bd8: 0x00001000 # count
0x7bd4: 0x00010000 # pa

然后call readseg

(gdb) si
=> 0x7d50:      call   0x7cf8

依然是会把%eip推进去

0x7bd0: 0x00007d55 # readseg的return address

然后运行readseg的function prologue:

(gdb) si
=> 0x7cf8:      push   %ebp
0x00007cf8 in ?? ()
(gdb) si
=> 0x7cf9:      mov    %esp,%ebp
0x00007cf9 in ?? ()

就会有

0x7bcc: 0x00007bf8 # bootmain的frame address

同事%ebp变为0x7bcc

之后依然是3个push,

(gdb) si
=> 0x7cfb:      push   %edi
0x00007cfb in ?? ()
(gdb) si
=> 0x7cfc:      push   %esi
0x00007cfc in ?? ()
(gdb) si
=> 0x7cfd:      push   %ebx
0x00007cfd in ?? ()

仍然是推进去3个0,

0x7bc8: 0x00000000 # edi
0x7bc4: 0x00000000 # esi
0x7bc0: 0x00000000 # ebx

后面就非常类似了,这里叙述的栈的内容已经超过需要的了,之所以会超过是因为之后readseg函数返回会退出来很多。