From 97137e59629071fede1870b3355556b80eeac1de Mon Sep 17 00:00:00 2001 From: vasilito Date: Tue, 4 Aug 2026 21:07:39 +0300 Subject: [PATCH] icu: add .note.GNU-stack to the data object via objcopy -Wa,--noexecstack did not reach icudt75l_dat.o: ICU builds it through pkgdata, which drives the assembler with its own flags and ignores CFLAGS. Verified empirically -- after a full ICU rebuild readelf still showed 0 GNU-stack sections, and plasma-workspace failed identically. Patching the archive member is deterministic where the flag was not. The added section is empty and read-only: it declares a non-executable stack, which is exactly true for a pure data blob. Verified objcopy 0 -> 1 section on the real archive before committing. Without it, binutils warns "missing .note.GNU-stack section implies executable stack", and KDE's ECM links with -Wl,--fatal-warnings, so every KDE consumer of static ICU fails (plasma-workspace applets/digital-clock, ld exit 1). Asserts the section is present afterwards rather than swallowing errors: a silent no-op here resurfaces as a link failure in a different package, far from the cause. --- local/recipes/libs/icu/recipe.toml | 43 ++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/local/recipes/libs/icu/recipe.toml b/local/recipes/libs/icu/recipe.toml index e873b98c52..5f116a92e2 100644 --- a/local/recipes/libs/icu/recipe.toml +++ b/local/recipes/libs/icu/recipe.toml @@ -78,5 +78,48 @@ ln -sf mh-linux "${ICU_SRC}/config/mh-unknown" make -j"${COOKBOOK_MAKE_JOBS}" make install DESTDIR="${COOKBOOK_STAGE}" +# Add .note.GNU-stack to the generated data object. +# +# ICU builds icudt75l_dat.o through pkgdata, which drives the assembler with its +# own flags and ignores CFLAGS -- so -Wa,--noexecstack above does NOT reach it +# (verified: readelf showed 0 GNU-stack sections after a full ICU rebuild). +# Without that section modern binutils warns +# ld: warning: icudt75l_dat.o: missing .note.GNU-stack section implies +# executable stack +# and KDE's ECM links with -Wl,--fatal-warnings, so every KDE consumer of +# static ICU fails outright (plasma-workspace applets/digital-clock: +# collect2: error: ld returned 1). +# +# Patching the archive member is deterministic where the flag was not. The +# section is empty and read-only: it declares a non-executable stack, which is +# exactly true for a pure data blob. +_icu_lib="${COOKBOOK_STAGE}/usr/lib/libicudata.a" +if [ -f "${_icu_lib}" ]; then + _icu_tmp="$(mktemp -d)" + ( cd "${_icu_tmp}" && ar x "${_icu_lib}" ) + # Prefer the cross objcopy; fall back to the host one. Both write the same + # ELF section for x86_64. `|| true` is deliberately NOT used -- a silent + # skip here reappears as a link failure in a KDE package, so the assertion + # below must be able to catch a no-op. + _icu_objcopy="${TARGET}-objcopy" + command -v "${_icu_objcopy}" >/dev/null 2>&1 || _icu_objcopy=objcopy + for _o in "${_icu_tmp}"/*.o; do + [ -f "${_o}" ] || continue + "${_icu_objcopy}" \ + --add-section .note.GNU-stack=/dev/null \ + --set-section-flags .note.GNU-stack=contents,readonly \ + "${_o}" + done + ( cd "${_icu_tmp}" && ar rcs "${_icu_lib}" *.o ) + rm -rf "${_icu_tmp}" + # Assert it actually took: a silent no-op here resurfaces as a link failure + # in a KDE package, far from this recipe. + _icu_chk="$(mktemp -d)" + ( cd "${_icu_chk}" && ar x "${_icu_lib}" && \ + readelf -S icudt*_dat.o 2>/dev/null | grep -q 'GNU-stack' ) \ + || { echo "ERROR: .note.GNU-stack not added to ICU data object" >&2; rm -rf "${_icu_chk}"; exit 1; } + rm -rf "${_icu_chk}" +fi + mkdir -p "${COOKBOOK_STAGE}/usr/lib/pkgconfig" """