Skip to content

feat(elf_loader): run .preinit_array/.init_array/.fini_array of loaded ELF - #797

Closed
franz-hoepfinger-4diac wants to merge 1 commit into
espressif:masterfrom
franz-hoepfinger-4diac:feature/elf-loader-init-array
Closed

franz-hoepfinger-4diac wants to merge 1 commit into
espressif:masterfrom
franz-hoepfinger-4diac:feature/elf-loader-init-array

Conversation

@franz-hoepfinger-4diac

@franz-hoepfinger-4diac franz-hoepfinger-4diac commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Description

C++ ELF applications loaded by elf_loader never had their static constructors executed, because .preinit_array / .init_array / .fini_array were ignored.

This PR:

  • records the three arrays after relocation (esp_elf_find_init_arrays()); in section mode (ESP32/S2/S3) it now also copies them to memory, because otherwise relocations that target them have no destination, and it handles the legacy .ctors/.dtors layout the Xtensa toolchain emits (.ctors runs backwards),
  • runs .preinit_array and .init_array once, in order, before the entry point in esp_elf_request(),
  • runs .fini_array in reverse order in esp_elf_deinit() (only if the constructors were run),
  • documents C++ ELF applications in the README (the host has to register C++ runtime symbols such as operator new/delete and __cxa_pure_virtual with esp_elf_register_symbol(); __dso_handle / __cxa_atexit must live in the ELF or be exported by the host) and adds a CHANGELOG entry.

Fixes #796

Testing

Tested on ESP32-S31 (RISC-V) and ESP32-S3 (Xtensa), ESP-IDF v6.1, with elf_console_example-style host (ELF on LittleFS, exec shell command). The test ELF has a class hierarchy with virtual functions, new/delete, a global object with constructor/destructor, and __attribute__((constructor(101))) / __attribute__((destructor)) functions. Same output on both targets:

early_ctor (init_array, priority 101)
Global Counter ctor (init_array ran)
s_counter.next() = 42 (expected 42 if constructor ran)
Shape ctor: Rect
Rect area = 12 (expected 12)
Shape ctor: Rect
Rect area = 25 (expected 25)
Shape dtor: Rect
C++ test done
Global Counter dtor
Shape dtor: Rect
Success to exit from ELF file
late_dtor (fini_array)

Not tested: ESP32 / ESP32-S2 (same code path as ESP32-S3), the dynamic shared object (dlopen) path, .preinit_array, C++ exceptions and RTTI.

Checklist

  • Tested on hardware (ESP32-S31, ESP32-S3)
  • CHANGELOG and README updated

🤖 Generated with Claude Code

@CLAassistant

CLAassistant commented Oct 9, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

…+ static objects)

Static constructors of C++ ELF applications were never executed because
.preinit_array/.init_array/.fini_array were ignored.

After relocation the loader now records these arrays, runs preinit/init
once before the entry point in esp_elf_request() and fini in
esp_elf_deinit(). The Xtensa toolchain emits the legacy .ctors/.dtors
sections, which are supported too (.ctors is run backwards). In section
mode (ESP32/S2/S3) the arrays are additionally copied to memory, since
relocations targeting them had no destination before.

Also document C++ ELF applications (host must register C++ runtime
symbols) and add a changelog entry.

Tested on ESP32-S31 (RISC-V) and ESP32-S3 (Xtensa).

Fixes espressif#796

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@franz-hoepfinger-4diac
franz-hoepfinger-4diac force-pushed the feature/elf-loader-init-array branch from e379341 to 9d559c3 Compare October 9, 2026 06:51
zhanzhaocheng pushed a commit that referenced this pull request Oct 27, 2026
Preserve Franz Höpfinger's original PR #797 commit as the preceding author-attributed commit. Rework the lifecycle section allocator and validator for ELF loader 2.0.0, retaining aligned data layout, section mapping and strict relocation bounds. Add real Xtensa/RISC-V callback tests and C++ ELF build fixtures.

Closes: #796

Copy link
Copy Markdown
Contributor

Thanks for the contribution. The C++ constructor/destructor support was brought into master in 028dcf7, then adapted for elf_loader v2.0.0 in 3a6a02e. The functionality is already included, so I'm closing this PR to avoid applying it twice.

@wujiangang wujiangang closed this Oct 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[elf_loader] C++ ELF: static constructors (.init_array) are never run and __init_array_start/_end cannot be resolved

4 participants