在学校,我们一直在使用引导程序来运行没有操作系统的独立程序。我一直在研究这个程序,当启用保护模式时,通过直接将操作码和操作数组合为程序中的数据来执行远程跳转。这是针对GNU汇编程序的:
/* this code immediately follows the setting of the PE flag in CR0 */
.byte 0x66, 0xEA
.long TARGET_ADDRESS
.word 0x0010 /* descriptor #2, GDT, RPL=0 */
首先,为什么要这样做(而不是指令助记符)?
我一直在查看英特尔手册,但我仍然对代码感到困惑。特别是在第2A卷第3-549页,有一个操作码表。相关条目:
EA *cp* JMP ptr16:32 Inv. Valid Jump far, absolute, address given in operand
实际的操作码是显而易见的,但是第一个字节0x66让我感到困惑。参考英特尔手册中的表格,cp显然意味着将遵循一个6字节的操作数。显然,在接下来的两行中会有6个字节。 0x66编码'操作数大小覆盖前缀'。这与表中的cp有什么关系?我原以为cp会有一些十六进制值,而是有这个覆盖前缀。有人可以帮我解决这个问题吗?
这是od的转储:
c022 **ea66 0000 0001 0010** ba52 03f2 c030
TARGET_ADDRESS定义为0x00010000。
我也对最后两个字节的重要性感到困惑。然而,这似乎是另一个问题。现在已经很晚了,我一直盯着代码和英特尔手册几个小时,所以我希望我明白我的观点。
感谢您的期待!
答案 0 :(得分:12)
0x66表示JMP(0xEA)指的是6个字节。默认值是在实模式下指向64K(16位)或在保护模式下指向32位(如果我记得很清楚)。增加它后,它还包括段描述符,GDT或LDT中段的索引,这意味着,此代码正在进行传统上称为“长跳转”的跳转:跳过超出段的段x86架构。在这种情况下,该段指向GDT上的第二个条目。如果您在该程序中查看,您可能会看到GDT是如何根据段起始地址和长度定义的(请参阅英特尔手册以研究GDT和LDT表,32位条目描述每个段)。 / p>
答案 1 :(得分:2)
我遇到了这个问题。有些汇编程序只会跳转到LABEL。在这种情况下,该人想要绝对跳转到特定的硬编码偏移。 jmp TARGET_ADDRESS无法工作我猜,所以他们只是把它作为字节来解决这个问题。
答案 2 :(得分:0)
0x66指定当前代码段大小的操作数大小覆盖。假设当前代码大小为16位,则新指令指针将为32位,而不是16位。如果当前代码段大小为32位,则0x66将使目标指令指针呈现为16位。当前代码大小属性取决于正在使用的CS选择器及其从GDT / LDT表加载的属性。在实模式下,除了“虚幻”模式的特殊情况外,代码段大小通常为16位。