diff --git a/configure.ac b/configure.ac index f9138d39821e..6bae5a2b9efa 100644 --- a/configure.ac +++ b/configure.ac @@ -1128,6 +1128,11 @@ if test "$use_hardening" != "no"; then AX_CHECK_LINK_FLAG([-fPIE -pie], [PIE_FLAGS="-fPIE"; HARDENED_LDFLAGS="$HARDENED_LDFLAGS -pie"], [], [$CXXFLAG_WERROR]) fi +dnl Add build-id for perf and debug symbol matching (Linux only) +if test "$TARGET_OS" = "linux"; then + AX_CHECK_LINK_FLAG([-Wl,--build-id=sha1], [CORE_LDFLAGS="$CORE_LDFLAGS -Wl,--build-id=sha1"], [], [$LDFLAG_WERROR]) +fi + dnl These flags are specific to ld64, and may cause issues with other linkers. dnl For example: GNU ld will interpret -dead_strip as -de and then try and use dnl "ad_strip" as the symbol for the entry point. diff --git a/contrib/devtools/split-debug.sh.in b/contrib/devtools/split-debug.sh.in index 92b72b1446cf..7ad5f6ed5ad7 100644 --- a/contrib/devtools/split-debug.sh.in +++ b/contrib/devtools/split-debug.sh.in @@ -6,5 +6,5 @@ fi @OBJCOPY@ --enable-deterministic-archives -p --only-keep-debug $1 $3 @OBJCOPY@ --enable-deterministic-archives -p --strip-debug $1 $2 -@STRIP@ --enable-deterministic-archives -p -s $2 +@STRIP@ --enable-deterministic-archives -p --strip-debug --strip-unneeded $2 @OBJCOPY@ --enable-deterministic-archives -p --add-gnu-debuglink=$3 $2 diff --git a/contrib/guix/libexec/build.sh b/contrib/guix/libexec/build.sh index 5f9b62e32cce..6408234d89f9 100755 --- a/contrib/guix/libexec/build.sh +++ b/contrib/guix/libexec/build.sh @@ -341,6 +341,61 @@ mkdir -p "$DISTSRC" find "${DISTNAME}/bin" -type f -executable -print0 find "${DISTNAME}/lib" -type f -print0 } | xargs -0 -P"$JOBS" -I{} "${DISTSRC}/contrib/devtools/split-debug.sh" {} {} {}.dbg + + case "$HOST" in + *linux*) + # Compress DWARF sections in debug files and set proper permissions + find "${DISTNAME}" -name "*.dbg" -type f -print0 | xargs -0 -P"$JOBS" -I{} sh -c "objcopy --compress-debug-sections=zlib \"\$1\" \"\$1.tmp\" && mv \"\$1.tmp\" \"\$1\" && chmod 644 \"\$1\"" _ {} + + # Create .build-id tree for perf auto-discovery + mkdir -p "${DISTNAME}/usr/lib/debug/.build-id" + { + find "${DISTNAME}/bin" -type f -executable -print0 + find "${DISTNAME}/lib" -type f -print0 + } | while IFS= read -r -d '' elf; do + if file "$elf" | grep -q "ELF.*executable\|ELF.*shared object"; then + build_id=$(readelf -n "$elf" 2>/dev/null | awk '/Build ID/ {print $3; exit}') + if [ -n "$build_id" ] && [ -f "${elf}.dbg" ]; then + dir="${DISTNAME}/usr/lib/debug/.build-id/${build_id:0:2}" + mkdir -p "$dir" + cp "${elf}.dbg" "${dir}/${build_id:2}.debug" + chmod 644 "${dir}/${build_id:2}.debug" + fi + fi + done + + # Verify build-ids and debug links + verification_output=$( + { + find "${DISTNAME}/bin" -type f -executable -print0 + find "${DISTNAME}/lib" -type f -print0 + } | { + verification_failed=0 + while IFS= read -r -d '' elf; do + if file "$elf" | grep -q "ELF.*executable\|ELF.*shared object"; then + # Check for build-id + if ! readelf -n "$elf" 2>/dev/null | grep -q "Build ID"; then + echo "ERROR: No build-id found in $elf" >&2 + verification_failed=1 + fi + + # Check for .gnu_debuglink + if ! readelf --string-dump=.gnu_debuglink "$elf" >/dev/null 2>&1; then + echo "ERROR: No .gnu_debuglink found in $elf" >&2 + verification_failed=1 + fi + fi + done + exit "$verification_failed" + } 2>&1 + ) + verification_status=$? + if [ "$verification_status" -ne 0 ]; then + echo "$verification_output" >&2 + exit 1 + fi + ;; + esac ;; esac @@ -377,12 +432,14 @@ mkdir -p "$DISTSRC" || ( rm -f "${OUTDIR}/${DISTNAME}-${HOST//x86_64-w64-mingw32/win64}-debug.zip" && exit 1 ) ;; *linux*) - find "${DISTNAME}" -not -name "*.dbg" -print0 \ + # Main (non-debug) tarball: exclude separate debug files and the build-id debug tree + find "${DISTNAME}" -not -name "*.dbg" -not -path "${DISTNAME}/usr/lib/debug/*" -print0 \ | sort --zero-terminated \ | tar --create --no-recursion --mode='u+rw,go+r-w,a+X' --null --files-from=- \ | gzip -9n > "${OUTDIR}/${DISTNAME}-${HOST}.tar.gz" \ || ( rm -f "${OUTDIR}/${DISTNAME}-${HOST}.tar.gz" && exit 1 ) - find "${DISTNAME}" -name "*.dbg" -print0 \ + # Debug tarball: include .dbg files and the build-id debug tree + find "${DISTNAME}" \( -name "*.dbg" -o -path "${DISTNAME}/usr/lib/debug/*" \) -print0 \ | sort --zero-terminated \ | tar --create --no-recursion --mode='u+rw,go+r-w,a+X' --null --files-from=- \ | gzip -9n > "${OUTDIR}/${DISTNAME}-${HOST}-debug.tar.gz" \