date: 2019-02-14
tags: OS 6.828
没什么需要做的,都在配置的那片里面介绍过了。
首先按照要求,运行:
$ 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
如下是寄存器存有的信息:
(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.S
的call bootmain
之上,把$start
(具体值是0x7c00)付给%esp
movl $start, %esp
call bootmain
也就是在运行bootmain
之前,%esp
的值为0x7c00
,0x7c00
之后的地址都是无用的,所以实际上上面有用的栈是:
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的%ebp
为0x7bf8,
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
函数返回会退出来很多。