Homework: boot xv6
boot XV6
首先搭建相应的实现环境,具体可以参考实验环境搭建。
按照下面的指令进行clone和编译:
Fetch the xv6 source:
$ mkdir 6.828
$ cd 6.828
$ git clone git://github.com/mit-pdos/xv6-public.git
Build xv6 on Athena:
$ cd xv6-public
$ make
Finding and breaking at an address
这一部分就是让我们找到相关的函数的地址,并且在这个地址打上断点,便于之后的调试。
这里以内核的启动入口为例,首先我们使用nm
1工具找到内核的入口0x0010000c
,之后在这个地址打上断点br * 0x0010000c
,之后就能通过gdb的si
指令进行单步的调试了。
关于这个内核的启动地址为什么是0x100000c
,这个在kernel.ld
固定设置,并且是一个不成文的规定。
# 一个终端:
make qemu-gdb
# 另外一个终端:
make gdb
Exercise: What is on the stack?
本部分希望我们了解函数调用时栈发生了什么变化。
Q1:什么时候初始化了栈空间?
bootasm.S: movl $start, %esp # $start==0x7c00
26│ 0x7c43: mov $0x7c00,%sp
27│ 0x7c46: add %al,(%bx,%si)
28├──> 0x7c48: call 0x7d39
29│ 0x7c4b: add %al,(%bx,%si)
30│ 0x7c4d: mov $0x89668a00,%eax
31│ 0x7c53: ret $0xef66
32│ 0x7c56: mov $0xef668ae0,%eax
33│ 0x7c5c: jmp 0x7c5c
34│ 0x7c5e: xchg %eax,%eax
35│ 0x7c60: add %al,(%bx,%si)
36│ 0x7c62: add %al,(%bx,%si)
37│ 0x7c64: add %al,(%bx,%si)
38│ 0x7c66: add %al,(%bx,%si)
39│ 0x7c68: (bad)
40│ 0x7c69: incw (%bx,%si)
41│ 0x7c6b: add %al,(%bx,%si)
** 0x7c00: cli (7c00 - 7cea) **
gs 0x0 0
(gdb) b *0x7c48
Breakpoint 2 at 0x7c48
(gdb) c
Continuing.
The target architecture is assumed to be i386
=> 0x7c48: call 0x7d3b
Breakpoint 2, 0x00007c48 in ?? ()
(gdb) info reg
eax 0x0 0
ecx 0x0 0
edx 0x80 128
ebx 0x0 0
esp 0x7c00 0x7c00
ebp 0x0 0x0
esi 0x0 0
edi 0x0 0
eip 0x7c48 0x7c48
eflags 0x6 [ PF ]
cs 0x8 8
ss 0x10 16
ds 0x10 16
es 0x10 16
fs 0x0 0
gs 0x0 0
(gdb)
Q2: 单步执行到bootmain,看看栈发生了什么?
call bootmain
7c48: e8 ee 00 00 00 call 7d3b <bootmain>
此刻esp = 0x7c00变化为esp = 0x7bfc,并且里面保存的值为0x7c4d,连保存的是call bootmain的后面一个语句的执行地址。
之后是一系列的压栈操作:
7d3b: 55 push %ebp
7d3c: 89 e5 mov %esp,%ebp
7d3e: 57 push %edi
7d3f: 56 push %esi
7d40: 53 push %ebx
7d41: 83 ec 0c sub $0xc,%esp
此时esp = 0x7be0, ebp=0x7bf8。
Q3: 第一个操作栈的指令是什么?
7d3b: 55 push %ebp
7d3c: 89 e5 mov %esp,%ebp
可以看到是对原来的ebp进行保存,并且将原来的esp当作新的函数调用的栈底。
Q4:调用到0x10000c之前,栈寄存器发生了什么?
我们主要理解栈里面填充的内容是哪些指令来的。
之后我们在调用kernel入口函数之前打上一个断点:
发现期间函数栈没有发生变化。
执行完call指令之后,发现还是相同的套路,esp = esp-4,并且保存的值是call 指令后面的那个地址,即 0x7db4
。
红框中的值是两个函数调用的返回地址值,期间的若干值是下面的指令产生的:
7d3b: 55 push %ebp
7d3c: 89 e5 mov %esp,%ebp
7d3e: 57 push %edi
7d3f: 56 push %esi
7d40: 53 push %ebx
7d41: 83 ec 0c sub $0xc,%esp
...
7d82: ff 73 04 pushl 0x4(%ebx)
7d85: ff 73 10 pushl 0x10(%ebx)
7d88: 57 push %edi
正好是7个push的指令。
1. nm - list symbols from object files ↩