【问题标题】:Differences of x86 and x86-64 machine codex86 和 x86-64 机器码的区别
【发布时间】:2015-01-20 17:19:47
【问题描述】:

所以,我有一个程序可以生成 JIT x86 机器代码并直接执行它,我希望它也支持 x86-64/AMD64/x64。明显的区别是:

  • 新寄存器(rax、r8...)和指针宽度(指针需要使用 64 位 reg)
  • 默认 C 调用约定(堆栈参数与寄存器参数)
  • 一些新的助记词(pushq推送64位)

二进制指令是否也有任何差异,或者在适当的时候使用 pushq 和 64 位寄存器是否应该(大致)足够并且代码可以正常工作?

代码示例:

static inline void emit_call(uint32_t target) {
    emit_byte(0xE8);
    emit_dword(target - ((uint32_t)out + 4));
}

如果我使用 uintptr_t 而不是 uint32_t 我认为这仍然有效, 但是将立即数加载到 64 位寄存器 rax 与将其加载到较低的 32 位别名中是不同的:

static void emit_mov_x86reg_immediate(int x86reg, int imm) {
    emit_byte(0xB8 | x86reg);
    emit_dword(imm);
}

还有其他区别吗?

我正在处理的代码可以通过here 访问,如果你想看看的话。

【问题讨论】:

  • 有很多不同之处。有些列在我的(已关闭)SO 问题stackoverflow.com/questions/11897116/… 中。
  • push 仍然是旧指令,没有新的助记符,只是一个新的 AT&T 后缀,或者在不需要后缀的地方只使用 Intel 语法

标签: assembly x86 x86-64 jit machine-code


【解决方案1】:

旧的 32 位推送和新的 64 位推送之间实际上没有区别,这是为数不多的隐式 64 位指令之一。

相对分支和调用仍然使用 32 位偏移量。

一些实际差异是:

  • 显然,REX 前缀用于额外的寄存器(还要记住 sil 和 dil - 没有设置任何位的 REX 前缀仍然很重要!)
  • 又是REX前缀,它曾经是inc和dec的短编码(40+rd,48+rd)。所以inc 和dec 必须是FF /1 编码。
  • rip-相对寻址
  • 64 位立即加载,mov 使用直接 64 位地址
  • 符号扩展 32 位到 64 位 (movsxd) 与 arpl 共享操作码
  • les和lds不存在,作为VEX前缀重用(在32位模式下,只有les和lds带有非法操作数是VEX前缀,这就是为什么VEX前缀的编码有点奇怪)
  • 删除了一些在 64 位模式下没有人使用或无用的旧指令(十进制数学、bound、into、pushad、pop es 和朋友)
  • 80 /? 的 82 /? 别名不再有效
  • lahf 和 sahf 在一些旧的 x64 处理器上不存在(不是你无论如何都会使用它们..)

【讨论】:

  • cmpsq、lodsq、movsq、scasq 和 stosq 也需要 REX 前缀。
猜你喜欢
  • 2023-03-27
  • 2017-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-22
  • 2019-02-25
  • 2016-08-05
相关资源
最近更新 更多