正确比较 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 个库。
当您进入裸机时,列表中的一件事就是使用其他人的沙箱或掌握这些工具。看来你想掌握这些工具,这很好,上面看起来很容易......如果你不把它复杂化,它仍然很容易。
我已经习惯了查看 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 个库。
当您进入裸机时,列表中的一件事就是使用其他人的沙箱或掌握这些工具。看来你想掌握这些工具,这很好,上面看起来很容易......如果你不把它复杂化,它仍然很容易。