#!/usr/bin/bash # # strip.sh - Strip debugging symbols from binary files # # Copyright (c) 2007-2024 Pacman Development Team # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # [[ -n "$LIBMAKEPKG_TIDY_STRIP_SH" ]] && return LIBMAKEPKG_TIDY_STRIP_SH=1 MAKEPKG_LIBRARY=${MAKEPKG_LIBRARY:-'/usr/share/makepkg'} source "$MAKEPKG_LIBRARY/util/message.sh" source "$MAKEPKG_LIBRARY/util/option.sh" packaging_options+=('strip' 'debug') tidy_modify+=('tidy_strip') build_id() { LANG=C readelf -n "$1" | sed -n '/Build ID/ { s/.*: //p; q; }' } source_files() { # This function does two things: # # 1) rewrites source file locations for packages not respecting prefix- # map switches. This ensures all source file references in debug # info point to $dbgsrcdir. # # 2) outputs a list of files from the package source files to stdout # while stripping the $dbgsrcdir prefix LANG=C debugedit --no-recompute-build-id \ --base-dir "${srcdir}" \ --dest-dir "${dbgsrcdir}" \ --list-file /dev/stdout "$1" \ | sort -zu | tr '\0' '\n' } package_source_files() { local binary=$1 local file dest t while IFS= read -r t; do file="${srcdir}/${t}" dest="${dbgsrc}/${t}" mkdir -p "${dest%/*}" if [[ -f "$file" && ! -f "$dest" ]]; then cp -- "$file" "$dest" fi done < <(source_files "$binary") } collect_debug_symbols() { local binary=$1; shift if check_option "debug" "y"; then local bid=$(build_id "$binary") # has this file already been stripped if [[ -n "$bid" ]]; then if [[ -f "$dbgdir/.build-id/${bid:0:2}/${bid:2}.debug" ]]; then return fi elif [[ -f "$dbgdir/$binary.debug" ]]; then return fi # copy source files to debug directory package_source_files "$binary" # copy debug symbols to debug directory mkdir -p "$dbgdir/${binary%/*}" # abandon processing files that are not a recognised format if ! objcopy --only-keep-debug "$binary" "$dbgdir/$binary.debug" 2>/dev/null; then return fi local tempfile=$(mktemp "$binary.XXXXXX") objcopy --add-gnu-debuglink="$dbgdir/${binary#/}.debug" "$binary" "$tempfile" cat "$tempfile" > "$binary" rm "$tempfile" # create any needed hardlinks while IFS= read -rd '' file ; do if [[ "${binary}" -ef "${file}" && ! -f "$dbgdir/${file}.debug" ]]; then mkdir -p "$dbgdir/${file%/*}" ln "$dbgdir/${binary}.debug" "$dbgdir/${file}.debug" fi done < <(find . -type f -perm -u+w -print0 2>/dev/null) if [[ -n "$bid" ]]; then local target mkdir -p "$dbgdir/.build-id/${bid:0:2}" target="../../../../../${binary#./}" target="${target/..\/..\/usr\/lib\/}" target="${target/..\/usr\/}" ln -s "$target" "$dbgdir/.build-id/${bid:0:2}/${bid:2}" target="../../${binary#./}.debug" ln -s "$target" "$dbgdir/.build-id/${bid:0:2}/${bid:2}.debug" fi fi } strip_file(){ local binary=$1; shift local tempfile=$(mktemp "$binary.XXXXXX") if strip "$@" "$binary" -o "$tempfile"; then cat "$tempfile" > "$binary" fi rm -f "$tempfile" } strip_lto() { local binary=$1; local tempfile=$(mktemp "$binary.XXXXXX") if strip -R .gnu.lto_* -R .gnu.debuglto_* -N __gnu_lto_v1 "$binary" -o "$tempfile"; then cat "$tempfile" > "$binary" fi rm -f "$tempfile" } tidy_strip() { if check_option "strip" "y"; then msg2 "$(gettext "Stripping unneeded symbols from binaries and libraries...")" # make sure library stripping variables are defined to prevent excess stripping [[ -z ${STRIP_SHARED+x} ]] && STRIP_SHARED="-S" [[ -z ${STRIP_STATIC+x} ]] && STRIP_STATIC="-S" if check_option "debug" "y"; then dbgdir="$pkgdirbase/$pkgbase-debug/usr/lib/debug" dbgsrcdir="${DBGSRCDIR:-/usr/src/debug}/${pkgbase}" dbgsrc="$pkgdirbase/$pkgbase-debug$dbgsrcdir" mkdir -p "$dbgdir" "$dbgsrc" fi local binary strip_flags find . -type f -perm -u+w -print0 2>/dev/null | LC_ALL=C sort -z | while IFS= read -rd '' binary ; do # skip filepaths that cause stripping issues - ideally these should be temporary # guile-2.2 [[ "$binary" =~ .*/guile/.*\.go$ ]] && continue local STATICLIB=0 case "$(LC_ALL=C readelf -h "$binary" 2>/dev/null)" in *Type:*'DYN (Shared object file)'*) # Libraries (.so) or Relocatable binaries strip_flags="$STRIP_SHARED";; *Type:*'DYN (Position-Independent Executable file)'*) # Relocatable binaries strip_flags="$STRIP_SHARED";; *Type:*'EXEC (Executable file)'*) # Binaries if [[ "$(readelf -x .dynamic "$binary" 2>/dev/null)" ]]; then strip_flags="$STRIP_BINARIES" else strip_flags="$STRIP_STATIC" fi ;; *Type:*'REL (Relocatable file)'*) # Libraries (.a) or objects if ar t "$binary" &>/dev/null; then # Libraries (.a) strip_flags="$STRIP_STATIC" STATICLIB=1 elif [[ $binary = *'.ko' || $binary = *'.o' ]]; then # Kernel module or object file strip_flags="$STRIP_SHARED" else continue fi ;; *) continue ;; esac (( ! STATICLIB )) && collect_debug_symbols "$binary" strip_file "$binary" ${strip_flags} (( STATICLIB )) && strip_lto "$binary" done elif check_option "debug" "y"; then msg2 "$(gettext "Copying source files needed for debug symbols...")" dbgsrcdir="${DBGSRCDIR:-/usr/src/debug}/${pkgbase}" dbgsrc="$pkgdirbase/$pkgbase/$dbgsrcdir" mkdir -p "$dbgsrc" local binary find . -type f -perm -u+w -print0 2>/dev/null | while IFS= read -rd '' binary ; do package_source_files "$binary" 2>/dev/null done fi }