【问题标题】:GNU ARM assembler changes mov into adds?GNU ARM 汇编器将 mov 更改为添加?
【发布时间】:2020-07-10 16:21:29
【问题描述】:

社区!

我有以下一行源文件,名为first.S

mov R1, R2

我生成一个目标文件如下:

$ arm-none-eabi-as -mcpu=cortex-m3 -march=armv7 -mthumb -c -o first.o first.S 

然后,我拆解它。

$ arm-none-eabi-objdump -d first.o

first.o:     file format elf32-littlearm


Disassembly of section .text:

00000000 <.text>:
   0:   1c11        adds    r1, r2, #0

显然,在这种情况下,两条指令(movadd)具有相同的预期效果。

不过,问题是:为什么?

根据 ARMv7-M 架构参考手册,mov 寄存器指令存在多种编码,但汇编器选择将其编码为add 指令。

在任何文档中是否有任何地方可以描述这样的决定?

谢谢!

【问题讨论】:

  • 指令集参考描述了应该使用什么指令。它在 ARMv6 中从 adds Rd, Rn, #0 更改为 lsls Rd, Rn, #0,现在使用之前的 Hi/Lo 寄存器移动指令的扩展形式,确实没有设置标志 mov Rd, Rn
  • @fuz 我设法在 2005 年的 ARM 架构参考手册的 Thumb2 补充文件中找到了从 movadd 更改的参考。谢谢

标签: assembly encoding arm gnu thumb


【解决方案1】:

从拇指指令集开始,编码开始

0001110xxxnnnddd 

是一个

adds rd,rn,#xxx

它是更有效的编码IF标记的修改对你来说是可以的。

伪指令 mov rd,rn 意味着标志可以改变(显然是文档中的每个 arm asm 和 gas)。这样原始编码就可以了。

现在由反汇编程序来选择 if immed == 0 然后打印 mov rd,rn vs 添加 rd,rn,#0,两者都是正确的反汇编。

现在一高一低的mov说:

Unlike the low register MOV instruction described in MOV (2) on page A7-73, this instruction does not change the flags.

现在它进入了完全由汇编程序而不是目标(不是 ARM)定义的汇编语言,以及可怕的统一语法等等。所以现在它变成了一个工具特定的东西。用于 thumb 的 Gnu 汇编器不喜欢添加,例如(非统一的语法,我发现它更容易用于 thumb)您执行添加并获得添加。

.thumb
add r0,r1,#0
mov r0,r1
adds r0,r1,#0
movs r0,r1

arm-none-eabi-as so.s -o so.o
so.s: Assembler messages:
so.s:5: Error: instruction not supported in Thumb16 mode -- `adds r0,r1,#0'

.thumb
add r0,r1,#0
mov r0,r1
movs r0,r1

00000000 <.text>:
   0:   1c08        adds    r0, r1, #0
   2:   1c08        adds    r0, r1, #0
   4:   1c08        adds    r0, r1, #0

但 movs 没问题。

0x1c08 = 0x0001110000001000,当这一切开始时,这是一条返回到 armv4t 的拇指指令。

.syntax unified
.thumb
add r0,r1,#0
mov r0,r1
adds r0,r1,#0
movs r0,r1


   0:   f101 0000   add.w   r0, r1, #0
   4:   4608        mov r0, r1
   6:   1c08        adds    r0, r1, #0
   8:   0008        movs    r0, r1

所以现在在这种情况下,它是一种不同的汇编语言(相同的工具不同的汇编语言)

所以这个汇编语言尊重 add vs add 和 mov vs movs。

为了在没有 flahs 的情况下进行添加,您需要 thumb2 编码。没有标志的mov是高位寄存器mov 0x4608 0100011000001000 0x46xx

adds 和往常一样,movs 现在被编码为左移,但不是反汇编 lsl r0,r1,#0,而是反汇编为 mov r0,r1,更多内容供您咀嚼,而不仅仅是mov 反汇编作为添加。他们为什么不使用加法?这是另一个问题,如果您至少在旧臂臂中查看 mov 低寄存器指令,它描述了显示添加编码的标志发生了什么。但是如果你看一下 lsl 的描述,标志是不同的,lsl 并不能替代带有标志的 mov,至少如最长寿的 ARM ARM(拇指)中所描述的那样。

好的,这是有道理的,它们对较旧的手臂有所帮助。如果立即数为零,则没有进位,因此将其描述为与带符号的溢出标志一起设置为零。

Lsl 在一个文档与另一个文档中将进位显示为未更改而不是零。因此,随着时间的推移,指令的执行可能发生了一些变化,或者其中一个 ARM ARMS 是错误的(这种情况经常发生)。


简短的回答,mov rd,rn 一直是一个伪指令,记录为添加,反汇编程序可以选择打印出来,这取决于反汇编程序。

汇编语言是由工具而不是目标定义的,因此工具决定在其语法中使用哪个标志解决方案,并可以在加法、mov 高位寄存器、thumb2 编码或其他编码之间进行选择。

我们不知道为什么的问题,为什么选择一种编码而不是另一种可能的编码相同,通常选择较短的编码(thumb 与 thumb2 扩展)(x86 中的 xor 与 mov 立即数,零作为即时)。但是lsl vs add vs sub vs ...

您可以在 arm 文档(当时记录其工具的汇编语言)中找到其他伪指令,以及汇编器添加到其汇编语言中的伪指令,如 nop。

.thumb
nop
mov r8,r8
mov r4,r4

00000000 <.text>:
   0:   46c0        nop         ; (mov r8, r8)
   2:   46c0        nop         ; (mov r8, r8)
   4:   1c24        adds    r4, r4, #0

现在的问题是他们为什么不直接打印出来:

   0:   1c08        mov r0,r1  ; (adds r0, r1, #0)

我也很喜欢反汇编程序如何将分号作为注释边界,而汇编语言奇怪地不支持它(就像地球上的其他所有汇编程序一样(绝大多数)。

当然,反汇编器不知道创建该机器代码的汇编器是什么,所以对于有伪指令显示两者的情况会很好。

【讨论】:

    【解决方案2】:

    https://static.docs.arm.com/ddi0403/eb/DDI0403E_B_armv7m_arm.pdf#G11.5007716 一样,移动指令的操作码也是 2 字节,因此使用其中任何一个都没有优势。 https://developer.arm.com/documentation/ddi0337/e/instruction-timing/processor-instruction-timings 声明,两条指令都使用 1 个 CPU 周期来完成。

    编码 T2 https://static.docs.arm.com/ddi0403/eb/DDI0403E_B_armv7m_arm.pdf#G11.5007716 但是会有一个空字节,这在某些情况下是不好的(利用)。

    但是我可以想象构建一个汇编器会更容易,它会产生更少的不同操作码。

    【讨论】:

      猜你喜欢
      • 2015-12-20
      • 1970-01-01
      • 1970-01-01
      • 2016-09-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-23
      • 1970-01-01
      相关资源
      最近更新 更多