首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在x86-64 (AMD64)体系结构中是否存在默认操作数大小?

在x86-64 (AMD64)体系结构中是否存在默认操作数大小?
EN

Stack Overflow用户
提问于 2021-07-07 15:57:52
回答 1查看 954关注 0票数 2

这是一个关于x86-64 (AMD64)体系结构中操作数大小覆盖前缀的问题.

下面是一堆汇编程序指令(nasm)及其编码;新的我指的是r8、.、r15寄存器:

代码语言:javascript
复制
                                                                   67: address-size override prefix
                                                                   |
                                                                   |  4x: operand-size override prefix
                                                                   |  |
   ;   Assembler                   ; | Dst operand | Src operand | -- --
       mov      eax,ecx            ; | 32-bit      | 32-bit      |       89 C8     |
       mov      r8d,ecx            ; | 32-bit new  | 32-bit      |    41 89 C8     |
       mov      eax,r9d            ; | 32-bit      | 32-bit new  |    44 89 C8     |
       mov      r8d,r9d            ; | 32-bit new  | 32-bit new  |    45 89 C8     |
       mov      rax,rcx            ; | 64-bit      | 64-bit      |    48 89 C8     |
       mov      r8,rcx             ; | 64-bit new  | 64-bit      |    49 89 C8     |
       mov      rax,r9             ; | 64-bit      | 64-bit new  |    4C 89 C8     |
       mov      r8,r9              ; | 64-bit new  | 64-bit new  |    4D 89 C8     |

       lea      eax,[ecx]          ; | 32-bit      | 32-bit      | 67    8D 01     |
       lea      r8d,[ecx]          ; | 32-bit new  | 32-bit      | 67 44 8D 01     |
       lea      eax,[r9d]          ; | 32-bit      | 32-bit new  | 67 41 8D 01     |
       lea      r8d,[r9d]          ; | 32-bit new  | 32-bit new  | 67 45 8D 01     |
       lea      rax,[rcx]          ; | 64-bit      | 64-bit      |    48 8D 01     |
       lea      r8,[rcx]           ; | 64-bit new  | 64-bit      |    4C 8D 01     |
       lea      rax,[r9]           ; | 64-bit      | 64-bit new  |    49 8D 01     |
       lea      r8,[r9]            ; | 64-bit new  | 64-bit new  |    4D 8D 01     |

       push     rax                ; |             | 64-bit      |       50        |
       push     r8                 ; |             | 64-bit new  |    41 50        |

通过研究这些以及与其他寄存器相同的指令,我推断出以下几点。在“旧”和“新”寄存器之间有一个配对。无穷无尽的:

代码语言:javascript
复制
   AX <--> R8
   CX <--> R9
   DX <--> R10
   BX <--> R11
   BP <--> R13 

忽略大小前缀,指令字节不引用特定寄存器,而是引用寄存器对。例如:字节89 C8表示从源( ecx、rcx、r9d或r9 )到目标( eax、rax、r8d或r8 )的mov指令。考虑到操作数必须都是32位或64位宽,有八个合法的可能组合。操作数大小覆盖前缀(或没有前缀)指示这些组合中的哪一个是预期的。例如,如果前缀是44,则源操作数必须是32位新寄存器(在本例中为r9d),目标必须是32位旧寄存器(此处为信令eax)。

我可能不是完全正确,但我想我明白其中的要点。这样看来,操作数大小的覆盖前缀所做的覆盖是这样一个事实:没有它们,指令将使用32位的“旧”操作数。

但毫无疑问,有些事情让我无法理解,否则:那么谈论“一个默认操作数大小为64位的x86-64版本”(比如这里)又有什么意义呢?

或者有一种方法,运行在64位机器上,将默认的操作数大小设置为32或64,如果是,如果我的程序适当地设置机器,我会看到不同的编码?

另外:什么时候会使用66H操作数大小覆盖前缀?

EN

回答 1

Stack Overflow用户

发布于 2021-07-07 19:18:57

是的,在64位机器代码中,大多数指令( 64位用于堆栈和跳转/调用指令 )的默认操作数大小是32位,loopjrcxz也是64位.(默认的地址大小是64位,所以add eax, [rdi]是一个2字节的指令,没有前缀。)不,默认值是不可更改的,不能有2字节的add rax, rdx.

64位模式下的操作数编码

  • 64位操作数大小由REX.W发出信号(0x4?的高位设置在低比特,48.4f)。清除W位的REX前缀永远不能覆盖操作数大小为32位的操作数,因为在操作码中,操作数是默认的。(如push)
  • 16位操作数大小由0x66前缀表示,如imul ax, [r8], 123
  • 8位操作数大小使用不同的操作码。(8086有8位和16位操作数大小;8位操作数大小的操作码自那以后没有变化.)对于16位操作数大小,8086的操作码默认为模式和前缀依赖。

(在其他模式中,不存在REX,66将其设置为任何非缺省值。)

有趣的事实:被覆盖使用ECX代替RCX隐式地使用地址大小前缀,而不是操作数大小。IIRC,这是有意义的,因为分支的操作数大小属性会影响它是否将EIP截断为IP。

例如,GNU .intel_syntax反汇编的那些NASM-语法示例从上面。

代码语言:javascript
复制
objdump -drwC -Mintel foo
  401000:       6a 7b                   push   0x7b
  401002:       66 6a 7b                pushw  0x7b
  401005:       03 07                   add    eax,DWORD PTR [rdi]
  401007:       66 03 07                add    ax,WORD PTR [rdi]
  40100a:       48 03 07                add    rax,QWORD PTR [rdi]
  40100d:       66 41 6b 00 7b          imul   ax,WORD PTR [r8],0x7b

注意,imul示例使用了一个“高”寄存器,因此它需要一个REX前缀来向R8发送信号,而不需要使用66个前缀来表示16位操作数大小。.W位不是在rex前缀中设置的,而是0x41而不是0x49

同时使用REX.W和0x66前缀是没有意义的。在这种情况下,REX.W前缀似乎“赢”。在Linux GDB中,在i7-6700k (Skylake)上,单步66 48 05 40 e2 01 00 data16 add rax,0x1e240离开RIP指向整个指令的末尾(并将完整的立即添加到RAX),而不是将其解码为add ax, 0xe240,而使RIP指向4字节直接的中间。( 66前缀对该操作码来说是长度变化的,就像大多数具有32位即期(16位)的前缀一样。见https://agner.org/optimize/ re: LCP摊位。)

我让NASM从o16 add rax, 123456中发射出来。REX前缀一般都是普通的和良好的,使用66前缀,例如编码add r8w, [r15 + r12*4],需要在REX的低比特中设置所有其他3位。

  • 32位地址大小由0x67前缀(如add eax, [edx] )表示.

当然,它可以和操作数大小的东西结合起来,完全正交。

通常,32位地址大小仅适用于Linux x32 ABI (长模式下的ILP32,用于在指针重的数据结构上保存缓存占用),您可能希望从指针中截断高垃圾,以确保地址数学正确包装以保持低4GiB,即使是32位负数。

代码语言:javascript
复制
  401012:       67 03 04 ba             add    eax,DWORD PTR [edx+edi*4]

在其他模式中,67将地址大小设置为非默认值。16位地址大小也意味着对ModRM字节的16位解释,所以只允许[bx|bp + si|di],不允许SIB字节允许32 /64位的灵活寻址。

默认模式和集合

否,默认值不能在64位模式中更改.CS (或任何其他方法)选择的GDT条目中的不同位并不重要。模式中的表是模式和默认操作数/地址大小的可能组合的完整列表。

只有一组设置允许64位操作数大小。即使在任何遗留模式下,也不可能有像16位操作数、32位地址大小这样的组合。

从硬件复杂性的角度来看,这是有意义的。它需要支持的东西越多,就会有更多的晶体管涉及到CPU中已经很复杂和功率密集的部分。

(虽然push/pop隐式使用的默认堆栈地址大小由SS选择器IIRC独立选择。因此,我认为您可以使用普通的32位模式,其中add eax, [edx]是2字节,除了使用ss:sp而不是ss:esp的push/pop/call/ret之外。这不是我试过的东西。)

请注意,16位AX对应于16位R8W,而RAX和R8是由REX前缀区分的对。

在程序集源中,没有默认的,它必须由寄存器隐含或显式指定。

除了有些汇编程序具有push/pop的默认值,或者其他情况下也有一些错误的汇编程序(包括add $1, (%rdi)默认为dword的GNU ),仅在最近的版本中才有警告。奇怪的是,GAS在模棱两可的mov上确实有错误。clang的内置汇编程序更好,在任何不明确的操作数大小上都会出错。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68289333

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档