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.
This commit is contained in:
2026-08-04 21:07:39 +03:00
parent 908987494d
commit 97137e5962
+43
View File
@@ -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"
"""