DOS 在运行时插入段地址

DOS inserting segment addresses at runtime

我注意到我正在编写的某些代码中存在潜在错误。

我虽然如果我使用 mov ax, seg segment_name,该程序可能是不可移植的,并且只能在特定配置的一台机器上运行,因为加载位置可能因机器而异。

所以我决定在两台不同的机器 运行 DOS 上反汇编一个只包含一条指令的程序,我发现问题神奇地解决了。

机器一上的调试输出:0C7A:014C B8BB0C MOV AX,0CBB

机器二上的调试输出:06CA:014C B80B07 MOV AX,070B

在对程序进行十六进制转储后,我发现未改变的字节实际上是B84200

手动将这些字节插入回程序会导致 mov ax, 0042

那么 PE 格式是否存储对这些指令的引用并在运行时更新它们?

正如 Peter Cordes 指出的那样,MS-DOS 不使用 Windows 使用的 PECOFF executable 格式。它有自己的 "MZ" executable format,以 executable 的前两个字节命名,标识为这种格式。

MZ 格式通过包含重定位的重定位 table 支持使用多个段。这些重定位只是简单的 segment:offset 值,指示需要根据 executable 在内存中加载的位置调整的 16 位段值的位置。 MS-DOS 通过简单地将程序的实际加载段添加到 executable 中包含的值来执行这些调整。这意味着如果不应用重定位,executable 只有在加载到段 0 时才能工作,而这恰好是不可能的。

请注意,这不仅是一个程序在多台机器上运行所必需的,它也是同一程序在同一台机器上可靠运行所必需的。加载地址可以根据各种配置细节以及已经加载到内存中的其他程序和驱动程序进行更改,因此 MS-DOS executable 的加载地址本质上是不可预测的table .

从您的示例向后工作,我们可以知道您的示例程序在两台机器上的内存中的加载位置。由于 0042h 在第一台机器上被重新定位到 0CBBh,在第二台机器上被重新定位到 070Bh,我们知道 MS-DOS 将您的程序分别加载到两台机器的 0C79h 和 06C9h 段:

0CBB - 0042 = 0C79
070B - 0042 = 06C9

据此我们可以确定您的示例 executable 在其重定位 table:[=14= 中具有条目 0001:014D 或等效的 segment:offset 值]

0C7A:014D - 0C79:0000 = 0001:014D
06CA:014D - 06C9:0000 = 0001:014D

此项表示需要调整的mov ax, seg segname指令的16位立即操作数的未重定位位置。