PlatformIO Community

Can the compiler be set to output the Assembly that would be used?

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 http://cholla.mmto.org/esp8266/xtensa.html and http://wiki.linux-xtensa.org/index.php/ABI_Interface, 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.

1 Like