blob: 706802c50417f4fe80eb3dfca610e2a7cd953b0c [file] [log] [blame]
#!/bin/bash
# Copyright 1999-2019 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
source "${PORTAGE_BIN_PATH}"/helper-functions.sh || exit 1
# avoid multiple calls to `has`. this creates things like:
# FEATURES_foo=false
# if "foo" is not in $FEATURES
tf() { "$@" && echo true || echo false ; }
exp_tf() {
local flag var=$1
shift
for flag in "$@" ; do
eval ${var}_${flag}=$(tf has ${flag} ${!var})
done
}
exp_tf FEATURES compressdebug installsources nostrip splitdebug xattr
exp_tf RESTRICT binchecks installsources splitdebug strip
if ! ___eapi_has_prefix_variables; then
EPREFIX= ED=${D}
fi
banner=false
SKIP_STRIP=false
if ${RESTRICT_strip} || ${FEATURES_nostrip} ; then
SKIP_STRIP=true
banner=true
${FEATURES_installsources} || exit 0
fi
[[ ${__PORTAGE_HELPER} == prepstrip ]] && prepstrip=true || prepstrip=false
if ! ${prepstrip}; then
while [[ $# -gt 0 ]] ; do
case $1 in
--ignore)
shift
skip_dirs=()
for skip; do
if [[ -d ${ED%/}/${skip#/} ]]; then
skip_dirs+=( "${ED%/}/${skip#/}" )
else
rm -f "${ED%/}/${skip#/}.estrip" || die
fi
done
if [[ ${skip_dirs[@]} ]]; then
find "${skip_dirs[@]}" -name '*.estrip' -delete || die
fi
exit 0
;;
--queue)
shift
find_paths=()
for path; do
if [[ -e ${ED%/}/${path#/} ]]; then
find_paths+=( "${ED%/}/${path#/}" )
fi
done
if [[ ${find_paths[@]} ]]; then
while IFS= read -r path; do
>> "${path}.estrip" || die
done < <(
scanelf -yqRBF '#k%F' -k '.symtab' "${find_paths[@]}"
find "${find_paths[@]}" -type f ! -type l -name '*.a'
)
fi
exit 0
;;
--dequeue)
[[ -n ${2} ]] && die "${0##*/}: --dequeue takes no additional arguments"
break
;;
*)
die "${0##*/}: unknown arguments '$*'"
exit 1
;;
esac
shift
done
set -- "${ED}"
fi
PRESERVE_XATTR=false
if [[ ${KERNEL} == linux ]] && ${FEATURES_xattr} ; then
PRESERVE_XATTR=true
if type -P getfattr >/dev/null && type -P setfattr >/dev/null ; then
dump_xattrs() {
getfattr -d -m - --absolute-names "$1"
}
restore_xattrs() {
setfattr --restore=-
}
else
dump_xattrs() {
PYTHONPATH=${PORTAGE_PYTHONPATH:-${PORTAGE_PYM_PATH}} \
"${PORTAGE_PYTHON:-/usr/bin/python}" \
"${PORTAGE_BIN_PATH}/xattr-helper.py" --dump < <(echo -n "$1")
}
restore_xattrs() {
PYTHONPATH=${PORTAGE_PYTHONPATH:-${PORTAGE_PYM_PATH}} \
"${PORTAGE_PYTHON:-/usr/bin/python}" \
"${PORTAGE_BIN_PATH}/xattr-helper.py" --restore
}
fi
fi
# look up the tools we might be using
for t in STRIP:strip OBJCOPY:objcopy READELF:readelf RANLIB:ranlib ; do
v=${t%:*} # STRIP
t=${t#*:} # strip
eval ${v}=\"${!v:-${CHOST}-${t}}\"
type -P -- ${!v} >/dev/null || eval ${v}=${t}
done
# Figure out what tool set we're using to strip stuff
unset SAFE_STRIP_FLAGS DEF_STRIP_FLAGS SPLIT_STRIP_FLAGS
case $(${STRIP} --version 2>/dev/null) in
*elfutils*) # dev-libs/elfutils
# elfutils default behavior is always safe, so don't need to specify
# any flags at all
SAFE_STRIP_FLAGS=""
DEF_STRIP_FLAGS="--remove-comment"
SPLIT_STRIP_FLAGS="-f"
;;
*GNU*) # sys-devel/binutils
# We'll leave out -R .note for now until we can check out the relevance
# of the section when it has the ALLOC flag set on it ...
SAFE_STRIP_FLAGS="--strip-unneeded -N __gentoo_check_ldflags__"
DEF_STRIP_FLAGS="-R .comment -R .GCC.command.line -R .note.gnu.gold-version"
SPLIT_STRIP_FLAGS=
;;
esac
: ${PORTAGE_STRIP_FLAGS=${SAFE_STRIP_FLAGS} ${DEF_STRIP_FLAGS}}
prepstrip_sources_dir=${EPREFIX}/usr/src/debug/${CATEGORY}/${PF}
debugedit=$(type -P debugedit)
if [[ -z ${debugedit} ]]; then
debugedit_paths=(
"${EPREFIX}/usr/libexec/rpm/debugedit"
)
for x in "${debugedit_paths[@]}"; do
if [[ -x ${x} ]]; then
debugedit=${x}
break
fi
done
fi
[[ ${debugedit} ]] && debugedit_found=true || debugedit_found=false
debugedit_warned=false
__multijob_init
# Setup $T filesystem layout that we care about.
tmpdir="${T}/prepstrip"
rm -rf "${tmpdir}"
mkdir -p "${tmpdir}"/{inodes,splitdebug,sources}
# Usage: save_elf_sources <elf>
save_elf_sources() {
${FEATURES_installsources} || return 0
${RESTRICT_installsources} && return 0
if ! ${debugedit_found} ; then
if ! ${debugedit_warned} ; then
debugedit_warned=true
ewarn "FEATURES=installsources is enabled but the debugedit binary could not be"
ewarn "found. This feature will not work unless debugedit or rpm is installed!"
fi
return 0
fi
local x=$1
# since we're editing the ELF here, we should recompute the build-id
# (the -i flag below). save that output so we don't need to recompute
# it later on in the save_elf_debug step.
buildid=$("${debugedit}" -i \
-b "${WORKDIR}" \
-d "${prepstrip_sources_dir}" \
-l "${tmpdir}/sources/${x##*/}.${BASHPID:-$(__bashpid)}" \
"${x}")
}
# Usage: save_elf_debug <elf> [splitdebug file]
save_elf_debug() {
${FEATURES_splitdebug} || return 0
${RESTRICT_splitdebug} && return 0
# NOTE: Debug files must be installed in
# ${EPREFIX}/usr/lib/debug/${EPREFIX} (note that ${EPREFIX} occurs
# twice in this path) in order for gdb's debug-file-directory
# lookup to work correctly.
local x=$1
local inode_debug=$2
local splitdebug=$3
local d_noslash=${D%/}
local y=${ED%/}/usr/lib/debug/${x:${#d_noslash}}.debug
# dont save debug info twice
[[ ${x} == *".debug" ]] && return 0
mkdir -p "${y%/*}"
if [ -f "${inode_debug}" ] ; then
ln "${inode_debug}" "${y}" || die "ln failed unexpectedly"
else
if [[ -n ${splitdebug} ]] ; then
mv "${splitdebug}" "${y}"
else
# Copy full binary as the split-debug binary.
# This is to avoid symbolization issues with Perf/CWP (b/127337806).
cp "${x}" "${y}"
${OBJCOPY} --add-gnu-debuglink="${y}" "${x}"
fi
# Only do the following if the debug file was
# successfully created (see bug #446774).
if [ $? -eq 0 ] ; then
local args="a-x,o-w"
[[ -g ${x} || -u ${x} ]] && args+=",go-r"
chmod ${args} "${y}"
ln "${y}" "${inode_debug}" || die "ln failed unexpectedly"
fi
fi
# if we don't already have build-id from debugedit, look it up
if [[ -z ${buildid} ]] ; then
# convert the readelf output to something useful
buildid=$(${READELF} -n "${x}" 2>/dev/null | awk '/Build ID:/{ print $NF; exit }')
fi
if [[ -n ${buildid} ]] ; then
local buildid_dir="${ED%/}/usr/lib/debug/.build-id/${buildid:0:2}"
local buildid_file="${buildid_dir}/${buildid:2}"
mkdir -p "${buildid_dir}"
[ -L "${buildid_file}".debug ] || ln -s "../../${x:$((${#d_noslash} + 1))}.debug" "${buildid_file}.debug"
[ -L "${buildid_file}" ] || ln -s "/${x:$((${#d_noslash} + 1))}" "${buildid_file}"
fi
}
# Usage: process_elf <elf>
process_elf() {
local x=$1 inode_link=$2 strip_flags=${*:3}
local ed_noslash=${ED%/}
local already_stripped lockfile xt_data
__vecho " ${x:${#ed_noslash}}"
# If two processes try to debugedit or strip the same hardlink at the
# same time, it may corrupt files or cause loss of splitdebug info.
# So, use a lockfile to prevent interference (easily observed with
# dev-vcs/git which creates ~111 hardlinks to one file in
# /usr/libexec/git-core).
lockfile=${inode_link}_lockfile
if ! ln "${inode_link}" "${lockfile}" 2>/dev/null ; then
while [[ -f ${lockfile} ]] ; do
sleep 1
done
unset lockfile
fi
[ -f "${inode_link}_stripped" ] && already_stripped=true || already_stripped=false
if ! ${already_stripped} ; then
if ${PRESERVE_XATTR} ; then
xt_data=$(dump_xattrs "${x}")
fi
save_elf_sources "${x}"
fi
if ${strip_this} ; then
# see if we can split & strip at the same time
if [[ -n ${SPLIT_STRIP_FLAGS} ]] ; then
local shortname="${x##*/}.debug"
local splitdebug="${tmpdir}/splitdebug/${shortname}.${BASHPID:-$(__bashpid)}"
${already_stripped} || \
${STRIP} ${strip_flags} \
-f "${splitdebug}" \
-F "${shortname}" \
"${x}"
save_elf_debug "${x}" "${inode_link}_debug" "${splitdebug}"
else
save_elf_debug "${x}" "${inode_link}_debug"
${already_stripped} || \
${STRIP} ${strip_flags} "${x}"
fi
fi
if ${already_stripped} ; then
rm -f "${x}" || die "rm failed unexpectedly"
ln "${inode_link}_stripped" "${x}" || die "ln failed unexpectedly"
else
ln "${x}" "${inode_link}_stripped" || die "ln failed unexpectedly"
if [[ ${xt_data} ]] ; then
restore_xattrs <<< "${xt_data}"
fi
fi
[[ -n ${lockfile} ]] && rm -f "${lockfile}"
}
# Usage: process_ar <ar archive>
process_ar() {
local x=$1
local ed_noslash=${ED%/}
__vecho " ${x:${#ed_noslash}}"
if ${strip_this} ; then
# If we have split debug enabled, then do not strip this.
# There is no concept of splitdebug for objects not yet
# linked in (only for finally linked ELFs), so we have to
# retain the debug info in the archive itself.
if ! ${FEATURES_splitdebug} || ${RESTRICT_splitdebug} ; then
${STRIP} -g "${x}" && ${RANLIB} "${x}"
fi
fi
}
# The existance of the section .symtab tells us that a binary is stripped.
# We want to log already stripped binaries, as this may be a QA violation.
# They prevent us from getting the splitdebug data.
if ! ${RESTRICT_binchecks} ; then
# We need to do the non-stripped scan serially first before we turn around
# and start stripping the files ourselves. The log parsing can be done in
# parallel though.
log=${tmpdir}/scanelf-already-stripped.log
scanelf -yqRBF '#k%F' -k '!.symtab' "$@" | sed -e "s#^${ED%/}/##" > "${log}"
(
__multijob_child_init
qa_var="QA_PRESTRIPPED_${ARCH/-/_}"
[[ -n ${!qa_var} ]] && QA_PRESTRIPPED="${!qa_var}"
if [[ -n ${QA_PRESTRIPPED} && -s ${log} && \
${QA_STRICT_PRESTRIPPED-unset} = unset ]] ; then
shopts=$-
set -o noglob
for x in ${QA_PRESTRIPPED} ; do
sed -e "s#^${x#/}\$##" -i "${log}"
done
set +o noglob
set -${shopts}
fi
sed -e "/^\$/d" -e "s#^#/#" -i "${log}"
if [[ -s ${log} ]] ; then
__vecho -e "\n"
eqawarn "QA Notice: Pre-stripped files found:"
eqawarn "$(<"${log}")"
else
rm -f "${log}"
fi
) &
__multijob_post_fork
fi
# Since strip creates a new inode, we need to know the initial set of
# inodes in advance, so that we can avoid interference due to trying
# to strip the same (hardlinked) file multiple times in parallel.
# See bug #421099.
if [[ ${USERLAND} == BSD ]] ; then
get_inode_number() { stat -f '%i' "$1"; }
else
get_inode_number() { stat -c '%i' "$1"; }
fi
cd "${tmpdir}/inodes" || die "cd failed unexpectedly"
if ${prepstrip}; then
while read -r x ; do
inode_link=$(get_inode_number "${x}") || die "stat failed unexpectedly"
echo "${x}" >> "${inode_link}" || die "echo failed unexpectedly"
done < <(
# Use sort -u to eliminate duplicates for bug #445336.
(
scanelf -yqRBF '#k%F' -k '.symtab' "$@"
find "$@" -type f ! -type l -name '*.a'
) | LC_ALL=C sort -u
)
else
while IFS= read -d '' -r x ; do
inode_link=$(get_inode_number "${x%.estrip}") || die "stat failed unexpectedly"
echo "${x%.estrip}" >> "${inode_link}" || die "echo failed unexpectedly"
done < <(find "${ED}" -name '*.estrip' -delete -print0)
fi
# Now we look for unstripped binaries.
for inode_link in $(shopt -s nullglob; echo *) ; do
while read -r x
do
if ! ${banner} ; then
__vecho "strip: ${STRIP} ${PORTAGE_STRIP_FLAGS}"
banner=true
fi
(
__multijob_child_init
f=$(file "${x}") || exit 0
[[ -z ${f} ]] && exit 0
if ${SKIP_STRIP} ; then
strip_this=false
elif ! ${prepstrip}; then
strip_this=true
else
# The noglob funk is to support STRIP_MASK="/*/booga" and to keep
# the for loop from expanding the globs.
# The eval echo is to support STRIP_MASK="/*/{booga,bar}" sex.
set -o noglob
strip_this=true
for m in $(eval echo ${STRIP_MASK}) ; do
[[ ${x#${ED%/}} == ${m} ]] && strip_this=false && break
done
set +o noglob
fi
# In Prefix we are usually an unprivileged user, so we can't strip
# unwritable objects. Make them temporarily writable for the
# stripping.
was_not_writable=false
if [[ ! -w ${x} ]] ; then
was_not_writable=true
chmod u+w "${x}"
fi
# only split debug info for final linked objects
# or kernel modules as debuginfo for intermediatary
# files (think crt*.o from gcc/glibc) is useless and
# actually causes problems. install sources for all
# elf types though cause that stuff is good.
buildid=
if [[ ${f} == *"current ar archive"* ]] ; then
process_ar "${x}"
elif [[ ${f} == *"SB executable"* || ${f} == *"SB pie executable"* ||
${f} == *"SB shared object"* ]] ; then
process_elf "${x}" "${inode_link}" ${PORTAGE_STRIP_FLAGS}
elif [[ ${f} == *"SB relocatable"* ]] ; then
# Disable splitdebug for object files as breakpad fails atm w/them.
# http://crosbug.com/204974
FEATURES_splitdebug_save=${FEATURES_splitdebug}
[[ ${x} != *.ko ]] && FEATURES_splitdebug=false
process_elf "${x}" "${inode_link}" ${SAFE_STRIP_FLAGS}
FEATURES_splitdebug=${FEATURES_splitdebug_save}
fi
if ${was_not_writable} ; then
chmod u-w "${x}"
fi
) &
__multijob_post_fork
done < "${inode_link}"
done
# With a bit more work, we could run the rsync processes below in
# parallel, but not sure that'd be an overall improvement.
__multijob_finish
cd "${tmpdir}"/sources/ && cat * > "${tmpdir}/debug.sources" 2>/dev/null
if [[ -s ${tmpdir}/debug.sources ]] && \
${FEATURES_installsources} && \
! ${RESTRICT_installsources} && \
${debugedit_found}
then
__vecho "installsources: rsyncing source files"
[[ -d ${D%/}/${prepstrip_sources_dir#/} ]] || mkdir -p "${D%/}/${prepstrip_sources_dir#/}"
grep -zv '/<[^/>]*>$' "${tmpdir}"/debug.sources | \
(cd "${WORKDIR}"; LANG=C sort -z -u | \
rsync -tL0 --chmod=ugo-st,a+r,go-w,Da+x,Fa-x --files-from=- "${WORKDIR}/" "${D%/}/${prepstrip_sources_dir#/}/" )
# Preserve directory structure.
# Needed after running save_elf_sources.
# https://bugzilla.redhat.com/show_bug.cgi?id=444310
while read -r -d $'\0' emptydir
do
>> "${emptydir}"/.keepdir
done < <(find "${D%/}/${prepstrip_sources_dir#/}/" -type d -empty -print0)
fi
cd "${T}"
rm -rf "${tmpdir}"