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.
This commit is contained in:
Will Miles
2025-11-28 09:23:49 -05:00
parent 26d2cc971a
commit b97b46ae12
7 changed files with 28 additions and 4 deletions

12
pio-scripts/dynarray.py Normal file
View File

@@ -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"])

View File

@@ -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):

View File

@@ -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

9
tools/dynarray.ld Normal file
View File

@@ -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
}

2
tools/esp8266_rodata.ld Normal file
View File

@@ -0,0 +1,2 @@
/* Linker script fragment to shim ESP8266 section name to newer ESP32 standards */
REGION_ALIAS("default_rodata_seg", iram1_0_seg)

View File

@@ -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();

View File

@@ -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];