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.
@msbit, yes, this seems to be the issue. That's also what I was told on Stack Overflow: https://stackoverflow.com/questions/62105226/arm-thumb-bl-instruction-loops-to-itself. Makes total sense now.