我的问题的原因是Starman似乎相信GRUB Legacy作者的解释(请参阅以获取以下莫名其妙的代码:
7C4B EA507C0000 JMP 0000:7C50 ; Long Jump to the next instruction
; because some bogus BIOSes jump to
; 07C0:0000 instead of 0000:7C00.
当我在第一个内存引用上执行英特尔指定的算法来构造有效地址时,我将07C0:乘以16(有效地将其左移四位或半字节)。然后我加上偏移量:0000,得到十进制地址31744。
如果我将第二个存储器引用的段左移四位,我仍然有0000:并且:7C00的偏移量仍然寻址位置31744。因此,我的直觉反应是,这个GRUB Legacy引导扇区代码的作者正在拉我们的腿。不管任何BIOS所做的内存引用的形式如何,如果有效地址计算为十进制31744,那么这个跳远似乎没有问题。
假设代码的作者只是以一种似乎与正确物理位置相同的方式表达了一个虚假的物理内存位置,我开始思考如何处理将一个BIOS发送到错误地址的问题。五字节的跳远似乎并不能解决任何问题。五个NOP可以达到相同的目的(事实上提前五个字节开始引导扇区代码并消除长跳转将具有与到下一指令的长跳转相同的效果)。
如果BIOS跳到正确的位置(7C00),则没有问题。如果BIOS跳到7C00以上的位置,那么在7C00加载的任何代码都无法解决该问题。如果BIOS跳转到7C00和7C4B之间的位置,则存储在该区域中的数据(或解释指令时缺少字节)可能会导致崩溃。如果BIOS跳到恰好7C4B,TEST指令将被重写(通过长跳),并且JNZ到7C54将基于BIOS中执行的最后一次数学运算来执行。
对于低于7C4B的BIOS跳转,再次错误对齐的指令可能会导致崩溃。如果运气好,引导扇区代码的某些部分将被执行。这种执行的结果将取决于BIOS跳到的"伪"内存地址。那么,这个引导扇区代码的作者是否在用一个关于"跳到错误位置的伪BIOS"的故事来欺骗我们呢?
我在Luke Luo的博客中注意到,GRUB2引导扇区虽然与GRUB Legacy引导扇区不同,但保留了这种莫名其妙的跳远。因此,如果GRUB Legacy引导扇区的原作者在开我们的玩笑,那就是一个非常成功的玩笑(它在GRUB的完整重写中幸存了下来)。我只能选择相信一个关于一些未命名BIOS的令人难以置信的断言,以及一个似乎实际上毫无作用的问题的解决方案或相信原始引导扇区的作者在和我们开玩笑。
Luke Luo似乎接受将NOP指令写入7C66和7C67,作为他没有跳错位置的BIOS的证据。Linux Mint 13在我的闪存驱动器中写入的GRUB2引导扇区具有相同的NOP。然而,写入我笔记本电脑硬盘的GRUB2引导扇区(由Debian Etch编写)有一个短跳转到7C66和7C67编写的下一条指令(请注意,Luke Luo向我们展示了存储在/usr/lib/grub/i386-pc/boot.img的原始引导扇区具有Debian值)。两个备选方案具有相同的效果(执行它们后面的指令),因此两个引导扇区都可以工作。在引导扇区中,只有大约450个字节可用于必须加载另一个扇区并执行它的代码(包括简单操作出错时的错误消息和扇区本身的8字节地址),这两种情况都没有我所期望的效率。
那么,我是遗漏了什么,还是发现了一个应该从GRUB引导扇区中删除的漏洞(为更有意义的代码腾出空间)?
您缺少CPU实际状态的差异(虽然物理地址是,但这是正确的)。
BIOS执行JMP 0000:7C00
:时
您的第一条指令位于cs=0000, ip=7C00
,无论何时执行任何绝对寻址,都必须编译机器代码以进行这种重新定位,例如mov ax,cs:[myTable]
(那么myTable
可以类似于0x7F00
)。
BIOS执行JMP 07C0:0000
:时
您的第一条指令位于cs=07C0, ip=0000
,因此像myTable
这样的指令将更像0x0300
。
因此,代码开头的第一个长跳转将把相同的物理地址31744"规范化"为预期的cs:ip
形式,使引导加载程序代码中的其余绝对地址正常工作。