【问题标题】:Properly comparing Arm machine code to assembly正确比较 Arm 机器代码和程序集
【发布时间】:2020-11-23 21:01:34
【问题描述】:

我已经习惯了在 Arm 上查看汇编输出,并且认为查看机器代码会很有趣,因为我有参考并且可以轻松地在我面前看到它。但是,我发现它与我期望看到的不符。这告诉我我在某个地方有点误会。也许有人可以帮助我。这是我的汇编:

    .arch armv8-m.main
    .eabi_attribute 20, 1
    .eabi_attribute 21, 1
    .eabi_attribute 23, 3
    .eabi_attribute 24, 1
    .eabi_attribute 25, 1
    .eabi_attribute 26, 1
    .eabi_attribute 30, 6
    .eabi_attribute 34, 1
    .eabi_attribute 18, 4
    .file   "arm_test.c"
    .text
    .align  1
    .global main
    .syntax unified
    .thumb
    .thumb_func
    .fpu softvfp
    .type   main, %function
main:
    @ args = 0, pretend = 0, frame = 8
    @ frame_needed = 1, uses_anonymous_args = 0
    @ link register save eliminated.
    push    {r7}
    sub sp, sp, #12
    add r7, sp, #0
    movs    r3, #2
    str r3, [r7, #4]
    ldr r3, [r7, #4]
    adds    r3, r3, #13
    str r3, [r7]
    movs    r3, #0
    mov r0, r3
    adds    r7, r7, #12
    mov sp, r7
    @ sp needed
    pop {r7}
    bx  lr
    .size   main, .-main
    .ident  "GCC: (GNU Arm Embedded Toolchain 9-2020-q2-update) 9.3.1 20200408 (release)"

这是我的十六进制输出的开始(来自 objcopy):

:0C800000F8B500BFF8BC08BC9E467047F5
:10800C0008B50021044600F0F5F8044B1868C36B62
:10801C0003B19847204600F0C7F900BFC4830000A5
:10802C00024B13B1024800F047B970470000000042
:10803C00D182000010B5054C237833B9044B13B131
:10804C00044800E000BF0123237010BD1488010018
:10805C0000000000D083000008B5034B1BB103499E
:10806C00034800E000BF08BD0000000018880100B4

现在由于 arm 默认为 little-endian,我希望第一个字节 (0x47) 是 PUSH 命令。但是 0x47 = 0b01000111 在我看来就像是一条 BLX 指令。很好,也许它从一个分支开始?如果这是真的,那么由于它是一条 16 位指令,因此下一条指令以 0x46 开头(我认为?)。现在 0x46 = 0b01000110 看起来像一个 MOV(另一个 16 位指令)。 ...但是现在已经开始觉得这与 ASM 不对应...所以,既然我显然看错了,谁能指出我正确的起点?

【问题讨论】:

  • 你是如何创建这个十六进制的?此外,您可能对装配列表感兴趣,例如as -al 和/或反汇编,例如objdump -d
  • @Jester 我刚刚使用arm-eabi-none-objcopy -O ihex input.elf output.hex 来获取十六进制
  • @TrivialCase 您是如何从 asm 到 object 再到 elf,特别是从 object 到 elf?
  • @old_timer asm 来自arm-eabi-none-gcc -march=armv8-m.main -S in.c -o out.S,elf 是一样的,但没有-Sout.elf。根据这些问题,我猜这在某种程度上是错误的?
  • armv8-m 表示微控制器,这意味着您可能不想要作为此交叉编译器一部分的库存引导文件和链接器脚本。这导致你从哪里得到这个交叉编译器,它是专门为这个特定的 mcu 或板构建和打包的吗?如果不是,那么不,那不是您想要的方式。

标签: assembly arm machine-code


【解决方案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 <main>:
   0:   b480        push    {r7}
   2:   b083        sub sp, #12
   4:   af00        add r7, sp, #0
   6:   2302        movs    r3, #2
   8:   607b        str r3, [r7, #4]
   a:   687b        ldr r3, [r7, #4]
   c:   330d        adds    r3, #13
   e:   603b        str r3, [r7, #0]
  10:   2300        movs    r3, #0
  12:   4618        mov r0, r3
  14:   370c        adds    r7, #12
  16:   46bd        mov sp, r7
  18:   bc80        pop {r7}
  1a:   4770        bx  lr

对于 armv8-m,您的 hex 文件有点腥味。 0x8000 的低地址?同样,这是对象还是完整的二进制文件?十六进制文件仅作为一个完整的二进制文件才有意义,其中反汇编对象不是 100% 熟,而是比汇编语言本身更熟。

:0C800000F8B500BFF8BC08BC9E467047F5
:10800C0008B50021044600F0F5F8044B1868C36B62
:10801C0003B19847204600F0C7F900BFC4830000A5

注意:

$(ARMGNU)-objcopy --srec-forceS3 so.elf -O srec so.srec

虽然您可能对英特尔与摩托罗拉有强烈的宗教立场。使用 S3 行,您可以获得完整的地址。底线是二进制文件的消费者,您必须匹配消费者使用的格式(MCU 编程工具等)。当我在启用 S3 的情况下制作自己的工具时,srec 是可行的方法。许多工具直接支持 elf 文件,因此通常不需要这些文件。其他支持原始二进制图像(-O 二进制),因此不需要十六进制格式。 YMMV

嗯,push {r7} 很清楚,你的 hex 文件中只有一个 b4,它是一个校验和。

这一行有一个bx lr

:10802C00024B13B1024800F047B9 7047 0000000042

所以第一次尝试是

.thumb
.inst.n 0x4B02
.inst.n 0xB113  
.inst.n 0x4802
.inst.n 0xF000
.inst.n 0xB947
.inst.n 0x4770
.inst.n 0x0000
.inst.n 0x0000

给了

00000000 <.text>:
   0:   4b02        ldr r3, [pc, #8]    ; (c <.text+0xc>)
   2:   b113        cbz r3, a <.text+0xa>
   4:   4802        ldr r0, [pc, #8]    ; (10 <.text+0x10>)
   6:   f000 b947   b.w 298 <.text+0x298>
   a:   4770        bx  lr
   c:   0000        movs    r0, r0

因为有一个 b.w 被拆卸,所以没有必要更进一步。你只有一个 bx lr,所以

哦,等一下,第一行也有一个

   0:   b5f8        push    {r3, r4, r5, r6, r7, lr}
   2:   bf00        nop
   4:   bcf8        pop {r3, r4, r5, r6, r7}
   6:   bc08        pop {r3}
   8:   469e        mov lr, r3
   a:   4770        bx  lr

所以我在 ihex 文件中看不到您的(机器)代码。

我们也没有看到每四个字节有很多 0xEx 字节,所以它可能不是全尺寸的 arm 指令,即使 0x8000 有点暗示这是作为全尺寸 arm 的 linux 二进制文件构建的。

objcopy 从对象(elf,不是链接的elf)制作十六进制类型文件

S00A00006F75742E7478740F
S3150000000080B483B000AF02237B607B680D333B6016
S31100000010002318460C37BD4680BC704724
S70500000000FA

:1000000080B483B000AF02237B607B680D333B601C
:0C001000002318460C37BD4680BC70472A
:00000001FF

我们可以看到前面的 0xb480 和末尾的 0x4770。请注意,某些工具不会对十六进制文件进行字节交换,您可能会看到 4770 而不是 7047,这并没有错,只是世界有时是这样的……十六进制文件的创建者和使用者都需要同步这个。

编辑

极少,但这是您想要做的事情

flash.s(向量表和引导程序)

.thumb
.word 0x20001000
.word reset
.thumb_func
reset:
    bl main
    b .

flash.ld(链接器脚本)

MEMORY
{
    bob : ORIGIN = 0x08000000, LENGTH = 0x1000
    ted : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
    .hello : { *(.text*) } > bob
    .world : { *(.data*) } > ted
}

构建

arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m23 flash.s -o flash.o
arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m23 -mthumb -c so.s -o so.o
arm-none-eabi-ld -nostdlib -nostartfiles -T flash.ld flash.o so.o -o so.elf
arm-none-eabi-objdump -D so.elf > so.list
arm-none-eabi-objcopy --srec-forceS3 so.elf -O srec so.srec
arm-none-eabi-objcopy -O binary so.elf so.bin

检查

08000000 <reset-0x8>:
 8000000:   20001000    andcs   r1, r0, r0
 8000004:   08000009    stmdaeq r0, {r0, r3}

08000008 <reset>:
 8000008:   f000 f801   bl  800000e <main>
 800000c:   e7fe        b.n 800000c <reset+0x4>

0800000e <main>:
 800000e:   b480        push    {r7}
 8000010:   b083        sub sp, #12
 8000012:   af00        add r7, sp, #0
 8000014:   2302        movs    r3, #2
 8000016:   607b        str r3, [r7, #4]
 8000018:   687b        ldr r3, [r7, #4]
 800001a:   330d        adds    r3, #13
 800001c:   603b        str r3, [r7, #0]
 800001e:   2300        movs    r3, #0
 8000020:   4618        mov r0, r3
 8000022:   370c        adds    r7, #12
 8000024:   46bd        mov sp, r7
 8000026:   bc80        pop {r7}
 8000028:   4770        bx  lr

(naturally you can make an ihex or whatever other format you want)

S00A0000736F2E7372656338
S31508000000001000200900000800F001F8FEE780B49F
S3150800001083B000AF02237B607B680D333B6000230F
S30F0800002018460C37BD4680BC704731
S70500000000FA

hexdump -C so.bin
00000000  00 10 00 20 09 00 00 08  00 f0 01 f8 fe e7 80 b4  |... ............|
00000010  83 b0 00 af 02 23 7b 60  7b 68 0d 33 3b 60 00 23  |.....#{`{h.3;`.#|
00000020  18 46 0c 37 bd 46 80 bc  70 47                    |.F.7.F..pG|
0000002a

向量表在正确的位置并且看起来不错(处理程序地址 ORRed 为 1),因此它不会在启动时挂起。不知道你有哪个特定的核心,我只拿了一个,所以我可以使用 -mcpu ...如果你选择 cortex-m0,它到目前为止可以在所有的 cortex-ms 上工作,有时可能会更慢,但会起作用。更大的错误是将 armv7-m 的东西剪切并粘贴到 armv6-m 或一些 armv8-ms 上,这将不起作用,因此还要检查反汇编以查看其中是否有任何 armv7-m 指令,这会很好地让你进入处理程序以及(因为向量表是错误的)

大多数人都希望支持 .data 和 .bss 并对其进行初始化,这使得链接器脚本和引导程序更加复杂。

EDIT2

如果你想半直接使用C(gcc为你调用汇编器)

so.c

int main ( void )
{
    return(5);
}

几乎一样,这里也改成-m33。

arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m33 flash.s -o flash.o
arm-none-eabi-gcc -Wall -O2 -ffreestanding -mcpu=cortex-m33 -mthumb -c so.c -o so.o
arm-none-eabi-ld -nostdlib -nostartfiles -T flash.ld flash.o so.o -o so.elf
arm-none-eabi-objdump -D so.elf > so.list
arm-none-eabi-objcopy -O binary so.elf so.bin

给予

08000000 <reset-0x8>:
 8000000:   20001000    andcs   r1, r0, r0
 8000004:   08000009    stmdaeq r0, {r0, r3}

08000008 <reset>:
 8000008:   f000 f802   bl  8000010 <main>
 800000c:   e7fe        b.n 800000c <reset+0x4>
    ...

08000010 <main>:
 8000010:   2005        movs    r0, #5
 8000012:   4770        bx  lr

0x08000000 是 stm32 的大部分(如果不是全部的话)(一些 0x00200000),0x01000000 是我所知道的 ti(msp432 和以前的 luminary micros)。如果我没记错的话,nxp 可能是 0x00000000。我不记得北欧或其他人。只需阅读文档。从技术上讲,所有这些都应该适用于使用 0x00000000 的小型二进制文件,但例如核板(stm32)不允许您将 .bin 文件复制到它上面,如果地址错误,则会出错。飞思卡尔或某人对复制bin文件的事情有更多的规定。

您确实需要自己控制引导程序和链接器脚本,即使您是从别人那里借来的。 C 库或 hal 库也将发挥作用,也许这就是您实际构建东西的方式,因为那里的 makefile 使用 hal/cmsis/other 库中的引导程序和链接器脚本。出于某种原因,许多人会制作繁重的链接器脚本,试图解决所有问题,引导程序、C 库(如果有的话)、芯片库、编译器库等。而不是精益求精,让事情自然而然地工作。我建议从最小化开始,然后变得更复杂,但是你会牺牲一个 C 库和供应商提供的 hal/任何库。

当您使用裸机时,清单中的一件事就是使用别人的沙箱或掌握工具。看起来你想掌握工具,那很好,上面的东西看起来很容易......如果你不把它复杂化,那么它仍然很容易。

【讨论】:

  • thumb2 是可变长度指令,从技术上讲(我已在此站点上演示过)混淆 objdump 是可行的,但如果您不剥离标签并且不努力尝试,反汇编看起来会很好。请注意,它也会反汇编数据(向量表),努力反汇编所有内容。用 arm 一点也不差,但是用 x86 反汇编这个问题就很严重了。
  • 非常感谢您的所有帮助,您在这里给了我很多乐趣。
  • 顺便说一句,是的,我正在非常努力地掌握工具和平台......进步是艰苦的,但非常有趣和有益。
  • 这非常有帮助和启发 - 非常感谢您!
  • 请先付款。去回答几个问题或阻塞交通,这样老人就可以过马路,为某人开门……等等……
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多