07C0:0000 与 0000:7C00 在 x86 机器上的物理地址不一样吗?

Posted

技术标签:

【中文标题】07C0:0000 与 0000:7C00 在 x86 机器上的物理地址不一样吗?【英文标题】:Isn't 07C0:0000, the same physical address on x86 machines as 0000:7C00? 【发布时间】:2017-05-19 01:43:40 【问题描述】:

我的问题的原因是Starman seems to believe the GRUB Legacy author's explanation(请参阅以下莫名其妙的代码:

7C4B EA507C0000    JMP     0000:7C50  ; Long Jump to the next instruction
                                      ; because some bogus Bioses jump to
                                      ; 07C0:0000 instead of 0000:7C00.

当我执行 Intel 指定的算法以在第一个内存引用上构造有效地址时,我将 07C0: 乘以 16(有效地将其左移四位或一个半字节)。然后我添加偏移量:0000 得到十进制地址 31,744。

如果我将第二个内存引用的段左移四位,我仍然有 0000: 并且 :7C00 的偏移量仍然指向位置 31,744。所以我的直觉反应是这个 GRUB Legacy 引导扇区代码的作者正在拉扯我们的腿。不管任何BIOS的内存引用的形式如何,如果有效地址计算到十进制31744,那么这个Long Jump解决的似乎没有问题。

假设代码的作者只是以一种看起来与正确的物理位置相同的方式表达了一个虚假的物理内存位置,我开始思考如何处理一个发送错误的 BIOS地址。五字节跳远似乎不能解决任何问题。五个 NOP 可以达到相同的目的(实际上,简单地说 提前五个字节开始引导扇区代码并消除长跳转与跳转到下一条指令的效果相同。

如果 BIOS 跳转到正确的位置 (7C00),没有问题。如果 BIOS 跳转到 7C00 以上的位置,则在 7C00 加载的任何代码都无法解决该问题。如果 BIOS 跳转到 7C00 和 7C4B 之间的位置,则存储在该区域中的数据(或解释为缺少字节的指令)可能会导致崩溃。如果 BIOS 恰好跳转到 7C4B,则 TEST 指令将被覆盖(通过长跳转),并且将根据 BIOS 中执行的最后一次数学运算执行 JNZ 到 7C54。

对于低于 7C4B 的 BIOS 跳转,再次错位的指令可能会导致崩溃。运气好的话,引导扇区代码的某些部分将被执行。这种执行的结果将取决于 BIOS 确实跳转到的“虚假”内存地址。那么这个引导扇区代码的作者是不是用一个关于“跳转到错误位置的假 BIOS”的故事来拉扯我们的腿?

我在 Luke Luo 的 BLOG 中注意到 GRUB2 引导扇区,虽然不同于 GRUB Legacy 引导扇区,retains this inexplicable Long Jump。因此,如果 GRUB Legacy 引导扇区的原作者是在开我们的玩笑,这是一个非常成功的玩笑(它在 GRUB 的完全重写中幸存下来)。我只能选择相信一个关于一些未命名的 BIOS 的令人难以置信的断言,以及一个似乎实际上什么都不做的问题的解决方案相信原始引导扇区的作者正在玩开我们的玩笑。

Luke Luo 似乎接受了向 7C66 和 7C67 写入 NOP 指令,作为他没有跳转到错误位置的 BIOS 的证据。 Linux Mint 13 写入我的闪存驱动器的 GRUB2 引导扇区具有相同的 NOP。然而,写入我笔记本电脑硬盘驱动器的 GRUB2 引导扇区(由 Debian Etch 编写)有一个短跳转到下一条写在 7C66 和 7C67 的指令(请注意,Luke Luo 向我们展示了存储在 /usr/lib/grub/ 的原始引导扇区i386-pc/boot.img 具有 Debian 值)。两种选择都具有相同的效果(执行它们后面的指令),因此两个引导扇区都可以工作。在引导扇区中也没有我期望的效率,其中只有大约 450 字节可用于必须加载另一个扇区并执行它的代码(包括当简单操作出现问题时的错误消息和八字节地址部门本身)。

那么我是否遗漏了什么,或者我发现了一个应该从 GRUB 引导扇区中消除的杂物(以便为更有意义的代码腾出空间)?

【问题讨论】:

前段时间我写了一个*** Q&A,关于没有那么远的跳转会在引导加载程序中产生错误结果的情况。这实际上取决于引导加载程序代码的编写方式。一些引导加载程序开发人员出于谨慎考虑添加了 FAR JMP 以将 CS 显式设置为他们想要的值,这样他们就不会遇到问题,但如果他们的代码从不依赖,则可能没有必要CS 中的特定值 认为“谨慎”涵盖了它。如果没有进一步识别 BIOS 与此问题,甚至无法验证它是否仍然存在。在我们别无选择只能通过硬件获取 BIOS 的日子里,这将是不知道如何编码的 BIOS 供应商的解决方案。今天我希望解决办法是更换 BIOS。 硬件不附带BIOS的情况什么时候发生了变化?我在过去几年购买的每块主板都带有内置 BIOS。即使使用虚拟机,VM 供应商仍会编写 BIOS 实现。 并不是说硬件(或虚拟机)没有 BIOS。只是,如果 BIOS 出现严重故障,在当今市场上,无需指望供应商进行纠正。可以过渡到开放固件。将这种行为视为故障也许是不公平的(更多的是一个 pecadillo - 如果一个人接受这样一个前提,即在启动时有权假设一个人位于地址 7C00,DL 包含启动驱动器并且不需要初始化其他寄存器)。检查“伪造”BIOS的代码会很有趣,看看为什么没有设置CS。 我不会说 BIOS 有严重故障。 80 年代缺乏 BIOS 标准文档恕我直言,这导致了歧义。大约在 90 年代之后,引导加载程序编写者的经验法则是绝对不假设任何寄存器(包括 CS)是特定值. 1995 年,Phoenix 和 IBM 推出了 eltorito 规范,建议 0x07c0 是传统部分(而不是 0x0000),这并没有帮助,因此您经常会看到较旧的 BIOS 引导 CD-ROM 并跳转到 0x07c0:0x0000 。在同一系统上,您可能会发现软盘启动,而 JMP 指向 0x0000:0x7c00。某些版本的 BOCHS 也可以这样做。 【参考方案1】:

您错过了 CPU 实际状态的差异(而物理地址 相同,这是正确的)。

当 BIOS 执行 JMP 0000:7C00:

您的第一条指令位于cs=0000, ip=7C00,并且您的机器代码必须针对这种重定位进行编译,只要它执行任何绝对寻址,例如mov ax,cs:[myTable](然后myTable 可以类似于@ 987654325@)。

当 BIOS 执行 JMP 07C0:0000:

您的第一条指令位于cs=07C0, ip=0000,因此myTable 之类的内容将更像0x0300

因此,代码开头的第一次长跳转会将相同的物理地址 31,744 “规范化”为预期的 cs:ip 形式,从而使引导加载程序代码中的其余绝对地址正常工作。

【讨论】:

我是这么想的,但是我在汇编代码中没有找到任何段覆盖前缀。接下来的指令将 DS 初始化为 [0000]。您是否发现代码中的哪些地方有所不同?也许它只是以前代码的遗留物...... @fjardon 我没有检查特定的来源,因为即使特定的引导加载程序只能在 ds: 和相对跳转下运行,对于将来尝试的任何人来说,这仍然是一个有点“有毒”的陷阱修改它。因此,即使对于不依赖 cs 值的代码,我仍然会推荐这种做法。 @fjardon 现在我确实看了看,没有发现任何问题。所以看起来跳转实际上只是在这种情况下的预防措施(我只看了大约 5 分钟,没有认真的代码审查)。再说一次,我不确定额外的 5B 是否值得任何风险。在某些特殊情况下肯定是的,但通常可能不是。 @fjardon 您的第一个链接反汇编引导扇区中的JMP [7C42] 指令必须工作。这是一个近乎间接的跳转,所以它只改变 IP 而不是 CS。 DS:7C42 处的偏移量是 8000,所以如果 CS 是 07C0 而不是 0000,代码将跳转到 07C0:8000 而不是预期的 0000:8000 设计引导扇区是很容易的,所以这不会是有必要,但看起来还有很多地方可以改进。 @RossRidge 谢谢你,对于这个教育性的评论 :) 这是使这个答案完整的缺失部分。

以上是关于07C0:0000 与 0000:7C00 在 x86 机器上的物理地址不一样吗?的主要内容,如果未能解决你的问题,请参考以下文章

計算機的啟動

在引导加载程序中跳转到0x7C00不会引起无限循环

如何将 MySQL 5.x 与 PHP 4.x 一起使用?

为啥 ( x & 3 ) 与 ( x mod 4 ) 相同?

python2.X与Python3.X区别

python 3.x 与2.x的区别