#!/bin/bash # Copyright 1999-2012 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/helper-functions.sh # 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 exp_tf RESTRICT binchecks installsources 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 # 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" SPLIT_STRIP_FLAGS= ;; esac : ${PORTAGE_STRIP_FLAGS=${SAFE_STRIP_FLAGS} ${DEF_STRIP_FLAGS}} prepstrip_sources_dir=${EPREFIX}/usr/src/debug/${CATEGORY}/${PF} type -P debugedit >/dev/null && 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: inode_var_name: inode_file_link() { echo -n "${tmpdir}/inodes/" if [[ ${USERLAND} == "BSD" ]] ; then stat -f '%i' "$1" else stat -c '%i' "$1" fi } # Usage: save_elf_sources 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" ewarn "be found. This feature will not work unless debugedit is installed!" fi return 0 fi local x=$1 [[ -f $(inode_file_link "${x}") ]] && return 0 # 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}" \ "${x}") } # Usage: save_elf_debug [splitdebug file] save_elf_debug() { ${FEATURES_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 splitdebug=$2 local y=${ED}usr/lib/debug/${x:${#D}}.debug # dont save debug info twice [[ ${x} == *".debug" ]] && return 0 mkdir -p "${y%/*}" local inode=$(inode_file_link "${x}") if [[ -f ${inode} ]] ; then ln "${inode}" "${y}" 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 local args="a-x,o-w" [[ -g ${x} || -u ${x} ]] && args+=",go-r" chmod ${args} "${y}" ln "${y}" "${inode}" 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} -x .note.gnu.build-id "${x}" 2>/dev/null \ | awk '$NF ~ /GNU/ { getline; printf $2$3$4$5; getline; print $2 }') 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}" ln -s "../../${x:${#D}}.debug" "${buildid_file}.debug" ln -s "/${x:${#D}}" "${buildid_file}" fi } # Usage: process_elf process_elf() { local x=$1 strip_flags=${*:2} __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). local lockfile=$(inode_file_link "${x}")_lockfile if ! ln "${x}" "${lockfile}" 2>/dev/null ; then while [[ -f ${lockfile} ]] ; do sleep 1 done unset lockfile fi save_elf_sources "${x}" 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}" ${STRIP} ${strip_flags} \ -f "${splitdebug}" \ -F "${shortname}" \ "${x}" save_elf_debug "${x}" "${splitdebug}" else save_elf_debug "${x}" ${STRIP} ${strip_flags} "${x}" 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 # Now we look for unstripped binaries. for x in \ $(scanelf -yqRBF '#k%F' -k '.symtab' "$@") \ $(find "$@" -type f -name '*.a') 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 # hmm, can we split debug/sources for .a ? ${STRIP} -g "${x}" fi elif [[ ${f} == *"SB executable"* || ${f} == *"SB shared object"* ]] ; then process_elf "${x}" ${PORTAGE_STRIP_FLAGS} elif [[ ${f} == *"SB relocatable"* ]] ; then process_elf "${x}" ${SAFE_STRIP_FLAGS} fi if ${was_not_writable} ; then chmod u-w "${x}" fi ) & __multijob_post_fork 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}"