机器代码指令是否在 Intel x86-64 架构上以 little endian 4 字节字的形式获取?

Are machine code instructions fetched in little endian 4-byte words on an Intel x86-64 architecture?

尽管单词(维基百科上的as stated)的通用定义是:

The largest possible address size, used to designate a location in memory, is typically a hardware word (here, "hardware word" means the full-sized natural word of the processor, as opposed to any other definition used).

x86 系统,根据 some sources,请注意它被视为 16 位:

In the x86 PC (Intel, AMD, etc.), although the architecture has long supported 32-bit and 64-bit registers, its native word size stems back to its 16-bit origins, and a "single" word is 16 bits. A "double" word is 32 bits. See 32-bit computer and 64-bit computer.

然而英特尔的 official documentation(sdm 卷 2,第 1.3.1 节)指出:

this means the bytes of a word are numbered starting from the least significant byte. Figure 1-1 illustrates these conventions.

并且图 1-1 显示了 x86-64 上下文中 word 的小端序列中的 4 个字节,而不是 2 个字节或 8 个字节(正如上面链接的来源的不同定义所暗示的那样):

我真正感到困惑的地方在于如何获取和解析指令。我正在写一个模拟器,一旦我解析了一个 PE 格式的可执行文件并进入文本部分,如果我要遵循 4 字节的小端格式,这是否意味着第 4 个字节将首先被解析?

让我们组成一些字节,例如:

.text segment buffer:
< 0x10, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20 > ....

我会把第一条指令解析为 1C, 1B, 1A, 10, 20, 1F, 1E, 1D ... (依此类推,显然是可变长度可能需要阅读更多单词,具体取决于此处的实际字节数)?

不,x86 指令被解析为字节序列,而不是更长的字。在您的示例中,第一条指令是解码为 adc [rdx], bl 的字节 0x10 0x1a。它不是 0x1c 0x1b 将解码为 sbb al, 0x1b 也不是 0x20 0x1f 将是 and [rdi], bl

然而,当一条指令包含一个多字节数(16/32/64 位)作为立即操作数、位移、地址等时,则该数字被编码为小端。例如,add ecx, 0x12345678 被编码为 0x81 0xc1 0x78 0x56 0x34 0x12.

不是,x86机器码是一个byte-stream;除了 32 位位移和小端字节序的立即数外,它没有任何面向单词的内容。例如在 add qword [rdi + 0x1234], 0xaabbccdd。它在现代 CPUs 上以 16 字节或 32 字节块的形式物理获取,并在指令边界上并行拆分以并行提供给解码器。

48    81   87     34 12 00 00    dd cc bb aa       
REX.W add ModRM    le32 0x1234    le32 0xaabbccdd le32 (sign-extended to 64-bit)

   add    QWORD PTR [rdi+0x1234],0xffffffffaabbccdd

x86-64不是面向字的架构;没有单一的自然字长,也不必对齐。在考虑 x86-64 时,这个概念不是很有用。整数寄存器宽度恰好是 8 个字节,但这甚至不是机器代码中的默认操作数大小,并且对于大多数指令,您可以使用从字节到 qword 的任何操作数大小,对于 SIMD,从 8 或 16 字节到 32或 64 字节。最重要的是,机器代码甚至数据都不需要更宽的整数对齐。


有些人喜欢把方钉塞进圆孔并用机器字来描述 x86,但这个概念只适用于围绕单一字长设计的 RISC ISA。 (对于某些 RISC 上的字大小访问,固定指令长度、寄存器大小甚至数据存储器 load/store 都需要进行字对齐,尽管现代 RISC 通常允许未对齐 load/store,但会带来一些性能损失。)

(公平地说,64 位 RISC 通常对 32 位和 64 位整数也同样有效。但与 x86 不同的是,它们不能 add ax, cx 避免将进位传播到 a 的更高位寄存器。尽管 RISC 可以在对符号扩展或零扩展加载结果进行一些数学运算后进行 16 位存储)。

相关:

  • x86 字节/未对齐 word/dword 存储比许多 RISC 更高效。

according to some sources, note it's treated as 16 bits:

是的,在 x86 术语/文档中,一个“字”是 16 位,因为现代 x86-64 是从 8086 演变而来的,如果在每个人都在使用的文档中更改术语的含义,那将是愚蠢的在 386 发布的那些年里。因此 paddw packed add of 16-bit SIMD elements, and movsw/stosw/等等。字符串指令。

x86 16 位“字”与CPU 体系结构.

中的“机器字”概念的联系绝对为零

在 8086 到 286 上,16 位是寄存器和总线宽度,并且是除字节之外唯一可用于大多数 ALU 指令的整数操作数大小。但是那些 CPU 仍然非常不像 MIPS 那样基于“单词”;机器代码格式仍然相同,具有未对齐的 little-endian 16 位立即数和位移。 (8088 与 8086 相同,除了 8 位总线接口和 4 字节指令预取缓冲区而不是 6 字节。)