本文最后更新于 1 年前,文中所描述的信息可能已发生改变。
本文内容分析于 2023 年 12 月 18 日。今整理成文。
先看源代码:kern/mpentry.S:76:
asm
# Call mp_main(). (Exercise for the reader: why the indirect call?)
movl $mp_main, %eax
call *%eax灵魂提问:why the indirect call?
直接调用的 call 指令,使用 %eip 相对寻址。而 call 指令的偏移量,是在链接时确定的。但 mpentry.S 链接地址和运行地址不一样(复制到 0x7000 再执行),这使得链接时确定的偏移量在运行时是错误的。因此,只能使用间接调用,将 mp_main 的绝对地址加载到 %eax 中,然后 call *%eax。
知道了原理,就可以改动链接脚本,使 mpentry_start 到 mp_main 通过 call 指令直接调用。
上面两句汇编改成:
asm
# Call mp_main(). (Exercise for the reader: why the indirect call?)
movl $mp_main, %eax
call *%eax
call true_mp_main // [!code ++]链接脚本 kern/kernel.ld:
txt
.text : AT(0x100000) {
*(.text .stub .text.* .gnu.linkonce.t.*)
PROVIDE(true_mp_main = mp_main + (0xF0100000 + mpentry_start - 0x7000)); // [!code ++]
*(.text .stub .text.* .gnu.linkonce.t.*)
}