pacman/scripts/libmakepkg/tidy/strip.sh.in
Allan McRae 5f4c7e3ddc libmakepkg: strip - disguard error messages when copying source files
Parallel processing of file stripping is causing a TOC/TOU race when copying
source files into the debug location resulting in error messages from cp.
While hiding this error is not the ideal solution, it is currently the only
one we have. Given this is a error of our own making, and we understand the
cause and have determined there is zero actual downside to ignoring the
error, we will accept this approach until something better is found.

Signed-off-by: Allan McRae <allan@archlinux.org>
2024-12-28 13:54:27 +00:00

246 lines
6.9 KiB
Bash

#!/bin/bash
#
# strip.sh - Strip debugging symbols from binary files
#
# Copyright (c) 2007-2024 Pacman Development Team <pacman-dev@lists.archlinux.org>
#
# 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 <http://www.gnu.org/licenses/>.
#
[[ -n "$LIBMAKEPKG_TIDY_STRIP_SH" ]] && return
LIBMAKEPKG_TIDY_STRIP_SH=1
MAKEPKG_LIBRARY=${MAKEPKG_LIBRARY:-'@libmakepkgdir@'}
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" 2>/dev/null
fi
done < <(source_files "$binary")
}
safe_objcopy() {
local binary=$1; shift
local tempfile=$(mktemp "$binary.XXXXXX")
objcopy "$@" "$binary" "$tempfile"
cat "$tempfile" > "$binary"
rm "$tempfile"
}
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
safe_objcopy "$binary" --remove-section=.gnu_debuglink
safe_objcopy "$binary" --add-gnu-debuglink="$dbgdir/${binary#/}.debug"
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
}
safe_strip_file(){
local binary=$1; shift
local tempfile=$(mktemp "$binary.XXXXXX")
if strip "$@" "$binary" -o "$tempfile"; then
cat "$tempfile" > "$binary"
fi
rm -f "$tempfile"
}
safe_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"
}
process_file_stripping() {
local binary="$1"
local strip_flags
# skip filepaths that cause stripping issues - ideally these should be temporary
# guile-2.2
[[ "$binary" =~ .*/guile/.*\.go$ ]] && return
local STATICOBJ=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 [[ $binary = *'.o' ]] || ar t "$binary" &>/dev/null; then
strip_flags="$STRIP_STATIC"
STATICOBJ=1
elif [[ $binary = *'.ko' ]]; then # Kernel modules
strip_flags="$STRIP_SHARED"
else
return
fi
;;
*)
return ;;
esac
(( ! STATICOBJ )) && collect_debug_symbols "$binary"
safe_strip_file "$binary" ${strip_flags}
(( STATICOBJ )) && safe_strip_lto "$binary"
}
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-@DEBUGSUFFIX@/usr/lib/debug"
dbgsrcdir="${DBGSRCDIR:-/usr/src/debug}/${pkgbase}"
dbgsrc="$pkgdirbase/$pkgbase-@DEBUGSUFFIX@$dbgsrcdir"
mkdir -p "$dbgdir" "$dbgsrc"
fi
_parallel_stripper() {
# Inherit traps in subshell to perform cleanup after an interrupt
set -E
(
local jobs binary
while IFS= read -rd '' binary ; do
# Be sure to keep the number of concurrently running processes less
# than limit value to prevent an accidental fork bomb.
jobs=($(jobs -p))
(( ${#jobs[@]} >= $NPROC )) && wait -n "${jobs[@]}"
process_file_stripping "$binary" &
done < <(find . -type f -perm -u+w -links 1 -print0 2>/dev/null)
# Wait for all jobs to complete
wait
)
set +E
}
_parallel_stripper
# hardlinks only need processed once, but need additional links in debug packages
declare -A hardlinks
while IFS= read -rd '' binary ; do
if check_option "debug" "y"; then
local inode="$(@INODECMD@ -- "$binary")"
inode=${inode%% *}
if [[ -z "${hardlinks[$inode]}" ]]; then
hardlinks[$inode]="$binary"
else
if [[ -f "$dbgdir/${hardlinks[$inode]}.debug" ]]; then
mkdir -p "$dbgdir/${binary%/*}"
ln "$dbgdir/${hardlinks[$inode]}.debug" "$dbgdir/${binary}.debug"
continue
fi
fi
fi
process_file_stripping "$binary"
done < <(find . -type f -perm -u+w -links +1 -print0 2>/dev/null | LC_ALL=C sort -z)
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
}