mirror of
https://github.com/wled/WLED.git
synced 2026-04-20 06:04:29 +00:00
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>
73 lines
3.1 KiB
Python
73 lines
3.1 KiB
Python
# Add a section to the linker script to store our dynamic arrays
|
|
# This is implemented as a pio post-script to ensure that we can
|
|
# place our linker script at the correct point in the command arguments.
|
|
Import("env")
|
|
from pathlib import Path
|
|
|
|
# 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)
|