Apparently, both GCC and Clang encode bl label
in such a way that the resulting machine code ends up jumping to itself,
not label
. However, when assembled on an actual thumbv7 machine, the machine code suddenly becomes correct.
What's more, in many cases of "incorrect" encoding that should jump to itself, objdump
somehow recognizes that it jumps
to the correct label.
Put Dockerfile
and mve_docker.s
in the same directory and run:
docker build --tag arm_bl . && docker run --rm arm_bl
SEE COMMENTS next to bl
instructions!
gcc (Alpine 9.3.0) 9.3.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
armv7-alpine-linux-musleabihf
Alpine clang version 10.0.0 (https://gitlab.alpinelinux.org/alpine/aports.git 7445adce501f8473efdb93b17b5eaf2f1445ed4c)
Target: armv7-alpine-linux-musleabihf
Thread model: posix
InstalledDir: /usr/bin
00000000 <_factorial>:
0: b500 push {lr}
2: 2800 cmp r0, #0
4: d008 beq.n 18 <L0_return_one>
6: b401 push {r0}
8: f1a0 0001 sub.w r0, r0, #1
c: f7ff fffe bl 0 <_factorial> # ENCODING: f7ff fffe (ends up jumping to same address! yet objdump puts the correct label)
10: bc02 pop {r1}
12: fb00 f001 mul.w r0, r0, r1
16: e001 b.n 1c <L1_exit>
000004c0 <_factorial>:
4c0: b500 push {lr}
4c2: 2800 cmp r0, #0
4c4: d008 beq.n 4d8 <L0_return_one>
4c6: b401 push {r0}
4c8: f1a0 0001 sub.w r0, r0, #1
4cc: f7ff fff8 bl 4c0 <_factorial> # ENCODING: f7ff fff8 (runs fine, checked in Dockerfile)
4d0: bc02 pop {r1}
4d2: fb00 f001 mul.w r0, r0, r1
4d6: e001 b.n 4dc <L1_exit>
00000000 <_factorial>:
0: b500 push {lr}
2: 2800 cmp r0, #0
4: d008 beq.n 18 <L0_return_one>
6: b401 push {r0}
8: f1a0 0001 sub.w r0, r0, #1
c: f7ff fffe bl 0 <_factorial> # ENCODING: f7ff fffe (jumps to itself, but the label is correct)
10: bc02 pop {r1}
12: fb00 f001 mul.w r0, r0, r1
16: e001 b.n 1c <L1_exit>
000004c0 <_factorial>:
4c0: b500 push {lr}
4c2: 2800 cmp r0, #0
4c4: d008 beq.n 4d8 <L0_return_one>
4c6: b401 push {r0}
4c8: f1a0 0001 sub.w r0, r0, #1
4cc: f7ff fff8 bl 4c0 <_factorial> # ENCODING: f7ff fff8 (works fine)
4d0: bc02 pop {r1}
4d2: fb00 f001 mul.w r0, r0, r1
4d6: e001 b.n 4dc <L1_exit>
When assembled for thumbv7-apple-darwin
on that platform (old iPhone 4) by Clang 3.7 and Clang 9.0, both object files and executables contain exactly the same assembly - the one where bl _factorial
is fff7 f8ff
(note that the words are flipped); both executables run correctly.
If you take a look at the disassembly of
main
, in the*.obj
files, you'll see something like the following:i.e. the same emission of
f7ff fffe bl 0 <_factorial>
.What you are seeing in both these locations is actually a temporary value, which will be replaced with the correct calculated value at link time (hence why the values are correct in the linked binary). The metadata required to allow this replacement (called "relocation") is stored in the relocation section of the binary.
You can see this information using
readelf
: