Intel x86 转 ARM 汇编代码转换

9
我目前正在学习ARM汇编语言;为此,我试图将一些x86代码(AT&T Syntax)转换为ARM汇编(Intel Syntax)代码。
__asm__("movl $0x0804c000, %eax;");

__asm__("mov R0,#0x0804c000");

从这个文档中,我了解到在x86中,堆结构的第一个块从0x0804c000开始。但是当我尝试在arm上做同样的事情时,我遇到了以下错误:
/tmp/ccfNZp9F.s:174: Error: invalid constant (804c000) after fixup

我假设问题在于ARM只能加载32位指令。
Question 1: Any idea what would be the first chunk in case of ARM processors?


Question 2:

根据我之前提出的问题,我知道内存间接寻址是如何工作的。

下面的代码片段是否执行相同的任务?

movl (%eax), %ebx

LDR R0,[R1]

我正在使用ARMv7处理器 rev 4(v7l)


请参考:label and label... 以及我在那里标记的所有重复内容。ARM仅支持由2的倍数旋转的8位常量。为了支持像您拥有的这样的常量,使用语法ldr r0,=0x804c000。汇编器维护一个文字池并将常量放置在其中。使用PC相对寻址来加载常量。在汇编器中使用指令.ltorg来转储池。 - artless noise
因为ARM是精简指令集,而x86是复杂指令集,并且它们只是不同的指令集,所以只有很小一部分x86代码能够直接“移植”。可能需要多个ARM指令来代替一个x86指令(有时反之亦然,一个ARM指令代表一组x86指令)。所有处理器都会有类似的东西,比如寄存器间接寻址,是的,这两个在功能上是相同的。 - old_timer
2个回答

7
尝试通过查看x86来学习ARM并不是一个好主意,因为一个是CISC且相当丑陋,而另一个是RISC且更加简洁。只需通过阅读架构参考手册中的指令集参考即可学习ARM。查询mov指令、add指令等。
ARM不使用英特尔语法,而是使用ARM语法。
不要通过内联汇编来学习,应该写真正的汇编代码。首先使用指令集模拟器,而不是硬件。
ARM、MIPS和其他处理器都采用固定字长。那么例如,如何将一条指令放入32位中,该指令包含将某些立即数移动到寄存器、指定寄存器和适合32位的立即数?这是不可能的。因此,在固定长度的指令集中,您不能简单地将任何立即数加载到任何寄存器中。您必须了解该指令集的规则。MIPS允许16位立即数,ARM允许8位左右的立即数,具体取决于ARM指令集的类型和指令。MIPS限制可将这16位放置在高位或低位,而ARM允许根据ARM指令集(ARM、Thumb、Thumb2扩展)的类型将这8位放置在32位寄存器中的任何位置。
与大多数汇编语言一样,您可以通过以下方式解决此问题。
ldr r0,my_value
...
my_value: .word 0x12345678

对于CISC,操作码立即数直接附加在指令上,因此无论距离是0个字节还是20个字节,它都存在于任意一种方法中。

ARM汇编器通常也允许您使用这种快捷方式:

ldr r0,=something
...
something:

该指令将r0的值设置为某个地址,而不是该地址上的内容(类似于lea指令)

但这也能够有一个快捷方式

ldr r0,=0x12345678

如果汇编器支持,则会分配一个内存位置来保存值,并生成一个 ldr r0,[pc,offset] 指令来读取它。如果立即数符合 mov 的规则,则汇编器可能会将其优化为 mov rd,#immediate。

1
什么是 ARM 语法 - pistal
3
Intel语法是Intel手册中为Intel处理器定义的语法。AT&T语法是AT&T针对他们的汇编器所偏离的语法。这两种语法与除x86之外的任何其他处理器都没有任何关系。在ARM、MIPS、AVR、6502或PDP11或任何其他处理器上应用这些术语是没有意义的。原始供应商指令集参考中定义的语法是该供应商的语法,该供应商通常制作或使用相关语法的汇编器。您可以使用它。 - old_timer
1
任何编写汇编程序的人都可以根据自己的需要更改语法,因为汇编语言实际上没有标准,机器码才是标准,无论你如何到达那里都可以。只需看看GNU汇编器对汇编语言所做的改变就可以了解。 - old_timer

2

问题1的答案

ARM上的MOV指令只有12位可用于立即数,这些位的使用方式如下:8位用于值,4位用于指定向右旋转的次数(将旋转次数乘以2,以增加范围)。

这意味着该指令只能使用有限数量的值。它们是:

  • 0-255
  • 256、260、264......1020
  • 1024、1040、1056......4080
  • 等等

因此,你会收到该错误,因为你的常量不能使用8位+旋转来创建。你可以使用以下指令将该值加载到寄存器中:

LDR r0, =0x0804c000

请注意,这只是一个伪指令。汇编器会将该常量放在您的代码中,并将其作为带有一些偏移量的内存位置加载到PC(程序计数器)。

回答问题2:

是的,这些指令是等效的。


谢谢。当我尝试使用LDR r0, =0x0804c000时,汇编器显示了以下信息:偏移量超出范围 - pistal
在加载该值的附近放置.ltorg。这将把文字池放在那里,应该解决偏移问题。 - Daniel Scocco
1
.ltorg 就是我所描述的内容。它是一个 字面池;汇编器放置的常量数据,以满足 ldr r0,=0x804c000 请求。也许 ARM 汇编中的重定位 有所帮助?或者 Arm 操作码 和 gnu-assembler 手册中的 .ltorg。只需在子例程返回后间隙处添加文本 .ltorg,这就是你需要知道的全部。 - artless noise
@artlessnoise:谢谢。 只有一个问题:如果我想将数据从r0加载到另一个寄存器中,我应该执行LDR R1,[R0]。其中R0的值为0x0804c000。按照之前描述的方式,我会尝试执行LDR =R1,[R0] - pistal
抱歉,我本意是在主问题下方发表我的评论。无论如何,请使用ADD而不是LDR。ARM是一种RISC CPU,具有特定的loadstore指令。您不能执行memory to register类型的操作。我猜0x0804c000是一个地址?如果它是一个结构体地址,那么您可以执行ldr r1, [R0, #offset]。这将添加一个值并获取该地址。如果0x0804c000是某个寄存器数据,则add r1, r0, #const可能有效。在这里**#const**与mov具有相同的限制,即右移多个二进制位的八位旋转;所有ARM代码都是32位的,没有空间。 - artless noise
明白了。这是我的错误。我可以执行“MOV R1,R0;”。现在我会检查反汇编和寄存器信息。 - pistal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接