首页 > 解决方案 > 如何在 ARM64 mov 指令中对寄存器进行编码?

问题描述

我想了解 ARM64 mov 指令中的哪个位负责寄存器信息。我使用 clang 编译我的代码,针对 aarch64 架构。

例如,我使用以下机器代码获取此指令:

01418C52 MOVZ            W1, #0x6208

查看文档“Arm Architecture Reference Manual Armv8, for Armv8-A architecture profile”页面C6-1123 在此处输入图像描述

Rd 是保存文档中指定的寄存器信息的字段:

是通用目的寄存器的 32 位名称,编码在“Rd”字段中。
是通用目的寄存器的 64 位名称,编码在“Rd”字段中。

使用网站armconverter,我更改了寄存器的值。

我按预期获得以下代码:

02418C52 MOVZ            W2, #0x6208

左边的十六进制值(最低有效)从 0x01 变为 0x02。似乎代码是小端的,但文档是大端的。但是,如果我将寄存器的字母从 W 更改为 X,则会移动另一位。

02418CD2 MOVZ            X2, #0x6208

右边的最后一个值从 0xC52 更改为 0xCD2。为什么 ?

>>> bin(0xCD2)
'0b110011010010'
>>> bin(0xC52)
'0b110001010010'

从文档中,它是字段sf中的最高有效位,负责根据立即值的大小(32b 或 64b)选择寄存器。

32-bit (sf == 0)

MOVZ <Wd>, #<imm>{, LSL #<shift>}
64-bit (sf == 1)

MOVZ <Xd>, #<imm>{, LSL #<shift>}

但是该位不在正确的位置。也许我使用了错误的文档。我想了解 32 位指令中的哪个字段负责寄存器值。

谢谢

标签: assemblyarm64machine-code

解决方案


GNU 和 LLVM 工具做到了这一点: aarch64-linux-gnu-objdump -d显示528c41024 个字节的 32 位整数解释。llvm-objdump -d 显示02 41 8c 52原始字节序列。这两者是等价的,没有误导性。

但是https://armconverter.com/愚蠢地将其分组02418C52(在其默认的“GDB”模式下)。这是不好的。如果你想手动编码一些 AArch64 shellcode,你会使用.long 0x528c4102(在一个 little-endian 汇编器上,例如 x86、AArch64 或其他)来获得MOVZ W2, #0x6208.

按照惯例,没有空格的单个数字字符串的位值从右到左增加,并表示某个宽度的单个整数值。 不是你,而是https://armconverter.com/这就是问题所在。

armconverter 有一个“GDB/LLDB”切换,可将其固定为528C4102LLDB 模式,它称之为“大端”。但它不是“大端”字节序列,没有空格,所以它是 32 位整数值。 02418C52是如果将 4 个字节解释为大端(与 AArch64 CPU 所做的相反)得到的整数,是对这528C41024 个字节的正确小端解释。

我认为 armconverter 使用“big endian”实际上意味着“在删除字节之间的空格之前字节反转”。这是对术语的脑残滥用。 同样,GNU binutils 和 LLVM 反汇编程序都正确,问题纯粹是 armconverter。


推荐阅读