Why does clang insert a giant block of padding in the middle of my function? How do I prevent this?

I am attempting to write a bare-metal armv7a kernel from scratch. When compiling my implementation of printf
, the resulting binary contains a massive block of andmi
instructions:
$ arm-none-eabi-objdump --disassembler-color=extended --visualize-jumps=color -Dx cmake-build-debug-arm32/leg.elf | less -R
4000874c <printf>:
4000874c: ,----------------------> e24dd00c sub sp, sp, #12
40008750: | e92d4ff0 push {r4, r5, r6, r7, r8, r9, sl, fp, lr}
40008754: | e28db01c add fp, sp, #28
40008758: | e24dd028 sub sp, sp, #40 @ 0x28
4000875c: | e1a07000 mov r7, r0
40008760: | e58b1008 str r1, [fp, #8]
40008764: | e58b200c str r2, [fp, #12]
40008768: | e58b3010 str r3, [fp, #16]
4000876c: | e28b0008 add r0, fp, #8
40008770: | e58d0004 str r0, [sp, #4]
40008774: | e3a06000 mov r6, #0
40008778: | e28d8008 add r8, sp, #8
4000877c: | e30991eb movw r9, #37355 @ 0x91eb
40008780: | e3449000 movt r9, #16384 @ 0x4000
40008784: | ,-------------> e1a04007 mov r4, r7
40008788: | | e4d40001 ldrb r0, [r4], #1
4000878c: | | e3500025 cmp r0, #37 @ 0x25
40008790: | | ,-- 0a000005 beq 400087ac <printf+0x60>
40008794: | | | e3500000 cmp r0, #0
40008798: | ,-----|-----------|-- 0a000110 beq 40008be0 <printf+0x494>
4000879c: | | | | eb000257 bl 40009100 <putchar>
400087a0: | | | | e2866001 add r6, r6, #1
400087a4: | | | | e1a07004 mov r7, r4
400087a8: | | +-----------|-- eafffff5 b 40008784 <printf+0x38>
400087ac: | | | '-> e5d75001 ldrb r5, [r7, #1]
400087b0: | | | e2450025 sub r0, r5, #37 @ 0x25
400087b4: | | | e3500053 cmp r0, #83 @ 0x53
400087b8: | | ,--|-------------- 8a0000fa bhi 40008ba8 <printf+0x45c>
400087bc: | | | | e28f1000 add r1, pc, #0
400087c0: | | | | e791f100 ldr pc, [r1, r0, lsl #2]
400087c4: | | | | 40008aa4 andmi r8, r0, r4, lsr #21
400087c8: | | | | 40008ba8 andmi r8, r0, r8, lsr #23
400087cc: | | | | 40008ba8 andmi r8, r0, r8, lsr #23
400087d0: | | | | 40008ba8 andmi r8, r0, r8, lsr #23
400087d4: | | | | 40008ba8 andmi r8, r0, r8, lsr #23
400087d8: | | | | 40008ba8 andmi r8, r0, r8, lsr #23
400087dc: | | | | 40008ba8 andmi r8, r0, r8, lsr #23
400087e0: | | | | 40008ba8 andmi r8, r0, r8, lsr #23
400087e4: | | | | 40008ba8 andmi r8, r0, r8, lsr #23
400087e8: | | | | 40008ba8 andmi r8, r0, r8, lsr #23
...
...
40008904: | | | | 4000895c andmi r8, r0, ip, asr r9
40008908: | | | | 40008ba8 andmi r8, r0, r8, lsr #23
4000890c: | | | | 40008ba8 andmi r8, r0, r8, lsr #23
40008910: | | | | 400089bc @ <UNDEFINED> instruction: 0x400089bc
40008914: | | | | e59d0004 ldr r0, [sp, #4]
40008918: | | | | e2801004 add r1, r0, #4
4000891c: | | | | e58d1004 str r1, [sp, #4]
40008920: | | | | e5900000 ldr r0, [r0]
40008924: | | | | e28d5008 add r5, sp, #8
40008928: | | | | e1a01005 mov r1, r5
I have tried several combinations of compiler flags, including optimizing for size or enabling/disabling features, but nothing seems to affect this. I did notice that the encoding of the pad instructions looks suspiciously similar to the current program address, but I don't really know what to do with that.
The permalink to the source that generates this binary is below. My compiler options can be found in the root CMakeLists.txt
.
https://github.com/romirk/leg/blob/aabad666439dbd6be0eda4862cdee9a90e8a8245/src/stdio.c#L63
Any help diagnosing this is very appreciated. Thanks!
Answer
It is a jump table. The instructions that use it are immediately before it. add r1, pc, #0
loads a starting address for the table, and ldr pc, [r1, r0, lsl #2]
loads the element at offset r0*4
in the table into the program counter.
Enjoyed this question?
Check out more content on our blog or follow us on social media.
Browse more questions