正确比较 Arm 机器代码和汇编代码

Properly comparing Arm machine code to assembly

我已经习惯了查看 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 默认为小端,我希望第一个字节 (0x47) 是一个 PUSH 命令。但是 0x47 = 0b01000111 对我来说看起来像是一条 BLX 指令。很好,也许它从一个分支开始?如果这是真的,那么由于它是一条 16 位指令,下一条指令从 0x46 开始(我想?)。现在 0x46 = 0b01000110 看起来像一个 MOV(另一个 16 位指令)。 ...但现在已经开始感觉这与 ASM 不符...所以,既然我显然看错了,谁能指出我正确的起点?

按原样使用您的代码。

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} 很清楚你的十六进制文件中只有一个 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 从对象(小精灵,不是链接的小精灵)生成十六进制类型文件

S00A00006F75742E7478740F
S3150000000080B483B000AF02237B607B680D333B6016
S31100000010002318460C37BD4680BC704724
S70500000000FA

:1000000080B483B000AF02237B607B680D333B601C
:0C001000002318460C37BD4680BC70472A
:00000001FF

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

编辑

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

flash.s(矢量 table 和 bootstrap)

.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

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

大多数人希望支持 .data 和 .bss 并初始化它们,这使得链接描述文件更加复杂,bootstrap。

编辑2

如果你想半直接使用 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(msp432s 和以前的发光微处理器)。如果我没记错的话,nxp 可能是 0x00000000 或者有些是。我不记得北欧或其他人。只需阅读文档。从技术上讲,所有这些都应该适用于使用 0x00000000 的小型二进制文件,但例如核板 (stm32) 不允许您复制 .bin 文件,如果地址错误,它会出错。飞思卡尔或某人对复制 bin 文件有更多规则。

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

当您进入裸机时,列表中的一件事就是使用其他人的沙箱或掌握这些工具。看来你想掌握这些工具,这很好,上面看起来很容易......如果你不把它复杂化,它仍然很容易。