GDB ARM 汇编程序指令以我不理解的方式编译
GDB ARM assembler directives are compiled in a way I don't understand
我有ARM汇编源代码:
.global _start
.text
entry: b _start
array: .byte 10, 20, 25
eoa:
.align
_start:
ldr r0, =eoa
ldr r1, =array
mov r3, #0
loop:
ldrb r2, [r1], #1
add r3, r2, r3
cmp r1, r0
bne loop
stop: b stop
这是数组的简单求和。所以现在我在 Linux 终端中使用以下命令序列编译它:
arm-none-eabi-as -o program.o program.s
arm-none-eabi-ld -Ttext=0x0 -o program.elf program.o
arm-none-eabi-objcopy -O binary program.elf program.bin
如果我检查编译文件的大小:
ls -l
-rwxrwxrwx 1 ziga ziga 48 Nov 22 12:38 program.bin
-rwxrwxrwx 1 ziga ziga 66396 Nov 22 12:37 program.elf
-rwxrwxrwx 1 ziga ziga 864 Nov 22 12:36 program.o
-rwxrwxrwx 1 ziga ziga 1952 Jan 3 18:50 program.s
我看到,如果我从我的可执行文件 .elf
中删除 header,我会得到 .bin
文件,该文件恰好 48
字节长。这意味着它可以有 12 条 ARM 指令对吗?
现在我准备好16KiB的FLASH镜像,将.bin
文件复制到FLASH镜像中,然后在connex
板上启动QEMU模拟,如下所示:
dd if=/dev/zero of=flash.bin bs=4096 count=4096
dd if=program.bin of=flash.bin conv=notrunc
qemu-system-arm -M connex -nographic -serial /dev/null -pflash flash.bin
在模拟器中,如果我使用 info registers
检查寄存器,我会得到:
R00=00000007 R01=00000007 R02=00000019 R03=00000037
R04=00000000 R05=00000000 R06=00000000 R07=00000000
R08=00000000 R09=00000000 R10=00000000 R11=00000000
R12=00000000 R13=00000000 R14=00000000 R15=00000024
PSR=600001d3 -ZC- A svc32
FPSCR: 00000000
这看起来不错,因为寄存器 R03
保存十六进制值 0x37
,十进制为 55
,它是我用命令 [=25= 提供的数组的正确总和].
我不明白的是,如果我使用 xp /12wi 0x0
转储前 12 个命令,我会得到:
0x00000000: ea000000 b 0x8
0x00000004: 0019140a andseq r1, r9, sl, lsl #8
0x00000008: e59f0018 ldr r0, [pc, #24] ; 0x28
0x0000000c: e59f1018 ldr r1, [pc, #24] ; 0x2c
0x00000010: e3a03000 mov r3, #0 ; 0x0
0x00000014: e4d12001 ldrb r2, [r1], #1
0x00000018: e0823003 add r3, r2, r3
0x0000001c: e1510000 cmp r1, r0
0x00000020: 1afffffb bne 0x14
0x00000024: eafffffe b 0x24
0x00000028: 00000007 andeq r0, r0, r7
0x0000002c: 00000004 andeq r0, r0, r4
我如何为自己证明命令 2-4 和 11-12 的合理性?我的总和 55 是怎么计算出来的?
ldr r0, =eoa
和 ldr r1, =array
是一个 pseudo instruction as they do not exist in the ARM instruction set。它们不能直接转换成 ARM 汇编器。当编译器看到这些指令时,它会将它们转换成最有效的形式。
如果你看看你的反汇编:
0x00000008: e59f0018 ldr r0, [pc, #24] ; 0x28
0x0000000c: e59f1018 ldr r1, [pc, #24] ; 0x2c
(...)
0x00000028: 00000007 andeq r0, r0, r7
0x0000002c: 00000004 andeq r0, r0, r4
你可以在偏移量处分别看到编译器数组的基地址(即:0x4,因为它是 32 位对齐的)和数组的末尾(即:数组的基地址 + 3 个字节 = 0x7) 0x28(pc
在 0x8 + 0x24 之前)和 0x2c(pc
在 0xc + 0x24 之前)。
注:也可以ldr rN, =#immediate
编码成一条流水线。 ARM 指令集支持 16 位立即数(即:从 0 到 65535)。请参阅 ARM instruction set 的第 2 页。编译器将尝试使用最有效的编码。
实际上将 ldr rN, =label
替换为 adr rN, label
会更有效率! ADR
使用标签形成 PC 相对地址。您的代码将构建为:
00000000 <entry>:
0: eafffffe b 8 <_start>
00000004 <array>:
4: 140a .short 0x140a
6: 19 .byte 0x19
00000007 <eoa>:
...
00000008 <_start>:
8: e24f0009 sub r0, pc, #9
c: e24f1010 sub r1, pc, #16
10: e3a03000 mov r3, #0
00000014 <loop>:
14: e4d12001 ldrb r2, [r1], #1
18: e0823003 add r3, r2, r3
1c: e1510000 cmp r1, r0
20: 1afffffb bne 14 <loop>
00000024 <stop>:
24: eafffffe b 24 <stop>
注意:我对您的原始代码所做的唯一更改是替换:
ldr r0, =eoa
ldr r1, =array
作者:
adr r0, eoa
adr r1, array
我有ARM汇编源代码:
.global _start
.text
entry: b _start
array: .byte 10, 20, 25
eoa:
.align
_start:
ldr r0, =eoa
ldr r1, =array
mov r3, #0
loop:
ldrb r2, [r1], #1
add r3, r2, r3
cmp r1, r0
bne loop
stop: b stop
这是数组的简单求和。所以现在我在 Linux 终端中使用以下命令序列编译它:
arm-none-eabi-as -o program.o program.s
arm-none-eabi-ld -Ttext=0x0 -o program.elf program.o
arm-none-eabi-objcopy -O binary program.elf program.bin
如果我检查编译文件的大小:
ls -l
-rwxrwxrwx 1 ziga ziga 48 Nov 22 12:38 program.bin
-rwxrwxrwx 1 ziga ziga 66396 Nov 22 12:37 program.elf
-rwxrwxrwx 1 ziga ziga 864 Nov 22 12:36 program.o
-rwxrwxrwx 1 ziga ziga 1952 Jan 3 18:50 program.s
我看到,如果我从我的可执行文件 .elf
中删除 header,我会得到 .bin
文件,该文件恰好 48
字节长。这意味着它可以有 12 条 ARM 指令对吗?
现在我准备好16KiB的FLASH镜像,将.bin
文件复制到FLASH镜像中,然后在connex
板上启动QEMU模拟,如下所示:
dd if=/dev/zero of=flash.bin bs=4096 count=4096
dd if=program.bin of=flash.bin conv=notrunc
qemu-system-arm -M connex -nographic -serial /dev/null -pflash flash.bin
在模拟器中,如果我使用 info registers
检查寄存器,我会得到:
R00=00000007 R01=00000007 R02=00000019 R03=00000037
R04=00000000 R05=00000000 R06=00000000 R07=00000000
R08=00000000 R09=00000000 R10=00000000 R11=00000000
R12=00000000 R13=00000000 R14=00000000 R15=00000024
PSR=600001d3 -ZC- A svc32
FPSCR: 00000000
这看起来不错,因为寄存器 R03
保存十六进制值 0x37
,十进制为 55
,它是我用命令 [=25= 提供的数组的正确总和].
我不明白的是,如果我使用 xp /12wi 0x0
转储前 12 个命令,我会得到:
0x00000000: ea000000 b 0x8
0x00000004: 0019140a andseq r1, r9, sl, lsl #8
0x00000008: e59f0018 ldr r0, [pc, #24] ; 0x28
0x0000000c: e59f1018 ldr r1, [pc, #24] ; 0x2c
0x00000010: e3a03000 mov r3, #0 ; 0x0
0x00000014: e4d12001 ldrb r2, [r1], #1
0x00000018: e0823003 add r3, r2, r3
0x0000001c: e1510000 cmp r1, r0
0x00000020: 1afffffb bne 0x14
0x00000024: eafffffe b 0x24
0x00000028: 00000007 andeq r0, r0, r7
0x0000002c: 00000004 andeq r0, r0, r4
我如何为自己证明命令 2-4 和 11-12 的合理性?我的总和 55 是怎么计算出来的?
ldr r0, =eoa
和 ldr r1, =array
是一个 pseudo instruction as they do not exist in the ARM instruction set。它们不能直接转换成 ARM 汇编器。当编译器看到这些指令时,它会将它们转换成最有效的形式。
如果你看看你的反汇编:
0x00000008: e59f0018 ldr r0, [pc, #24] ; 0x28
0x0000000c: e59f1018 ldr r1, [pc, #24] ; 0x2c
(...)
0x00000028: 00000007 andeq r0, r0, r7
0x0000002c: 00000004 andeq r0, r0, r4
你可以在偏移量处分别看到编译器数组的基地址(即:0x4,因为它是 32 位对齐的)和数组的末尾(即:数组的基地址 + 3 个字节 = 0x7) 0x28(pc
在 0x8 + 0x24 之前)和 0x2c(pc
在 0xc + 0x24 之前)。
注:也可以ldr rN, =#immediate
编码成一条流水线。 ARM 指令集支持 16 位立即数(即:从 0 到 65535)。请参阅 ARM instruction set 的第 2 页。编译器将尝试使用最有效的编码。
实际上将 ldr rN, =label
替换为 adr rN, label
会更有效率! ADR
使用标签形成 PC 相对地址。您的代码将构建为:
00000000 <entry>:
0: eafffffe b 8 <_start>
00000004 <array>:
4: 140a .short 0x140a
6: 19 .byte 0x19
00000007 <eoa>:
...
00000008 <_start>:
8: e24f0009 sub r0, pc, #9
c: e24f1010 sub r1, pc, #16
10: e3a03000 mov r3, #0
00000014 <loop>:
14: e4d12001 ldrb r2, [r1], #1
18: e0823003 add r3, r2, r3
1c: e1510000 cmp r1, r0
20: 1afffffb bne 14 <loop>
00000024 <stop>:
24: eafffffe b 24 <stop>
注意:我对您的原始代码所做的唯一更改是替换:
ldr r0, =eoa
ldr r1, =array
作者:
adr r0, eoa
adr r1, array