1

我有一个 ARM 二进制文件和一些汇编代码。从二进制读取的反汇编

1e40 -> subs r0,r0,#1

汇编代码中的指令读起来是一样的。(代码使用.syntax unified指令)

但是,当我使用 GNU AS(来自 ARM 2017 Q4 的 gcc 工具链)时,它会评估

subs r0,r0,#1

3801-> subs r0,#1

使用 objdump

我想这两个可能是相同的,但是我仍然想知道是否有办法选择第一个操作码的生成而不是第二个操作码,因为我相信它会导致我正在使用的设备出现问题。

4

2 回答 2

5

您没有说明架构,但显然是 Thumb,我将使用 Cortex-M 作为基础。

查阅《ARMv7-M 应用层架构参考手册》,可以轻松解码操作码。试试吧,这是一个很好的做法,并且在编写机器级代码时将它放在附近总是一个好主意。

它们实际上是两个不同的指令,但具有相同的效果。

第一个操作码是指双寄存器(源、目标)操作,带有一个小的 3 位立即数(零扩展,范围 0…7)。在这里,两个寄存器碰巧是同一个寄存器,使其实际上是一个单寄存器递减。

第二种是带有 8 位立即操作数(零扩展,范围 0…255)的单个寄存器减法。目前尚不清楚为什么你会得到两种不同的变体,但它们应该没有实际区别(至少对于 Cortex-M4 来说,它们都需要一个时钟,如果其他架构有不同的值,我会感到惊讶)。

所以反汇编反映了实际的指令。gas 似乎将原始的两寄存器指令转换为单寄存器操作码。

请注意,有各种操作可能导致不同的操作码。对于给出的信息,尚不清楚为什么选择它们。Imo,第二个更合理,第一个只有在涉及两个不同的寄存器时才有意义。

如果您的代码有问题,则不太可能是这条指令。也许是时候启动调试器了。

于 2018-04-29T12:06:25.597 回答
3

去尝试一下...

所以.s

.thumb
sub r0,#1
.syntax unified
subs r0,r0,#1
sub r0,#1
subs.n r0,#1

组装和拆卸

arm-none-eabi-as so.s -o so.o
arm-none-eabi-objdump -D so.o

so.o:     file format elf32-littlearm


Disassembly of section .text:

00000000 <.text>:
   0:   3801        subs    r0, #1
   2:   3801        subs    r0, #1
   4:   f1a0 0001   sub.w   r0, r0, #1
   8:   3801        subs    r0, #1

编辑奥拉夫的评论。

.thumb
sub r0,#1
sub r0,r0,#1
sub r1,r2,#1
.syntax unified
subs r0,r0,#1
sub r0,#1
subs.n r0,#1
subs.n r0,r0,#1
subs r1,r2,#1


00000000 <.text>:
   0:   3801        subs    r0, #1
   2:   3801        subs    r0, #1
   4:   1e51        subs    r1, r2, #1
   6:   3801        subs    r0, #1
   8:   f1a0 0001   sub.w   r0, r0, #1
   c:   3801        subs    r0, #1
   e:   3801        subs    r0, #1
  10:   1e51        subs    r1, r2, #1

我/你/我们将不得不深入研究汇编源代码,看看是否有办法解决这个问题。Fuz 可能有最简单的答案。很可能,汇编程序立即优化为具有更大灵活性的汇编程序。您当然可以删除该优化/功能(如果没有标志)。

编辑2

也许就是这样。

#define T_OPCODE_SUB_I8 0x3800
#define T_OPCODE_SUB_I3 0x1e00

    else if (rs == rd)
      {
        if (value & ~0xff)
          as_bad_where (fixP->fx_file, fixP->fx_line,
                _("immediate value out of range"));
        newval = subtract ? T_OPCODE_SUB_I8 : T_OPCODE_ADD_I8;
        newval |= (rd << 8) | value;
      }
    else
      {
        if (value & ~0x7)
          as_bad_where (fixP->fx_file, fixP->fx_line,
                _("immediate value out of range"));
        newval = subtract ? T_OPCODE_SUB_I3 : T_OPCODE_ADD_I3;
        newval |= rd | (rs << 3) | (value << 6);
      }

如果你添加这个

else if (value & ~0x7)
  {
    newval = subtract ? T_OPCODE_SUB_I3 : T_OPCODE_ADD_I3;
    newval |= rd | (rs << 3) | (value << 6);
  }

在...前面

else if (rs == rd)

然后

.cpu cortex-m7
.thumb
sub r0,r0,#15
sub r0,r0,#1
.syntax unified
subs.n r0,r0,#1
subs.n r0,r0,#15

给出了想要的结果。

   0:   1fc0        subs    r0, r0, #7
   2:   3801        subs    r0, #1
   4:   3801        subs    r0, #1
   6:   1fc0        subs    r0, r0, #7

所以我认为 rs == rd 会阻止你生成你想要的指令。

编辑 3

binutils 2.7 没有这个定义 T_OPCODE_SUB_I8 所以可能没有拇指支持,没有比这更深入。binutils 2.8 做了并且也包括了这个优化。早在 1997 年,这种情况就已经存在。如果您想使用 gnu 汇编器生成相关指令,您似乎需要修改 gnu 汇编器...

于 2018-04-29T17:26:02.560 回答