我注意到我正在编写的某些代码中存在潜在的错误。
尽管我使用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可执行格式。它具有自己的"MZ" executable format,以可执行文件的前两个字节命名,该字节标识为采用这种格式。
MZ格式支持通过包含重定位的重定位表使用多个段。这些重定位只是简单的segment:offset值,这些值指示需要根据可执行文件在内存中的加载位置来调整16位段值的位置。 MS-DOS通过简单地将程序的实际负载段添加到可执行文件中包含的值来执行这些调整。这意味着,如果不应用重定位,则可执行文件仅在加载到段0时才起作用,而这恰好是不可能的。
请注意,这不仅是一个程序在多台计算机上工作所必需的,而且同一程序在同一台计算机上可靠地工作也有必要。可以根据各种配置详细信息以及已经在内存中加载的其他程序和驱动程序来更改加载地址,因此,MS-DOS可执行文件的加载地址本质上是不可预测的。
从您的示例开始,我们可以判断您的示例程序在两台计算机上的加载位置。由于将0042h重定位到第一台计算机上的0CBBh和第二台计算机上的070Bh,我们知道MS-DOS分别将您的程序加载到两台计算机上的0C79h和06C9h段:
0CBB - 0042 = 0C79
070B - 0042 = 06C9
由此,我们可以确定您的示例可执行文件在其重定位表中具有条目0001:014D或等效的segment:offset值:
0C7A:014D - 0C79:0000 = 0001:014D
06CA:014D - 06C9:0000 = 0001:014D
此条目表示mov ax, seg segname
指令的16位立即数操作数的未重定位位置,需要调整。