我正在学习mit6.828课程,我不明白为什么在lab4启动AP时必须间接调用。我尝试直接调用call mp_main
(源代码:)movl $mp_main, %eax; call *%eax
,但它会导致三重错误。这是部分源代码
###################################################################
# entry point for APs
###################################################################
# Each non-boot CPU ("AP") is started up in response to a STARTUP
# IPI from the boot CPU. Section B.4.2 of the Multi-Processor
# Specification says that the AP will start in real mode with CS:IP
# set to XY00:0000, where XY is an 8-bit value sent with the
# STARTUP. Thus this code must start at a 4096-byte boundary.
#
# Because this code sets DS to zero, it must run from an address in
# the low 2^16 bytes of physical memory.
#
# boot_aps() (in init.c) copies this code to MPENTRY_PADDR (which
# satisfies the above restrictions). Then, for each AP, it stores the
# address of the pre-allocated per-core stack in mpentry_kstack, sends
# the STARTUP IPI, and waits for this code to acknowledge that it has
# started (which happens in mp_main in init.c).
#
# This code is similar to boot/boot.S except that
# - it does not need to enable A20
# - it uses MPBOOTPHYS to calculate absolute addresses of its
# symbols, rather than relying on the linker to fill them
# Switch to the per-cpu stack allocated in boot_aps()
movl mpentry_kstack, %esp
movl $0x0, %ebp # nuke frame pointer
# Call mp_main(). (Exercise for the reader: why the indirect call?)
movl $mp_main, %eax
call *%eax
我不知道如何在多 CPU 中使用 gdb 进行调试,尤其是在启动时,但 entry.S 与此代码类似,并且很容易调试。这是entry.S键码
mov $relocated, %eax
jmp *%eax
relocated:
movl $0x0,%ebp # nuke frame pointer
下面是正确代码的 gdb 输出。之前jmp *%eax
,eip 是低地址,但之后 eip 是高地址。下一条指令是mov $0, %ebp
.
(gdb) si
=> 0x10002d: jmp *%eax
0x0010002d in ?? ()
(gdb) info reg eax eip
eax 0xf010002f -267386833
eip 0x10002d 0x10002d
(gdb) si
=> 0xf010002f: mov $0x0,%ebp
0xf010002f in ?? ()
(gdb) info reg eax eip
eax 0xf010002f -267386833
eip 0xf010002f 0xf010002f
如果我们更改为 directcall relocated
而不是mov $relocated, %eax; jmp *%eax
,则会导致三重错误,并且 gdb 输出:
(gdb)
=> 0x100028: call 0x10002d
0x00100028 in ?? ()
(gdb) info reg eip
eip 0x100028 0x100028
(gdb) si
=> 0x100028: call 0x10002d
0x00100028 in ?? ()
如果我们查看 objdump 输出
call relocated
f0100028: e8 00 00 00 00 call f010002d <relocated>
f010002d <relocated>:
relocated:
# Clear the frame pointer register (EBP)
# so that once we get into debugging C code,
# stack backtraces will be terminated properly.
movl $0x0,%ebp # nuke frame pointer
f010002d: bd 00 00 00 00 mov $0x0,%ebp
这里虚拟地址 [0xf0000000, 0xf0000000+4MB) 和 [0, 4MB) 都映射到物理地址 [0, 4MB)
我想知道间接调用如何改变这一点。您可以在 github 中找到完整代码,只需在 kern/entry.S 和 kern/mpentry.S 中搜索“mit6.828”