mirror of
https://github.com/wled/WLED.git
synced 2026-04-25 16:42:44 +00:00
dynarray: Directly apply linker fix
Rather than append a linker file, we edit the upstream supplied ones to add our section to the binaries. Works better on all platforms. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -4,16 +4,69 @@
|
||||
Import("env")
|
||||
from pathlib import Path
|
||||
|
||||
platform = env.get("PIOPLATFORM")
|
||||
script_file = Path(f"tools/dynarray_{platform}.ld")
|
||||
if script_file.is_file():
|
||||
linker_script = f"-T{script_file}"
|
||||
if platform == "espressif32":
|
||||
# For ESP32, the script must be added at the right point in the list
|
||||
linkflags = env.get("LINKFLAGS", [])
|
||||
idx = linkflags.index("memory.ld")
|
||||
linkflags.insert(idx+1, linker_script)
|
||||
env.Replace(LINKFLAGS=linkflags)
|
||||
else:
|
||||
# For other platforms, put it in last
|
||||
env.Append(LINKFLAGS=[linker_script])
|
||||
# Linker script fragment injected into the rodata output section of whichever
|
||||
# platform we're building for. Placed just before the end-of-rodata marker so
|
||||
# that the dynarray entries land in flash rodata and are correctly sorted.
|
||||
DYNARRAY_INJECTION = (
|
||||
"\n /* dynarray: WLED dynamic module arrays */\n"
|
||||
" . = ALIGN(0x10);\n"
|
||||
" KEEP(*(SORT_BY_INIT_PRIORITY(.dynarray.*)))\n"
|
||||
" "
|
||||
)
|
||||
|
||||
|
||||
def inject_before_marker(path, marker):
|
||||
"""Patch a linker script file in-place, inserting DYNARRAY_INJECTION before marker."""
|
||||
original = path.read_text()
|
||||
path.write_text(original.replace(marker, DYNARRAY_INJECTION + marker, 1))
|
||||
|
||||
|
||||
if env.get("PIOPLATFORM") == "espressif32":
|
||||
# Find sections.ld on the linker search path (LIBPATH).
|
||||
sections_ld_path = None
|
||||
for ld_dir in env.get("LIBPATH", []):
|
||||
candidate = Path(str(ld_dir)) / "sections.ld"
|
||||
if candidate.exists():
|
||||
sections_ld_path = candidate
|
||||
break
|
||||
|
||||
if sections_ld_path is not None:
|
||||
# Inject inside the existing .flash.rodata output section, just before
|
||||
# _rodata_end. IDF v5 enforces zero gaps between adjacent output
|
||||
# sections via ASSERT statements, so INSERT AFTER .flash.rodata would
|
||||
# fail. Injecting inside the section creates no new output section and
|
||||
# leaves the ASSERTs satisfied.
|
||||
build_dir = Path(env.subst("$BUILD_DIR"))
|
||||
patched_path = build_dir / "dynarray_sections.ld"
|
||||
import shutil
|
||||
shutil.copy(sections_ld_path, patched_path)
|
||||
inject_before_marker(patched_path, "_rodata_end = ABSOLUTE(.);")
|
||||
|
||||
# Replace "sections.ld" in LINKFLAGS with an absolute path to our
|
||||
# patched copy. The flag may appear as a bare token, combined as
|
||||
# "-Tsections.ld", or split across two tokens ("-T", "sections.ld").
|
||||
patched_str = str(patched_path)
|
||||
new_flags = []
|
||||
skip_next = False
|
||||
for flag in env.get("LINKFLAGS", []):
|
||||
if skip_next:
|
||||
new_flags.append(patched_str if flag == "sections.ld" else flag)
|
||||
skip_next = False
|
||||
elif flag == "-T":
|
||||
new_flags.append(flag)
|
||||
skip_next = True
|
||||
else:
|
||||
new_flags.append(flag.replace("sections.ld", patched_str))
|
||||
env.Replace(LINKFLAGS=new_flags)
|
||||
|
||||
elif env.get("PIOPLATFORM") == "espressif8266":
|
||||
# The ESP8266 framework preprocesses eagle.app.v6.common.ld.h into
|
||||
# local.eagle.app.v6.common.ld in $BUILD_DIR/ld/ at build time. Register
|
||||
# a post-action on that generated file so the injection happens after
|
||||
# C-preprocessing but before linking.
|
||||
build_ld = Path(env.subst("$BUILD_DIR")) / "ld" / "local.eagle.app.v6.common.ld"
|
||||
|
||||
def patch_esp8266_ld(target, source, env):
|
||||
inject_before_marker(build_ld, "_irom0_text_end = ABSOLUTE(.);")
|
||||
|
||||
env.AddPostAction(str(build_ld), patch_esp8266_ld)
|
||||
|
||||
@@ -80,8 +80,7 @@ def check_elf_modules(elf_path: Path, env, module_lib_builders) -> set[str]:
|
||||
return found
|
||||
|
||||
|
||||
DYNARRAY_SECTION = ".dtors" if env.get("PIOPLATFORM") == "espressif8266" else ".dynarray"
|
||||
USERMODS_SECTION = f"{DYNARRAY_SECTION}.usermods.1"
|
||||
USERMODS_SECTION = f".dynarray.usermods.1"
|
||||
|
||||
def count_usermod_objects(map_file: list[str]) -> int:
|
||||
""" Returns the number of usermod objects in the usermod list """
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
/* ESP32 linker script fragment to add dynamic array section to binary */
|
||||
SECTIONS
|
||||
{
|
||||
.dynarray :
|
||||
{
|
||||
. = ALIGN(0x10);
|
||||
KEEP(*(SORT_BY_INIT_PRIORITY(.dynarray.*)))
|
||||
} > default_rodata_seg
|
||||
}
|
||||
INSERT AFTER .flash.rodata;
|
||||
@@ -20,15 +20,4 @@ Macros for generating a "dynamic array", a static array of objects declared in d
|
||||
#define DYNARRAY_END(array_name) array_name##_end
|
||||
#define DYNARRAY_LENGTH(array_name) (&DYNARRAY_END(array_name)[0] - &DYNARRAY_BEGIN(array_name)[0])
|
||||
|
||||
#ifdef ESP8266
|
||||
// ESP8266 linker script cannot be extended with a unique section for dynamic arrays.
|
||||
// We instead pack them in the ".dtors" section, as it's sorted and uploaded to the flash
|
||||
// (but will never be used in the embedded system)
|
||||
#define DYNARRAY_SECTION ".dtors"
|
||||
|
||||
#else /* ESP8266 */
|
||||
|
||||
// Use a unique named section; the linker script must be extended to ensure it's correctly placed.
|
||||
#define DYNARRAY_SECTION ".dynarray"
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user