[ESP32] ld: region `dram0_0_seg' overflowed by 156768 bytes

Good day. I have an espressif clone that uses NodeMCU-32s board and the arduino framework. When I try to upload my code to the board I am met with the following error:

Linking .pio/build/nodemcu-32s/firmware.elf
/Users/kstanl27/.platformio/packages/toolchain-xtensa32/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/../../../../xtensa-esp32-elf/bin/ld: .pio/build/nodemcu-32s/firmware.elf section `.dram0.bss' will not fit in region `dram0_0_seg'
/Users/kstanl27/.platformio/packages/toolchain-xtensa32/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/../../../../xtensa-esp32-elf/bin/ld: DRAM segment data does not fit.
/Users/kstanl27/.platformio/packages/toolchain-xtensa32/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/../../../../xtensa-esp32-elf/bin/ld: region `dram0_0_seg' overflowed by 156768 bytes

After searching through other topics on this forum, and other places across the search engines, I’ve seen where it’s an issue with the number of uninitialized static variables exceeding the size limit of the .bss memory space and to look in the corresponding .map file to see which variables are the offenders.

The problem I’m running into, and have had no luck finding how to solve, is that there does not seem to be a .map file generated by platformio, or if it is, it’s being auto-removed before I can look at it.

I am using CLion 2020.3 and the PlatformIO plugin 203.5981.106 from Jetbrains. This is my first C++ project, and my first time using PlatformIO; in other words, my apologies if I missed something otherwise simple or obvious. And if you need any other information from me, please let me know.

Thank you in advance for any assistance. I’m banging my head on the keyboard over here.

Edit: source code

1 Like

If linking fails, there will be no .map and no .elf. The .map will also not be there unless you explicitly use build_flags to add it (Generate a .map file - #15 by krishna_chaitanya).

But there’s still some tricks to be used. We can ‘simply’ change the linker file and expand the size of the region dram0_0_seg so that it forcefully builds, and then take a look at what’s hogging up memory so badly.

After doing a verbose build, we see the final linker command (is huge)…

xtensa-esp32-elf-g++ -o .pio\build\nodemcu-32s\firmware.elf -T esp32_out.ld -nostdlib -Wl,-static -u call_user_start_cpu0 -Wl,–undefined=uxTopUsedPriority -Wl,–gc-sections -Wl,-EL -T esp32.common.ld -T esp32.rom.ld -T esp32.peripherals.ld -T esp32.rom.libgcc.ld -T esp32.rom.spiram_incompatible_fns.ld -u ld_include_panic_highint_hdl -u __cxa_guard_dummy -u __cxx_fatal_exception .pio\build\nodemcu-32s\src\dbuddy.cpp.o .pio\build\nodemcu-32s\src\fonts\roboto_black_16.c.o .pio\build\nodemcu-32s\src\fonts\roboto_black_24.c.o .pio\build\nodemcu-32s\src\fonts\roboto_black_72.c.o .pio\build\nodemcu-32s\src\fonts\roboto_regular_12.c.o .pio\build\nodemcu-32s\src\fonts\roboto_regular_14.c.o .pio\build\nodemcu-32s\src\fonts\roboto_regular_16.c.o .pio\build\nodemcu-32s\src\fonts\roboto_regular_18.c.o .pio\build\nodemcu-32s\src\main.cpp.o .pio\build\nodemcu-32s\src\ui.cpp.o -L.pio\build\nodemcu-32s -LC:\Users\Max.platformio\packages\framework-arduinoespressif32\tools\sdk\lib -LC:\Users\Max.platformio\packages\framework-arduinoespressif32\tools\sdk\ld -Wl,–start-group .pio\build\nodemcu-32s\lib43a\libEEPROM.a .pio\build\nodemcu-32s\lib274\libSPI.a .pio\build\nodemcu-32s\lib816\libWire.a “.pio\build\nodemcu-32s\lib2c5\libAdafruit BusIO.a” “.pio\build\nodemcu-32s\lib9cb\libAdafruit GFX Library.a” “.pio\build\nodemcu-32s\lib85a\libAdafruit RA8875.a” .pio\build\nodemcu-32s\libaa1\libBLE.a .pio\build\nodemcu-32s\lib3f9\liblvgl.a .pio\build\nodemcu-32s\libFrameworkArduinoVariant.a .pio\build\nodemcu-32s\libFrameworkArduino.a -lgcc -lesp32 -lphy -lesp_http_client -lmbedtls -lrtc -lesp_http_server -lbtdm_app -lspiffs -lbootloader_support -lmdns -lnvs_flash -lfatfs -lpp -lnet80211 -ljsmn -lface_detection -llibsodium -lvfs -ldl_lib -llog -lfreertos -lcxx -lsmartconfig_ack -lxtensa-debug-module -lheap -ltcpip_adapter -lmqtt -lulp -lfd -lfb_gfx -lnghttp -lprotocomm -lsmartconfig -lm -lethernet -limage_util -lc_nano -lsoc -ltcp_transport -lc -lmicro-ecc -lface_recognition -ljson -lwpa_supplicant -lmesh -lesp_https_ota -lwpa2 -lexpat -llwip -lwear_levelling -lapp_update -ldriver -lbt -lespnow -lcoap -lasio -lnewlib -lconsole -lapp_trace -lesp32-camera -lhal -lprotobuf-c -lsdmmc -lcore -lpthread -lcoexist -lfreemodbus -lspi_flash -lesp-tls -lwpa -lwifi_provisioning -lwps -lesp_adc_cal -lesp_event -lopenssl -lesp_ringbuf -lfr -lstdc++ -Wl,–end-group

But we know it’s linked against esp32_out.ld. That is in the framework. So we open up the file C:\Users\<user>\.platformio\packages\framework-arduinoespressif32\tools\sdk\ld\esp32_out.ld and see…

that is ~124K available. See comment above on why that is so low.

Anyways, we change the len of that to something we know that fits (old size plus overflowed-by plus a little extra)

  dram0_0_seg (RW) : org = 0x3FFB0000 + 0xdb5c,
                                     len = 289888

And we relink the binary by pressing “Build” again…

Linking .pio\build\nodemcu-32s\firmware.elf
Retrieving maximum program size .pio\build\nodemcu-32s\firmware.elf
Building .pio\build\nodemcu-32s\firmware.bin
Checking size .pio\build\nodemcu-32s\firmware.elf
Advanced Memory Usage is available via "PlatformIO Home > Project Inspect"
RAM:   [========= ]  85.9% (used 281332 bytes from 327680 bytes)
Flash: [=====     ]  49.2% (used 644605 bytes from 1310720 bytes)
esptool.py v3.0
======================= [SUCCESS] Took 8.44 seconds =======================

and that was “ok” and we have the ELF now.

Next for analysis, I’ll be cloning GitHub - ARMmbed/mbed-os-linker-report: Post-processing of linker output to calculate and visualize memory usage for elf-sections, but that change

to the absolute path where my xtensa-esp32-elf-nm is (C:\Users\Max\.platformio\packages\toolchain-xtensa32\bin\xtensa-esp32-elf-nm). The thing was originally written for ARM. So I’ll just let it run on the .elf file…

C:\Users\Max\mbed-os-linker-report>elfsize.py -i C:\Users\Max\Documents\PlatformIO\Projects\dbuddy\.pio\build\nodemcu-32s\firmware.elf -b
C:\Users\Max\.platformio\packages\toolchain-xtensa32\bin\xtensa-esp32-elf-nm -l -S -C -f sysv C:\Users\Max\Documents\PlatformIO\Projects\dbuddy\.pio\build\nodemcu-32s\firmware.elf
[INFO] data written to C:\Users\Max\mbed-os-linker-report\html\data-flare.js
[INFO] opening in browser file://C:\Users\Max\mbed-os-linker-report\index.html

and we get nice graphs that show how memory is being used in flash (code, constant variables, …) and ram (bss, global initialized but modifyable variables, etc.)

Clicking on the .dram0.bss section shows that mainly a single symbol is eating all memory…

A symbol called work_mem_int. Now where does that come from?

C:\Users\Max\Documents\PlatformIO\Projects\dbuddy>grep -r "work_mem_int" .
..
./.pio/libdeps/nodemcu-32s/lvgl/src/lv_misc/lv_mem.c:    static LV_MEM_ATTR MEM_UNIT work_mem_int[LV_MEM_SIZE / sizeof(MEM_UNIT)];
./.pio/libdeps/nodemcu-32s/lvgl/src/lv_misc/lv_mem.c:    work_mem = (uint8_t *)work_mem_int;

aha. The LVGL library is allocating a unified working buffer to do everything.

static LV_MEM_ATTR MEM_UNIT work_mem_int[LV_MEM_SIZE / sizeof(MEM_UNIT)];

and that thing is 256 kilobytes in its current config!! Much too large than what fits in the bss section for the ESP32 when using the Arduino-ESP32 linker script (and they probably put a lot of thought into that).

Now let’s modify what you have written in your config file…

to set that to (64*1024) and also revert the linker file hacks in esp32_out.ld and build again…

and it builds. Can LVGL work with that buffer space? No idea. But now you know why it doesn’t build.

Hint: The comment in the linker file section talks about how more memory may be available in the heap. So if static allocation in RAM doesn’t work, try LV_MEM_CUSTOM and do a heap alloc.

Even better, on a board with PSRAM, you can have a large buffer size allocated in PSRAM, if memory really is not enough in either stack or heap in the internal memory.

4 Likes

Wow. That was so much more than I could have ever expected. Thank you! There is so much to unpack from this that I will do my best to absorb as much from this as I can.

1 Like

Let me know if you have questions.

On another note, the LVGL project does have a nice PlatformIO demo project with a config file. And in there.

They do 32K. Even less than my 64K guess.

That project also takes advantage of PlatformIO’s native platform so it can build a desktop application (linked against SDL2), alongside the ESP32 and STM32F429ZI discovery board firmware.

There’s also separate from that GitHub - lvgl/lv_port_esp32: LVGL ported to ESP32 including various display and touchpad drivers which uses ESP-IDF instead of Arduino as the framework. Per README it’s also buildable with PlatformIO and the native ESP-IDF build system / tools.

Additional details:

Mine with esp32-s3-devkitc-1 the .ld file for modification of len is:

d:\PlatformIO\packages\framework-arduinoespressif32\tools\sdk\esp32s3\ld\memory.ld

Also the PlatformIO disk driver and the project driver should be the same (mine d:\) for the Python script mbed-os-linker-report works.