From b97b46ae12cdfc1b6c79ad827dc2fa50d96a949c Mon Sep 17 00:00:00 2001 From: Will Miles Date: Fri, 28 Nov 2025 09:23:49 -0500 Subject: [PATCH] Use new section for dynamic arrays Use a custom linker section to hold dynamic arrays such as the usermod list. This provides an extensible solution for wider use in the future (such as FX or Bus drivers), as well as IDF v5 compatibility. --- pio-scripts/dynarray.py | 12 ++++++++++++ pio-scripts/validate_modules.py | 2 +- platformio.ini | 1 + tools/dynarray.ld | 9 +++++++++ tools/esp8266_rodata.ld | 2 ++ wled00/fcn_declare.h | 2 +- wled00/um_manager.cpp | 4 ++-- 7 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 pio-scripts/dynarray.py create mode 100644 tools/dynarray.ld create mode 100644 tools/esp8266_rodata.ld diff --git a/pio-scripts/dynarray.py b/pio-scripts/dynarray.py new file mode 100644 index 000000000..c8c1e6d2c --- /dev/null +++ b/pio-scripts/dynarray.py @@ -0,0 +1,12 @@ +# Add a section to the linker script to store our dynamic arrays +# This is implemented as a pio post-script to ensure that our extra linker +# script fragments are processed last, after the base platform scripts have +# been loaded and all sections defined. +Import("env") + +if env.get("PIOPLATFORM") == "espressif8266": + # Use a shim on this platform so we can share the same output blocks + # names as used by later platforms (ESP32) + env.Append(LINKFLAGS=["-Ttools/esp8266_rodata.ld"]) + +env.Append(LINKFLAGS=["-Ttools/dynarray.ld"]) diff --git a/pio-scripts/validate_modules.py b/pio-scripts/validate_modules.py index d63b609ac..b002af40b 100644 --- a/pio-scripts/validate_modules.py +++ b/pio-scripts/validate_modules.py @@ -41,7 +41,7 @@ def check_map_file_objects(map_file: list[str], dirs: Iterable[str]) -> set[str] def count_usermod_objects(map_file: list[str]) -> int: """ Returns the number of usermod objects in the usermod list """ # Count the number of entries in the usermods table section - return len([x for x in map_file if ".dtors.tbl.usermods.1" in x]) + return len([x for x in map_file if ".dynarray.usermods.1" in x]) def validate_map_file(source, target, env): diff --git a/platformio.ini b/platformio.ini index 60dedd473..29949d33c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -135,6 +135,7 @@ extra_scripts = pre:pio-scripts/set_metadata.py post:pio-scripts/output_bins.py post:pio-scripts/strip-floats.py + post:pio-scripts/dynarray.py pre:pio-scripts/user_config_copy.py pre:pio-scripts/load_usermods.py pre:pio-scripts/build_ui.py diff --git a/tools/dynarray.ld b/tools/dynarray.ld new file mode 100644 index 000000000..2c81217d3 --- /dev/null +++ b/tools/dynarray.ld @@ -0,0 +1,9 @@ +/* Linker script fragment to add dynamic array section to binary */ +SECTIONS +{ + .dynarray : + { + . = ALIGN(4); + KEEP(*(SORT_BY_INIT_PRIORITY(.dynarray.*))) + } > default_rodata_seg +} diff --git a/tools/esp8266_rodata.ld b/tools/esp8266_rodata.ld new file mode 100644 index 000000000..0cb27df6d --- /dev/null +++ b/tools/esp8266_rodata.ld @@ -0,0 +1,2 @@ +/* Linker script fragment to shim ESP8266 section name to newer ESP32 standards */ +REGION_ALIAS("default_rodata_seg", iram1_0_seg) diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 1c0b5a7a0..e6a2112cd 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -381,7 +381,7 @@ namespace UsermodManager { }; // Register usermods by building a static list via a linker section -#define REGISTER_USERMOD(x) Usermod* const um_##x __attribute__((__section__(".dtors.tbl.usermods.1"), used)) = &x +#define REGISTER_USERMOD(x) Usermod* const um_##x __attribute__((__section__(".dynarray.usermods.1"), used)) = &x //usermod.cpp void userSetup(); diff --git a/wled00/um_manager.cpp b/wled00/um_manager.cpp index 647757ad6..2cefcdf34 100644 --- a/wled00/um_manager.cpp +++ b/wled00/um_manager.cpp @@ -10,8 +10,8 @@ // We stick them in the '.dtors' segment because it's always included by the linker scripts // even though it never gets called. Who calls exit() in an embedded program anyways? // If someone ever does, though, it'll explode as these aren't function pointers. -static Usermod * const _usermod_table_begin[0] __attribute__((__section__(".dtors.tbl.usermods.0"), unused)) = {}; -static Usermod * const _usermod_table_end[0] __attribute__((__section__(".dtors.tbl.usermods.99"), unused)) = {}; +static Usermod * const _usermod_table_begin[0] __attribute__((__section__(".dynarray.usermods.0"), unused)) = {}; +static Usermod * const _usermod_table_end[0] __attribute__((__section__(".dynarray.usermods.99"), unused)) = {}; static size_t getCount() { return &_usermod_table_end[0] - &_usermod_table_begin[0];