This patch includes the essential parts of the dostrip implementation from portage-mgorny. It also bans the non-standard prepstrip and prepallstrip helpers in EAPI 7, with a die message suggesting to use 'dostrip' instead. All of the prepstrip code has moved to bin/estrip, without any changes except the addition of argument parsing for estrip --ignore, --queue, and --deque modes which are equivalent to the corresponding ecompressdir modes.
Bug: https://bugs.gentoo.org/203891 --- bin/eapi.sh | 4 + bin/ebuild-helpers/prepall | 2 +- bin/ebuild-helpers/prepallstrip | 4 + bin/ebuild-helpers/prepstrip | 400 +---------------------------------- bin/ebuild.sh | 2 +- bin/estrip | 458 ++++++++++++++++++++++++++++++++++++++++ bin/misc-functions.sh | 6 + bin/phase-helpers.sh | 29 +++ bin/save-ebuild-env.sh | 4 +- 9 files changed, 509 insertions(+), 400 deletions(-) create mode 100755 bin/estrip diff --git a/bin/eapi.sh b/bin/eapi.sh index 326eb387e..f9a4744e9 100644 --- a/bin/eapi.sh +++ b/bin/eapi.sh @@ -76,6 +76,10 @@ ___eapi_has_docompress() { [[ ! ${1-${EAPI-0}} =~ ^(0|1|2|3)$ ]] } +___eapi_has_dostrip() { + [[ ${1-${EAPI-0}} =~ ^(0|1|2|3|4|4-python|4-slot-abi|5|5-hdepend|5-progress|6)$ ]] +} + ___eapi_has_nonfatal() { [[ ! ${1-${EAPI-0}} =~ ^(0|1|2|3)$ ]] } diff --git a/bin/ebuild-helpers/prepall b/bin/ebuild-helpers/prepall index 44643bb58..bc77af4a1 100755 --- a/bin/ebuild-helpers/prepall +++ b/bin/ebuild-helpers/prepall @@ -19,7 +19,7 @@ fi prepallman prepallinfo -prepallstrip +___eapi_has_dostrip || prepallstrip if has chflags $FEATURES ; then # Restore all the file flags that were saved at the beginning of prepall. diff --git a/bin/ebuild-helpers/prepallstrip b/bin/ebuild-helpers/prepallstrip index 59fa7cc61..4bde1f4b2 100755 --- a/bin/ebuild-helpers/prepallstrip +++ b/bin/ebuild-helpers/prepallstrip @@ -4,6 +4,10 @@ source "${PORTAGE_BIN_PATH}"/isolated-functions.sh || exit 1 +if ___eapi_has_dostrip; then + die "${0##*/}: ${0##*/} has been banned for EAPI '$EAPI'; use 'dostrip' instead" +fi + if ! ___eapi_has_prefix_variables; then ED=${D} fi diff --git a/bin/ebuild-helpers/prepstrip b/bin/ebuild-helpers/prepstrip index 2136e0d4d..9db06284d 100755 --- a/bin/ebuild-helpers/prepstrip +++ b/bin/ebuild-helpers/prepstrip @@ -2,402 +2,10 @@ # Copyright 1999-2018 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -source "${PORTAGE_BIN_PATH}"/helper-functions.sh || exit 1 +source "${PORTAGE_BIN_PATH}"/isolated-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 - -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 ; 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" - 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 - local objcopy_flags="--only-keep-debug" - ${FEATURES_compressdebug} && objcopy_flags+=" --compress-debug-sections" - ${OBJCOPY} ${objcopy_flags} "${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}}.debug" "${buildid_file}.debug" - [ -L "${buildid_file}" ] || ln -s "/${x:${#D}}" "${buildid_file}" - fi -} - -# Usage: process_elf <elf> -process_elf() { - local x=$1 inode_link=$2 strip_flags=${*:3} - local already_stripped lockfile xt_data - - __vecho " ${x:${#ED}}" - - # 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}" -} - -# 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} && ! ${RESTRICT_strip} ; 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" -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 -) - -# 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 - # 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 - else - strip_this=false - 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 - __vecho " ${x:${#ED}}" - 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}" - fi - fi - elif [[ ${f} == *"SB executable"* || ${f} == *"SB shared object"* ]] ; then - process_elf "${x}" "${inode_link}" ${PORTAGE_STRIP_FLAGS} - elif [[ ${f} == *"SB relocatable"* ]] ; then - process_elf "${x}" "${inode_link}" ${SAFE_STRIP_FLAGS} - 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) +if ___eapi_has_dostrip; then + die "${0##*/}: ${0##*/} has been banned for EAPI '$EAPI'; use 'dostrip' instead" fi -cd "${T}" -rm -rf "${tmpdir}" +exec -a "${0}" "${PORTAGE_BIN_PATH}"/estrip "${@}" diff --git a/bin/ebuild.sh b/bin/ebuild.sh index 061b1aff4..11441bfb2 100755 --- a/bin/ebuild.sh +++ b/bin/ebuild.sh @@ -59,7 +59,7 @@ else # These dummy functions are for things that are likely to be called # in global scope, even though they are completely useless during # the "depend" phase. - funcs="diropts docompress exeopts get_KV insopts + funcs="diropts docompress dostrip exeopts get_KV insopts KV_major KV_micro KV_minor KV_to_int libopts register_die_hook register_success_hook __strip_duplicate_slashes diff --git a/bin/estrip b/bin/estrip new file mode 100755 index 000000000..cc3450b78 --- /dev/null +++ b/bin/estrip @@ -0,0 +1,458 @@ +#!/bin/bash +# Copyright 1999-2018 Gentoo Foundation +# 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 + +if [[ ${0##*/} != 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 +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 ; 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" + 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 + local objcopy_flags="--only-keep-debug" + ${FEATURES_compressdebug} && objcopy_flags+=" --compress-debug-sections" + ${OBJCOPY} ${objcopy_flags} "${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}}.debug" "${buildid_file}.debug" + [ -L "${buildid_file}" ] || ln -s "/${x:${#D}}" "${buildid_file}" + fi +} + +# Usage: process_elf <elf> +process_elf() { + local x=$1 inode_link=$2 strip_flags=${*:3} + local already_stripped lockfile xt_data + + __vecho " ${x:${#ED}}" + + # 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}" +} + +# 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} && ! ${RESTRICT_strip} ; 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" +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 +) + +# 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 + # 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 + else + strip_this=false + 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 + __vecho " ${x:${#ED}}" + 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}" + fi + fi + elif [[ ${f} == *"SB executable"* || ${f} == *"SB shared object"* ]] ; then + process_elf "${x}" "${inode_link}" ${PORTAGE_STRIP_FLAGS} + elif [[ ${f} == *"SB relocatable"* ]] ; then + process_elf "${x}" "${inode_link}" ${SAFE_STRIP_FLAGS} + 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}" diff --git a/bin/misc-functions.sh b/bin/misc-functions.sh index 7643af7b5..df8361036 100755 --- a/bin/misc-functions.sh +++ b/bin/misc-functions.sh @@ -224,6 +224,12 @@ install_qa_check() { ecompressdir --dequeue ecompress --dequeue + if ___eapi_has_dostrip; then + "${PORTAGE_BIN_PATH}"/estrip --queue "${PORTAGE_DOSTRIP[@]}" + "${PORTAGE_BIN_PATH}"/estrip --ignore "${PORTAGE_DOSTRIP_SKIP[@]}" + "${PORTAGE_BIN_PATH}"/estrip --dequeue + fi + # Create NEEDED.ELF.2 regardless of RESTRICT=binchecks, since this info is # too useful not to have (it's required for things like preserve-libs), and # it's tempting for ebuild authors to set RESTRICT=binchecks for packages diff --git a/bin/phase-helpers.sh b/bin/phase-helpers.sh index 5eeecfef7..556d089b5 100644 --- a/bin/phase-helpers.sh +++ b/bin/phase-helpers.sh @@ -20,6 +20,9 @@ export MOPREFIX=${PN} export PORTAGE_DOCOMPRESS_SIZE_LIMIT="128" declare -a PORTAGE_DOCOMPRESS=( /usr/share/{doc,info,man} ) declare -a PORTAGE_DOCOMPRESS_SKIP=( /usr/share/doc/${PF}/html ) +declare -a PORTAGE_DOSTRIP=( / ) +has strip ${RESTRICT} && PORTAGE_DOSTRIP=() +declare -a PORTAGE_DOSTRIP_SKIP=() into() { if [ "$1" == "/" ]; then @@ -160,6 +163,32 @@ docompress() { fi } +dostrip() { + ___eapi_has_dostrip || die "'${FUNCNAME}' not supported in this EAPI" + + local f g + if [[ $1 = "-x" ]]; then + shift + for f; do + f=$(__strip_duplicate_slashes "${f}"); f=${f%/} + [[ ${f:0:1} = / ]] || f="/${f}" + for g in "${PORTAGE_DOSTRIP_SKIP[@]}"; do + [[ ${f} = "${g}" ]] && continue 2 + done + PORTAGE_DOSTRIP_SKIP+=( "${f}" ) + done + else + for f; do + f=$(__strip_duplicate_slashes "${f}"); f=${f%/} + [[ ${f:0:1} = / ]] || f="/${f}" + for g in "${PORTAGE_DOSTRIP[@]}"; do + [[ ${f} = "${g}" ]] && continue 2 + done + PORTAGE_DOSTRIP+=( "${f}" ) + done + fi +} + useq() { has $EBUILD_PHASE prerm postrm || eqawarn \ "QA Notice: The 'useq' function is deprecated (replaced by 'use')" diff --git a/bin/save-ebuild-env.sh b/bin/save-ebuild-env.sh index e5ae8af88..947ac79d5 100644 --- a/bin/save-ebuild-env.sh +++ b/bin/save-ebuild-env.sh @@ -15,7 +15,7 @@ __save_ebuild_env() { if has --exclude-init-phases $* ; then unset S _E_DESTTREE _E_INSDESTTREE _E_DOCDESTTREE_ _E_EXEDESTTREE_ \ PORTAGE_DOCOMPRESS_SIZE_LIMIT PORTAGE_DOCOMPRESS \ - PORTAGE_DOCOMPRESS_SKIP + PORTAGE_DOCOMPRESS_SKIP PORTAGE_DOSTRIP PORTAGE_DOSTRIP_SKIP if [[ -n $PYTHONPATH && ${PYTHONPATH%%:*} -ef $PORTAGE_PYM_PATH ]] ; then if [[ $PYTHONPATH == *:* ]] ; then @@ -60,7 +60,7 @@ __save_ebuild_env() { unpack __strip_duplicate_slashes econf einstall \ __dyn_setup __dyn_unpack __dyn_clean \ into insinto exeinto docinto \ - insopts diropts exeopts libopts docompress \ + insopts diropts exeopts libopts docompress dostrip \ __abort_handler __abort_prepare __abort_configure __abort_compile \ __abort_test __abort_install __dyn_prepare __dyn_configure \ __dyn_compile __dyn_test __dyn_install \ -- 2.13.6