Inspecting Executable Structure Using Object Dumps
In this lesson, you will learn how to use objdump to peer inside your compiled binary artifacts to verify exactly what instructions are being executed by your target processor.
When you compile code for an embedded system, the final binary image is often an opaque blob. While build tools and linkers manage the complexity of memory layout, objdump allows you to reverse-engineer that blob into human-readable assembly language. This is essential for verifying that your compiler optimizations haven’t introduced unexpected behaviors and for confirming that your code is correctly mapped to the memory addresses defined in your linker scripts.
To inspect your executable, you typically use the command arm-none-eabi-objdump -d firmware.elf. The -d flag instructs the tool to disassemble the executable sections. The output will show you the exact machine instructions mapped to their memory addresses, looking something like this:
08000180 <main>:
8000180: b580 push {r7, lr}
8000182: af00 add r7, sp, #0
8000184: f000 f802 bl 800018c <init_hardware>
8000188: 2001 movs r0, #1
By examining this output, you can confirm several critical architectural details:
- Instruction Alignment: Ensure your functions start on the correct byte boundaries required by the CPU architecture.
- Branch Targets: Verify that function calls (
bl) are jumping to the correct absolute memory addresses, which is vital when you are implementing features like vector table relocation or multi-stage bootloaders. - Optimizations: Check if the compiler has inlined specific functions or if it is generating compact
thumbinstructions as expected, which helps in optimizing your firmware footprint.
When you suspect that a piece of firmware is not behaving as expected—perhaps due to a corrupted branch or an incorrect interrupt vector—objdump is your most reliable tool for confirming the actual state of the instruction stream. It bridges the gap between your high-level C code and the reality of the silicon.
Key Takeaway: Using objdump allows you to disassemble your final binary, providing a verifiable map of instruction placement and control flow that confirms your linker configuration and compiler output are correct.