Disassembling Arduino (Due) code

Once you profile Arduino code, you may wonder why timing changes with little change to the Arduino code. Looking at the assembly code may help (or produce more questions.) For reference, the instruction set for the Arduino Due can be found starting in section 12.9 of the AT91SAM ARM MCU datasheet. And for the Arduino Uno, the summary is section 31 of the ATmega 8-bit AVR MCU datasheet and 8-bit AVR instruction set manual.

To quickly see the assembly code, in this case for an Arduino Duo with v1.5.4 of the Windows Arduino IDE, but other boards and Arduino IDE versions may be similar, first enable Verbose Output for compilation in the IDE preferences (File->Preferences). Then compile your code and look for a line like the following very near the bottom of the IDE's scrolling log.

C:\Program Files (x86)\Arduino/hardware/tools/g++_arm_none_eabi/bin/arm-none-eabi-objcopy -O binary C:\Users\kent\AppData\Local\Temp\build6841079203588404114.tmp/sketch_nov24a.cpp.elf

Paste this into a command window, modifying objcopy to objdump. The path may vary depending on IDE version and Arduino board, e.g. AVR vs. SAM. If the wrong objdump is used, you'll likely see an error about an unknown architecture.

C:\Program Files (x86)\Arduino/hardware/tools/g++_arm_none_eabi/bin/arm-none-eabi-objdump -S C:\Users\kent\AppData\Local\Temp\build6841079203588404114.tmp/sketch_nov24a.cpp.elf

Several interesting options are available, but the -S option may be the nicest for dissembling as it will provide some source code context, but -d and -D are alternatives. To intermix the source code with the disassembly, it seems necessary to point objdump to the directory with the sketch's .ino source files using the -I option, which the only indication if wrong is the absence of Arduino source code in the disassembly. The default options to the compiler are for this version of the Arduino IDE, include optimizing for code size (-Os) and including debug symbols (-g).

Usage: C:\Program Files (x86)\Arduino\hardware\tools\g++_arm_none_eabi\bin\arm-none-eabi-objdump.exe <option(s)> <file(s)>
Display information from object <file(s)>.
At least one of the following switches must be given:
-a, --archive-headers    Display archive header information
-f, --file-headers       Display the contents of the overall file header
-p, --private-headers    Display object format specific file header contents
-h, --[section-]headers  Display the contents of the section headers
-x, --all-headers        Display the contents of all headers
-d, --disassemble        Display assembler contents of executable sections
-D, --disassemble-all    Display assembler contents of all sections
-S, --source             Intermix source code with disassembly
-s, --full-contents      Display the full contents of all sections requested
-g, --debugging          Display debug information in object file
-e, --debugging-tags     Display debug information using ctags style
-G, --stabs              Display (in raw form) any STABS info in the file
-W[lLiaprmfFsoR] or
--dwarf[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=str,=loc,=Ranges]
Display DWARF info in the file
-t, --syms               Display the contents of the symbol table(s)
-T, --dynamic-syms       Display the contents of the dynamic symbol table
-r, --reloc              Display the relocation entries in the file
-R, --dynamic-reloc      Display the dynamic relocation entries in the file
@<file>                  Read options from <file>
-v, --version            Display this program's version number
-i, --info               List object formats and architectures supported
-H, --help               Display this information

The following switches are optional:
-b, --target=BFDNAME           Specify the target object format as BFDNAME
-m, --architecture=MACHINE     Specify the target architecture as MACHINE
-j, --section=NAME             Only display information for section NAME
-M, --disassembler-options=OPT Pass text OPT on to the disassembler
-EB --endian=big               Assume big endian format when disassembling
-EL --endian=little            Assume little endian format when disassembling
--file-start-context       Include context from start of file (with -S)
-I, --include=DIR              Add DIR to search list for source files
-l, --line-numbers             Include line numbers and filenames in output
-F, --file-offsets             Include file offsets when displaying information
-C, --demangle[=STYLE]         Decode mangled/processed symbol names
The STYLE, if specified, can be `auto', `gnu',
`lucid', `arm', `hp', `edg', `gnu-v3', `java'
or `gnat'
-w, --wide                     Format output for more than 80 columns
-z, --disassemble-zeroes       Do not skip blocks of zeroes when disassembling
--start-address=ADDR       Only process data whose address is >= ADDR
--stop-address=ADDR        Only process data whose address is <= ADDR
--prefix-addresses         Print complete address alongside disassembly
--[no-]show-raw-insn       Display hex alongside symbolic disassembly
--adjust-vma=OFFSET        Add OFFSET to all displayed section addresses
--special-syms             Include special symbols in symbol dumps
--prefix=PREFIX            Add PREFIX to absolute paths for -S
--prefix-strip=LEVEL       Strip initial directory names for -S

C:\Program Files (x86)\Arduino\hardware\tools\g++_arm_none_eabi\bin\arm-none-eabi-objdump.exe: supported targets: elf32-littlearm elf32-bigarm elf32-little elf32-big srec symbolsrec verilog tekhex binary ihex
C:\Program Files (x86)\Arduino\hardware\tools\g++_arm_none_eabi\bin\arm-none-eabi-objdump.exe: supported architectures: arm armv2 armv2a armv3 armv3m armv4 armv4t armv5 armv5t armv5te xscale ep9312 iwmmxt iwmmxt2

The following ARM specific disassembler options are supported for use with
the -M switch:
reg-names-special-atpcs  Select special register names used in the ATPCS
reg-names-atpcs          Select register names used in the ATPCS
reg-names-apcs           Select register names used in the APCS
reg-names-std            Select register names used in ARM's ISA documentation
reg-names-gcc            Select register names used by GCC
reg-names-raw            Select raw register names
force-thumb              Assume all insns are Thumb insns
no-force-thumb           Examine preceeding label to determine an insn's type

Report bugs to <https://support.codesourcery.com/GNUToolchain/>.

In the disassembled code, search for setup, loop, or whatever function you are interested in. Here a sample output segment is shown along with the Arduino source code. There is some unnecessary duplication in the intermixed source code.

C:\> <path_to_objdump>\objdump.exe -S -I <path_to_sketch_source_directory> <path_to_sketch_elf_file> | more

E.g.

C:\> "C:\Program Files (x86)\Arduino/hardware/tools/g++_arm_none_eabi/bin/arm-none-eabi-objdump" -S -I"C:\Users\kent\Documents\Arduino\test\sketch_nov24a" "C:\Users\kent\AppData\Local\Temp\build6841079203588404114.tmp/sketch_nov24a.cpp.elf" | more


00080124 <setup>:
void setup() {
80124:       b508            push    {r3, lr}
pinMode(31, OUTPUT);
80126:       201f            movs    r0, #31
80128:       2101            movs    r1, #1
8012a:       f000 f887       bl      8023c <pinMode>
8012e:       2380            movs    r3, #128        ; 0x80

int v=0;
while(1) {
v = v ? 0 : 1<<7;
REG_PIOA_ODSR = v;
80130:       4a03            ldr     r2, [pc, #12]   ; (80140 <setup+0x1c>)
80132:       6013            str     r3, [r2, #0]
void setup() {
pinMode(31, OUTPUT);

int v=0;
while(1) {
v = v ? 0 : 1<<7;
80134:       2b00            cmp     r3, #0
80136:       bf0c            ite     eq
80138:       2380            moveq   r3, #128        ; 0x80
8013a:       2300            movne   r3, #0
8013c:       e7f8            b.n     80130 <setup+0xc>
8013e:       bf00            nop
80140:       400e0e38        .word   0x400e0e38

The Arduino code:

void setup() {
pinMode(31, OUTPUT);

int v=0;
while(1) {
v = v ? 0 : 1<<7;
REG_PIOA_ODSR = v;
}
}

It would be handy to have disassembly functionality available from the IDE.