I want to try and see what some code is doing at the assembly level but I am unsure on how to get Platform IO to output an Assembly file.
The platform I am compiling for is the ESP32.
Any suggestions are welcome.
I want to try and see what some code is doing at the assembly level but I am unsure on how to get Platform IO to output an Assembly file.
The platform I am compiling for is the ESP32.
Any suggestions are welcome.
Disassembling the whole firmware will result in a ton of assembly, but you can still do it. PIO builds you the firmware.elf
(which has some symbol information in it). So you can use the xtensa-esp32-elf-objdump
program of your C:\Users\<user>\.platformio\packages\toolchain-xtensa32\bin
package to disassemble that, or one specific file.
E.g. main.cpp
#include <Arduino.h>
int do_some_computations(int a, int b, int c) {
return a*b+c;
}
void setup() {
Serial.begin(115200);
Serial.println("Output: " + String(do_some_computations(1,2,3)));
}
void loop() { }
Then build it
>pio run
Linking .pio\build\esp32\firmware.elf
Building .pio\build\esp32\firmware.bin
Retrieving maximum program size .pio\build\esp32\firmware.elf
Checking size .pio\build\esp32\firmware.elf
esptool.py v2.6
Advanced Memory Usage is available via "PlatformIO Home > Project Inspect"
DATA: [ ] 4.7% (used 15436 bytes from 327680 bytes)
PROGRAM: [== ] 16.6% (used 217693 bytes from 1310720 bytes)
============================================= [SUCCESS] Took 16.54 seconds =============================================
And disassemble it…
C:\Users\Maxi\Documents\stackoverflow_testing>"C:\Users\Maxi\.platformio\packages\toolchain-xtensa32\bin\xtensa-esp32-elf-objdump.exe" -C -d .pio\build\esp32\firmware.elf
.pio\build\esp32\firmware.elf: file format elf32-xtensa-le
[...]
You can selectively disassemble one object file (but then it isn’t aware of the other object files yet before linking)
C:\Users\Maxi\Documents\stackoverflow_testing>"C:\Users\Maxi\.platformio\packages\toolchain-xtensa32\bin\xtensa-esp32-elf-objdump.exe" -C -d .pio\build\esp32\src\main.cpp.o
[..]
Disassembly of section .text._Z20do_some_computationsiii:
00000000 <do_some_computations(int, int, int)>:
0: 004136 entry a1, 32
3: 822230 mull a2, a2, a3
6: 224a add.n a2, a2, a4
8: f01d retw.n
So you can see here how return a*b+c;
is implemented via first multiplying the first and second argument register (a2
, a3
) into the second argument register, to which then the third argument register a4
is added. a1
is the stackpointer and a2
doubles as one return register (see The ESP8266 and ABI Interface - Linux/Xtensa, XTensa reference)
You can also add the GCC option
build_flags = -S
to the platformio.ini
(docs), which will make GCC output the assembly. So now all the .pio\build\esp32\*.o
files will actually be assembler output files
So if I e.g. change the function to return a*b+3;
recompile and read the file I get
C:\Users\Maxi\Documents\stackoverflow_testing>cat .pio\build\esp32\src\main.cpp.o
.file "main.cpp"
.text
.Ltext0:
.section .text._Z20do_some_computationsiii,"ax",@progbits
.align 4
.global _Z20do_some_computationsiii
.type _Z20do_some_computationsiii, @function
_Z20do_some_computationsiii:
.LFB2430:
.file 1 "src\\main.cpp"
.loc 1 3 0
.LVL0:
entry sp, 32
.LCFI0:
.loc 1 4 0
mull a2, a2, a3
.LVL1:
.loc 1 5 0
addi.n a2, a2, 3
retw.n
So now it used mull
and addi
with a constant 3.
This is my platformio.ini which gets me the avr assembly code files
[env:nanoatmega328]
platform = atmelavr
board = nanoatmega328
framework = arduino
; keep asm .s, .i and .ii files in source dir (need to clean manually)
build_flags =
-save-temps
-fverbose-asm
; Serial Monitor options
monitor_speed = 115200
Thanks for the contribution @schaloule, but the OP was after:
as opposed to your AVR specific answer. I’m sure this may come in handy though if someone is after AVR info and comes across this thread though!
I’d like to say thanks very much to @schaloule, those flags work splendidly for ESP32 as well. I would like to add a suggestion for others visiting here… which is to use the obj option on save-temps (then the temporary files don’t go into the top level folder)…
build_flags =
-save-temps=obj
-fverbose-asm
The obj option is documented here.
so is there anyway to automatically generate a disassembly file after build using objdump tool?
I haven’t needed to do this sort of thing myself, but this link might help:
https://docs.platformio.org/en/latest/projectconf/sections/env/options/advanced/extra_scripts.html
Cheers,
Norm.
i will give it a try,thanks~
Until a few days ago this was working well. By using:
[env:ATmega328P]
platform = atmelavr
board = ATmega328P
#framework = arduino
build_flags =
-save-temps=obj
-fverbose-asm
at least two files containing the disassembly listing identical to the listings that could be manually generated by objdump
were generated when running build
but now .o
, .s
and .ii
files are completely different and do not contain the disassembly listings anymore.
I tried adding -S
flag:
build_flags =
-S
-save-temps=obj
-fverbose-asm
but then ld.exe
reports this error:
main.o: file format not recognized; treating as linker script
Until a few days ago some of autogenerated files contained the interrupt vectors, main
, -exit
, __stop_program
and commented assembly as generated with:
avr-objdump.exe -S firmware.elf
and
avr-objdump.exe -SD firmware.elf
but not anymore. Seems as the change was caused by some recent update.