LINK知道大约80x86处理器(1997)

2021-01-01 08:08:51

专门为英特尔处理器设计的主流操作系统的历史使得有时要由不编写操作系统本身的程序员来开发旨在在操作系统级别或接近操作系统级别运行的软件。这些程序员意在为操作系统添加功能,并且可能是Microsoft以外的程序员的创造性添加为Microsoft操作系统在消费市场上的稳定性和成功做出了很大贡献。

由于它们与处理器的协作更加紧密,而不是某些精心设计的操作系统所必需的,因此,英特尔80x86处理器的低级程序员有时会遇到这样的实际问题,即比简单地将80386与其他处理器区分开来更精确地识别处理器。奔腾的80486。这些程序员的本性也是想知道他们是否从整体上进行工作,并且偶尔会被英特尔处理器支持英特尔未记录的指令的事实所掩盖,这种怀疑并没有得到缓解。

至少可以追溯到早期DOS版本中对RAM磁盘驱动器使用未记录的LOADALL指令的过程,人们发现Microsoft对Intel处理器的了解远远超过仅研究Intel的80x86手册所学的知识。这只是在Microsoft作为英特尔处理器最常购买的操作系统组件编写的程序中可以预期的,尽管它确实提出了这样的问题:替换或增强的实用程序的开发者是否容易获得类似信息?操作系统功能或英特尔处理器的其他操作系统设计人员。显然,这些处理器的特殊知识偶尔会进入应用程序(最著名的是诸如汇编器,编译器和链接器之类的开发工具)的应用程序,因为在这些程序的市场中,Microsoft通常只是众多程序之一。

1997年,我检查了两个Microsoft程序,以了解有关英特尔处理器的不同寻常的知识,打算分两部分撰写论文。第一个显示了Microsoft的32位链接器,当时还相当新,因为它知道多达15条指令的操作码,而这些指令似乎并未为每个人提供。第二部分是从Windows NT 4.0看操作系统内核,以获取CPU识别的更多细节,但是它从来没有超出草稿范围。 (也就是说,请参阅Windows内核的CPU识别,该描述具有相同的基础,但对于Windows Vista是最新的。)

本文本质上是最初作为Word文档发布的标题为“ Microsoft知道英特尔80x86处理器的奇怪事物”的文档。它描述了随Microsoft Visual C ++的不同版本提供的链接器如何了解英特尔似乎没有为一般常识所记录的十几条80x86指令的操作码和操作数要求:LOADALL,CFLSH,WRECR,RDECR,SVDC, RSDC,SVLDT,RSLDT,SVTS,RSTS,SMINT,XBTS,IBTS,ZALLOC。

(我感谢Robert Collins和Christian Ludloff分别提供的信息,其中七个(即SVDC,RSDC,SVLDT,RSLDT,SVTS,RSTS和SMINT)实际上不是Intel的说明,但已作为Cyrix 80x86外观的说明记录处理器。我永远也不会想到。)

在用于开发32位应用程序的Microsoft Visual C ++版本中,链接器可以选择转储其输入文件(可能是目标文件或可执行文件)的内容。通过在命令行上给LINK / dump开关,或更常见的是,从称为DUMPBIN的存根程序间接运行LINK,可以将LINK转换为COFF二进制文件转储程序。

下表显示了本文已检查的LINK版本。所有这些都来自Microsoft Visual C ++的发行版,Windows 95 DDK附带的链接器显然是对Microsoft Visual C ++ 2.0某些版本的链接器的修正。 [1]

为文件转储提供的功能中包括对代码段的简单反汇编。通过还给链接器/ disasm开关来调用此选项。微软的链接器可以识别为许多处理器开发的文件(实际上,比相关的Microsoft文档中列出的文件少一些),并且可以反汇编其中一个文件的代码。 [2]

R10000机器是受支持机器列表中相对较新的一种:LINK版本2.60无法识别它。

在研究的四个版本中,用于英特尔处理器的反汇编程序知道15条指令,这些指令未出现在英特尔广泛提供的手册随附的操作码图中。下表显示了这些异常的操作码和相应的指令,在分解时使用占位符表示LINK包含在指令助记符中的操作数。

操作数的占位符改编自英特尔手册中使用的约定。因此,reg16和reg32代表16位和32位通用寄存器,而sreg代表段寄存器。 r / m16和r / m32组合可以由寄存器或存储器引用填充。 mem80和mem256占位符仅用于引用内存,尤其是十字节和32字节变量。 esi16和esi32占位符用于引用内存,但使用DS:SI或DS:ESI作为隐含地址。

严格来说,LINK对操作码的识别并不意味着相应的指令曾经存在于任何英特尔处理器上。例如,准备LINK的反汇编表的程序员可能是通过操作码映射工作的,该操作码映射仅反映了英特尔的一些意图。另一方面,Microsoft对操作码映射的使用比Intel向大多数程序员提供的操作码映射更详细,这显然不是一次性的:不同版本的链接器使用反汇编表,这些反汇编表支持与上表不同的选择,并且不支持与已发布的用于后续处理器的操作码映射轻松匹配。

考虑到LINK版本2.60知道所有已记录的奔腾指令。它不知道英特尔为奔腾Pro引入的FCOMI,FCOMIP,FUCOMI和FUCOMIP指令,也不知道英特尔向Pentium Pro和具有MMX技术的奔腾提供的RDPMC指令。但是,此版本的LINK确实可以识别Intel记载为Pentium Pro引入的CMOV cc和FCMOV cc指令的操作码。 [3]

LINK版本2.60中的拆卸表可能是为Pentium Pro准备的,但是一些说明被忽略了。有条件移动指令可能是在Pentium Pro的新指令中首先设计的,只是在准备拆卸表时才预料到的。最后,可能是条件移动指令至少在某些奔腾处理器上存在,没有记录,而对于为LINK版本2.60准备拆卸表的任何人都知道。

LINK版本3.00知道有关Pentium Pro的所有说明,但不了解任何MMX说明。它也是研究的唯一可识别七种指令SVDC,RSDC,SVLDT,RSLDT,SVTS,RSTS和SMINT的操作码的版本。请注意,作为SMINT的操作码0Fh 7Eh的反汇编与Intel(可能稍后)将该操作码分配为MOVD指令以从MMX寄存器读取dword冲突。引入了对MMX指令的支持的LINK版本3.10删除了所有这7条指令,我们推测这些指令只有在Pentium Pro上才存在。

LINK为指令SVDC和RSDC给出的操作数与对助记符的解释一致,表明该指令保存和恢复了与给定段寄存器相对应的内部描述符缓存。描述符高速缓存大概由一个双字组成,每个双字分别代表基数和限制,以及访问权限和其他标志的字。 SVLDT,RSLDT,SVTS和RSTS对当前LDT和TSS的内部描述符进行的类似解释将使这些指令也访问十个字节的内存。

但是,LINK的操作数知识的正确性不应被视为理所当然。甚至在拆卸文档说明时,LINK也会产生太多错误:

给定一个对字节进行操作的DEC指令,LINK会将其作为递减的dword进行反汇编(假设此处的反汇编是32位代码,并且没有前缀覆盖操作数的大小)。在Intel操作码映射的语言中,LINK将DEC Eb分解为DEC Ev。 LINK版本3.10和4.20中修复了此错误。 [4]

对于此错误引起的歧义的实际示例,请考虑LINK将指令反汇编为“ dec eax”可能确实意味着DEC EAX,但也可能意味着DEC AL。解决这种歧义性通常需要引用操作码映射或指令编码列表。

对于所有往返于控制,调试和测试寄存器的指令,LINK会以错误的顺序反汇编操作数。此错误已在LINK版本4.20中修复,但不适用于测试寄存器(仅存在于80386和80486处理器上)上的指令。

鉴于IMUL指令的长格式包含三个字或双字操作数,LINK的反汇编将最后一个操作数(即立即数据)显示为一个字节。在Intel操作码映射的语言中,LINK将IMUL Gv,Ev,Iv列为IMUL Gv,Ev,Ib。

对于由该错误引起的歧义的一个实际示例,请考虑LINK将指令反汇编为“ imul eax,eax,78h”可能确实是12345678h(或任何其他低字节为78h的dword)的乘法。在反汇编中,在指令助记符之前的操作码字节中显示了正确的立即操作数。

LINK将FCOMI,FUCOMI,FCOMIP和FUCOMIP指令识别为仅以ST,ST(0)作为其操作数。指示使用除ST(0)以外的任何堆栈寄存器作为第二操作数的操作码均无效。

对于MOVD和MOVQ指令,LINK以错误的顺序反汇编操作数。对于MOVD指令,还有另一个问题。具体来说,英特尔记录为通用寄存器或内存地址的操作数将被解释为MMX寄存器或内存地址。在Intel操作码映射的语言中,LINK将MOVD Pd,Ed分解为MOVD Qd,Pd,并且在另一个方向上也是如此。

给定一个以计数作为立即数据的打包移位指令,尽管英特尔将指令记录为仅允许MMX寄存器作为目标,但LINK允许目标操作数为MMX寄存器或内存地址。在Intel操作码映射的语言中,LINK将A组指令反汇编为操作数Qq,Ib而不是Pq,Ib。

LINK将操作码82h作为即时数据的MOV分解为al。在执行此操作时,LINK会出现一个错误,该错误出现在Intel的某些操作码图中。 [5]实际上,操作码82h表示来自第1组(ADD,OR,ADC,SBB,AND,SUB,XOR或CMP,取决于第二个字节中的位)的算术或逻辑指令。操作数是8位寄存器或内存字节,以及立即数字节。操作码与80h的区别仅在于要求立即数进行符号扩展,但是从一个字节到一个字节的符号扩展是没有意义的。操作码82h是冗余的,很少出现在程序代码中。

还有一些说明,LINK可以正确解码,但表示不正确(并非区别只不过是一点)。涉及从分解中丢失大量信息的示例是:

给定一条指令,该指令通过从操作码序列中的字节进行符号扩展来形成立即操作数,则LINK将立即数据显示为字节,而不是符号扩展后的完整操作数。

对于由这种错误表示引起的歧义的实际示例,请考虑LINK将指令反汇编为“ cmp eax,0ffh”可能确实意味着CMP EAX,000000FFh,但也可能意味着CMP EAX,0FFFFFFFFh。要解决这种歧义,通常需要参考操作码图或指令编码列表,或充分了解编码的一般原理,以便根据指令的长度推断符号扩展。

对于使用大于dword的内存变量的指令,LINK将内存引用显示为dword指针。

对于由这种错误表示引起的歧义的实际示例,请考虑将LINK的指令反汇编为“ fld dword ptr [eax]”可能表示单实数(32位),双实数(64h位)或扩展的FLD。 -真实(80位)。解决这种歧义性通常需要引用操作码映射或指令编码列表。

在LINK识别的未记录指令中,80386 LOADALL(操作码0Fh 07h)肯定是用错误的操作数反汇编的。为了列出操作数,LINK对待80386 LOADALL的方式与对待LODSW和LODSD的方式相同,因此在DS:SI或DS:ESI上,该操作数被显示为一个字或双字(取决于操作数和地址大小) 。实际情况是80386 LOADALL从ES:DI或ES:EDI(取决于地址大小)获取其操作数,并使用0127h字节的内存区域而不是字或dword。 [6]奇怪的是,Microsoft在其另一个程序中拥有更好的信息:Microsoft随各种Windows SDK和DDK一起提供的WDEB386.EXE调试器显示80386 LOADALL将ES:DI或ES:EDI的字节作为其操作数。

从软件分析作为一种软件开发技术或一门未来的学术学科的角度来看,很有趣的是LINK的操作码表有这么多错误,即使是众所周知的指令也是如此。确实,本文的主要动机不是要列出一些未记录的CPU指令,而是要证明让第二人检查第一人的编程工作是否有错误是可行和实用的。

该程序的后续版本已纠正了某些错误,但未纠正其他错误,这表明该程序的制造商有意愿拥有正确的表格,但很难发现错误。此外,随着对支持新处理器指令的不断升级,引入了新的错误。该程序的操作码表大概是通过宏生成的。尽管这些代码可能便于开发,但它们也可能掩盖了查看程序源代码的人的错误。通过让某人查看程序中实际生成的相关代码和数据,制造商可以检测到更多的错误。本文证明,即使制造商外部的人也可以做到这一点,而无需知道那些表的格式,而没有源代码则更少。这种审查过程甚至可能在商业上是可行的。

[1]请参阅MSVC20目录中的README.TXT文件,以获取有关以下提示的信息:即使构建虚拟设备驱动程序(VxD),也无法依靠多个Microsoft Visual C ++ 4.x发行版中的链接器正确链接目标文件。尽管这些产品中的文档继续描述/ vxd开关。

[2]机器类型记录在MSDN Library CD上的Microsoft便携式可执行和公共对象文件格式(PE / COFF)规范4.1中,并且在Microsoft Visual Visual随附的WINNT.H头文件中以IMAGE_FILE_MACHINE开头的符号表示。 C ++和Win32 SDK。

[3]有关指令兼容性的信息,请参阅《 Intel体系结构软件开发人员手册,第2卷:奔腾Pro的指令集参考》,订单号243191,可通过匿名FTP在download.intel.com上的design / pro /目录中获得。手册。

[4]我应该很高兴地引用Microsoft知识库(MSKB)一篇文章,其中描述了这些错误并指出了所有修复程序,但是我找不到它,例如,通过在知识中列为包含“ DUMPBIN”的文章中查找MSDN Library CD(1997年1月),Microsoft Visual C ++ 4.2 CD和TechNet CD(1997年2月)上的基础集合。微软错误报告实践的分析师可能会考虑,无论MSKB中有多少篇文章描述了错误,都没有理由假设MSKB仅列出了Microsoft已知的一小部分错误。

[5]例如,请参阅《英特尔386 DX微处理器程序员参考手册》(订单号230985)中附录A所示的操作码映射,或者最近从www.intel.com上获得的《奔腾Pro系列开发人员手册》第5卷, 3:操作系统作家指南(订单号242692)。后者至少具有脚注以将操作码82h标记为保留。

[6] Robert Collins在1991年10月发表于Tech Specialist的一篇文章中似乎已经引起了人们的广泛关注,该文章可在www.x86.org上在线获得(可能是最感兴趣的读者可以从中找到它的地方)首次)。在该文章发表之前,BIOS开发人员当然知道该指令,最值得注意的用途是在80386计算机上模拟80286 LOADALL。