Assembly 有人能解释一下这个直接组装的x86 JMP操作码吗?

Assembly 有人能解释一下这个直接组装的x86 JMP操作码吗?,assembly,x86,gnu,opcode,protected-mode,Assembly,X86,Gnu,Opcode,Protected Mode,在学校,我们一直使用引导程序来运行没有操作系统的独立程序。我一直在研究这个程序,当启用保护模式时,直接将操作码和操作数作为程序内的数据进行组装,从而执行跳转。这是针对GNU汇编程序的: 首先,为什么要这样做(而不是指令助记符) 我一直在看英特尔手册,但对代码还是有点困惑。特别是在第2A卷第3-549页,有一个操作码表。相关条目: c022 **ea66 0000 0001 0010** ba52 03f2 c030 EA*cp*JMP ptr16:3

在学校,我们一直使用引导程序来运行没有操作系统的独立程序。我一直在研究这个程序,当启用保护模式时,直接将操作码和操作数作为程序内的数据进行组装,从而执行跳转。这是针对GNU汇编程序的:

首先,为什么要这样做(而不是指令助记符)

我一直在看英特尔手册,但对代码还是有点困惑。特别是在第2A卷第3-549页,有一个操作码表。相关条目:

c022 **ea66 0000 0001 0010** ba52 03f2 c030 EA*cp*JMP ptr16:32库存有效跳转距离,绝对值,中给出的地址 操作数 实际的操作码是显而易见的,但第一个字节0x66让我感到困惑。参考《英特尔手册》中的表格,cp显然意味着后面将有一个6字节的操作数。很明显,接下来的两行中有6个字节。0x66编码“操作数大小覆盖前缀”。这与表中的cp有什么关系?我原以为cp会有一些十六进制值,但实际上有一个覆盖前缀。有人能帮我澄清一下吗

以下是od的转储文件:

c022**ea66 0000 0001 0010**ba52 03f2 c030 目标地址定义为0x00010000

最后两个字节的意义也让我有点困惑。然而,这似乎是另一个问题。时间已经很晚了,我已经盯着代码和英特尔手册看了好几个小时了,所以我希望我能把我的观点讲清楚


谢谢你的关注

0x66表示JMP(0xEA)引用六个字节。默认值是指实模式下的64K(16位)或保护模式下的32位(如果我记得很清楚的话)。增加后,它还包括段描述符,GDT或LDT中的段索引,这意味着此代码正在进行传统上称为“跳远”的操作:跨越x86体系结构中的段。在本例中,该段指向GDT上的第二个条目。如果您在该程序中查看之前的内容,您可能会看到GDT是如何根据段起始地址和长度定义的(请参阅《英特尔手册》以研究GDT和LDT表,其中32位条目描述了每个段)。

我对此有一点了解。有些汇编程序只会跳转到标签。在这种情况下,用户希望绝对跳转到特定的硬编码偏移量。我猜jmp目标地址不起作用,所以他们只是将其作为字节来解决这个问题。

0x66指定当前代码段大小的操作数大小覆盖。假设当前代码大小为16位,则新的指令指针将为32位,而不是16位。如果当前代码段大小为32位,则0x66将目标指令指针呈现为16位。当前代码大小属性取决于正在使用的CS选择器及其从GDT/LDT表加载的属性。在实模式下,代码段大小通常为16位,但“不真实”模式的特殊情况除外。

Ah,这现在是有意义的。在前面,当定义GDT时,第一个条目为null(如手册所述),但第二个条目是代码段。在重新阅读了手册的一些部分之后,我看到了它是如何工作的。谢谢你澄清这一点。我还是很好奇,为什么作者选择这样做而不是使用助记符。这是操作数大小前缀,但它改变了它。这个答案声称无前缀版本将是
jmp rel16
jmp rel32
,但这是不同的操作码,
E9
不是
EA
EA
始终是一个带有即时偏移量和段的远jmp。人们使用操作码(而不是指令)有两个原因。第一个原因是当汇编器“不够充分”并且不支持他们需要的指令时(这在添加新指令而旧的汇编器还不支持它们时是很常见的)。第二个原因是,当汇编程序确实支持他们需要的指令,但程序员不知道如何说服汇编程序生成指令时。基本上,要么是糟糕的工具(包括旧工具、混乱的语法和/或糟糕的文档),要么是糟糕的程序员。我不使用GAS,也不知道它是否支持“16位代码中的32位跳远”指令(或者文档的好坏)。非真实模式是真实模式,缓存描述符仍然设置为limit>64k,以切换到保护模式并返回。32位CS肯定意味着保护模式,但如果禁用分页,它仍然直接使用物理地址。 EA *cp* JMP ptr16:32 Inv. Valid Jump far, absolute, address given in operand c022 **ea66 0000 0001 0010** ba52 03f2 c030