I have ARM assembly source code:
.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
Which is a simple sum of array. So now I compile this using this sequence of commands in my Linux terminal:
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
And if I check size of compiled files:
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
I see that if I strip header from my executable .elf
i get .bin
file which is exactly 48
Bytes long. This means it can have 12 ARM instructions right?
Now I prepare the 16KiB FLASH image, copy the .bin
file to the FLASH image and start QEMU simulation on the connex
board like this:
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
While in simulator if I check registers using info registers
, I get:
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
Which looks fine as register R03
holds hexadecimal value 0x37
which is 55
in decimal and it is the correct sum of the array I supplied with a command .byte 10, 20, 25
.
What I don't understand is if I dump first 12 commands using xp /12wi 0x0
, I get this:
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
How can I justify commands 2-4 and 11-12 to myself? And how the he** is my sum 55 even calculated?
ldr r0, =eoa
and ldr r1, =array
are a pseudo instruction as they do not exist in the ARM instruction set. They cannot directly be converted into ARM assembler. When the compiler see these instructions, it converts them into whatever it is most efficient.
If you look at your disassembly:
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
You can see the compiler the base address of you array (ie: 0x4 because it is 32bit aligned) and the end of the array (ie: base address of the array + 3 bytes = 0x7) respectively at the offset 0x28 (pc
before 0x8 + 0x24) and 0x2c (pc
before 0xc + 0x24).
Note: It is also possible ldr rN, =#immediate
be encoded into a single assembly line. ARM instruction set support a 16bit immediate value (ie: from 0 to 65535). See page 2 of ARM instruction set. The compiler will try to use the most efficient encoding.
Actually replacing both ldr rN, =label
by adr rN, label
would be more efficient! ADR
form PC-relative address using label. Your code would be built as:
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>
Note: The only change I made to your original code was to replace:
ldr r0, =eoa
ldr r1, =array
By:
adr r0, eoa
adr r1, array
User contributions licensed under CC BY-SA 3.0