dd880527758a26de3ea3ac4aad31a73855be6d16
[portage.git] / bin / ebuild-helpers / prepstrip
1 #!/bin/bash
2 # Copyright 1999-2012 Gentoo Foundation
3 # Distributed under the terms of the GNU General Public License v2
4
5 source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/helper-functions.sh
6
7 # avoid multiple calls to `has`.  this creates things like:
8 #   FEATURES_foo=false
9 # if "foo" is not in $FEATURES
10 tf() { "$@" && echo true || echo false ; }
11 exp_tf() {
12         local flag var=$1
13         shift
14         for flag in "$@" ; do
15                 eval ${var}_${flag}=$(tf has ${flag} ${!var})
16         done
17 }
18 exp_tf FEATURES compressdebug installsources nostrip splitdebug
19 exp_tf RESTRICT binchecks installsources strip
20
21 if ! ___eapi_has_prefix_variables; then
22         EPREFIX= ED=${D}
23 fi
24
25 banner=false
26 SKIP_STRIP=false
27 if ${RESTRICT_strip} || ${FEATURES_nostrip} ; then
28         SKIP_STRIP=true
29         banner=true
30         ${FEATURES_installsources} || exit 0
31 fi
32
33 # look up the tools we might be using
34 for t in STRIP:strip OBJCOPY:objcopy READELF:readelf ; do
35         v=${t%:*} # STRIP
36         t=${t#*:} # strip
37         eval ${v}=\"${!v:-${CHOST}-${t}}\"
38         type -P -- ${!v} >/dev/null || eval ${v}=${t}
39 done
40
41 # Figure out what tool set we're using to strip stuff
42 unset SAFE_STRIP_FLAGS DEF_STRIP_FLAGS SPLIT_STRIP_FLAGS
43 case $(${STRIP} --version 2>/dev/null) in
44 *elfutils*) # dev-libs/elfutils
45         # elfutils default behavior is always safe, so don't need to specify
46         # any flags at all
47         SAFE_STRIP_FLAGS=""
48         DEF_STRIP_FLAGS="--remove-comment"
49         SPLIT_STRIP_FLAGS="-f"
50         ;;
51 *GNU*) # sys-devel/binutils
52         # We'll leave out -R .note for now until we can check out the relevance
53         # of the section when it has the ALLOC flag set on it ...
54         SAFE_STRIP_FLAGS="--strip-unneeded"
55         DEF_STRIP_FLAGS="-R .comment -R .GCC.command.line"
56         SPLIT_STRIP_FLAGS=
57         ;;
58 esac
59 : ${PORTAGE_STRIP_FLAGS=${SAFE_STRIP_FLAGS} ${DEF_STRIP_FLAGS}}
60
61 prepstrip_sources_dir=${EPREFIX}/usr/src/debug/${CATEGORY}/${PF}
62
63 type -P debugedit >/dev/null && debugedit_found=true || debugedit_found=false
64 debugedit_warned=false
65
66 __multijob_init
67
68 # Setup $T filesystem layout that we care about.
69 tmpdir="${T}/prepstrip"
70 rm -rf "${tmpdir}"
71 mkdir -p "${tmpdir}"/{inodes,splitdebug,sources}
72
73 # Usage: save_elf_sources <elf>
74 save_elf_sources() {
75         ${FEATURES_installsources} || return 0
76         ${RESTRICT_installsources} && return 0
77         if ! ${debugedit_found} ; then
78                 if ! ${debugedit_warned} ; then
79                         debugedit_warned=true
80                         ewarn "FEATURES=installsources is enabled but the debugedit binary could not"
81                         ewarn "be found. This feature will not work unless debugedit is installed!"
82                 fi
83                 return 0
84         fi
85
86         local x=$1
87
88         # since we're editing the ELF here, we should recompute the build-id
89         # (the -i flag below).  save that output so we don't need to recompute
90         # it later on in the save_elf_debug step.
91         buildid=$(debugedit -i \
92                 -b "${WORKDIR}" \
93                 -d "${prepstrip_sources_dir}" \
94                 -l "${tmpdir}/sources/${x##*/}.${BASHPID}" \
95                 "${x}")
96 }
97
98 # Usage: save_elf_debug <elf> [splitdebug file]
99 save_elf_debug() {
100         ${FEATURES_splitdebug} || return 0
101
102         # NOTE: Debug files must be installed in
103         # ${EPREFIX}/usr/lib/debug/${EPREFIX} (note that ${EPREFIX} occurs
104         # twice in this path) in order for gdb's debug-file-directory
105         # lookup to work correctly.
106         local x=$1
107         local inode_debug=$2
108         local splitdebug=$3
109         local y=${ED}usr/lib/debug/${x:${#D}}.debug
110
111         # dont save debug info twice
112         [[ ${x} == *".debug" ]] && return 0
113
114         mkdir -p "${y%/*}"
115
116         if [ -f "${inode_debug}" ] ; then
117                 ln "${inode_debug}" "${y}" || die "ln failed unexpectedly"
118         else
119                 if [[ -n ${splitdebug} ]] ; then
120                         mv "${splitdebug}" "${y}"
121                 else
122                         local objcopy_flags="--only-keep-debug"
123                         ${FEATURES_compressdebug} && objcopy_flags+=" --compress-debug-sections"
124                         ${OBJCOPY} ${objcopy_flags} "${x}" "${y}"
125                         ${OBJCOPY} --add-gnu-debuglink="${y}" "${x}"
126                 fi
127                 local args="a-x,o-w"
128                 [[ -g ${x} || -u ${x} ]] && args+=",go-r"
129                 chmod ${args} "${y}"
130                 ln "${y}" "${inode_debug}" || die "ln failed unexpectedly"
131         fi
132
133         # if we don't already have build-id from debugedit, look it up
134         if [[ -z ${buildid} ]] ; then
135                 # convert the readelf output to something useful
136                 buildid=$(${READELF} -x .note.gnu.build-id "${x}" 2>/dev/null \
137                         | awk '$NF ~ /GNU/ { getline; printf $2$3$4$5; getline; print $2 }')
138         fi
139         if [[ -n ${buildid} ]] ; then
140                 local buildid_dir="${ED}usr/lib/debug/.build-id/${buildid:0:2}"
141                 local buildid_file="${buildid_dir}/${buildid:2}"
142                 mkdir -p "${buildid_dir}"
143                 ln -s "../../${x:${#D}}.debug" "${buildid_file}.debug"
144                 ln -s "/${x:${#D}}" "${buildid_file}"
145         fi
146 }
147
148 # Usage: process_elf <elf>
149 process_elf() {
150         local x=$1 inode_link=$2 strip_flags=${*:3}
151         local already_stripped lockfile
152
153         __vecho "   ${x:${#ED}}"
154
155         # If two processes try to debugedit or strip the same hardlink at the
156         # same time, it may corrupt files or cause loss of splitdebug info.
157         # So, use a lockfile to prevent interference (easily observed with
158         # dev-vcs/git which creates ~111 hardlinks to one file in
159         # /usr/libexec/git-core).
160         lockfile=${inode_link}_lockfile
161         if ! ln "${inode_link}" "${lockfile}" 2>/dev/null ; then
162                 while [[ -f ${lockfile} ]] ; do
163                         sleep 1
164                 done
165                 unset lockfile
166         fi
167
168         [ -f "${inode_link}_stripped" ] && already_stripped=true || already_stripped=false
169
170         ${already_stripped} || save_elf_sources "${x}"
171
172         if ${strip_this} ; then
173
174                 # see if we can split & strip at the same time
175                 if [[ -n ${SPLIT_STRIP_FLAGS} ]] ; then
176                         local shortname="${x##*/}.debug"
177                         local splitdebug="${tmpdir}/splitdebug/${shortname}.${BASHPID}"
178                         ${already_stripped} || \
179                         ${STRIP} ${strip_flags} \
180                                 -f "${splitdebug}" \
181                                 -F "${shortname}" \
182                                 "${x}"
183                         save_elf_debug "${x}" "${inode_link}_debug" "${splitdebug}"
184                 else
185                         save_elf_debug "${x}" "${inode_link}_debug"
186                         ${already_stripped} || \
187                         ${STRIP} ${strip_flags} "${x}"
188                 fi
189         fi
190
191         if ${already_stripped} ; then
192                 rm -f "${x}" || die "rm failed unexpectedly"
193                 ln "${inode_link}_stripped" "${x}" || die "ln failed unexpectedly"
194         else
195                 ln "${x}" "${inode_link}_stripped" || die "ln failed unexpectedly"
196         fi
197
198         [[ -n ${lockfile} ]] && rm -f "${lockfile}"
199 }
200
201 # The existance of the section .symtab tells us that a binary is stripped.
202 # We want to log already stripped binaries, as this may be a QA violation.
203 # They prevent us from getting the splitdebug data.
204 if ! ${RESTRICT_binchecks} && ! ${RESTRICT_strip} ; then
205         # We need to do the non-stripped scan serially first before we turn around
206         # and start stripping the files ourselves.  The log parsing can be done in
207         # parallel though.
208         log=${tmpdir}/scanelf-already-stripped.log
209         scanelf -yqRBF '#k%F' -k '!.symtab' "$@" | sed -e "s#^${ED}##" > "${log}"
210         (
211         __multijob_child_init
212         qa_var="QA_PRESTRIPPED_${ARCH/-/_}"
213         [[ -n ${!qa_var} ]] && QA_PRESTRIPPED="${!qa_var}"
214         if [[ -n ${QA_PRESTRIPPED} && -s ${log} && \
215                 ${QA_STRICT_PRESTRIPPED-unset} = unset ]] ; then
216                 shopts=$-
217                 set -o noglob
218                 for x in ${QA_PRESTRIPPED} ; do
219                         sed -e "s#^${x#/}\$##" -i "${log}"
220                 done
221                 set +o noglob
222                 set -${shopts}
223         fi
224         sed -e "/^\$/d" -e "s#^#/#" -i "${log}"
225         if [[ -s ${log} ]] ; then
226                 __vecho -e "\n"
227                 eqawarn "QA Notice: Pre-stripped files found:"
228                 eqawarn "$(<"${log}")"
229         else
230                 rm -f "${log}"
231         fi
232         ) &
233         __multijob_post_fork
234 fi
235
236 # Since strip creates a new inode, we need to know the initial set of
237 # inodes in advance, so that we can avoid interference due to trying
238 # to strip the same (hardlinked) file multiple times in parallel.
239 # See bug #421099.
240 if  [[ ${USERLAND} == BSD ]] ; then
241         get_inode_number() { stat -f '%i' "$1"; }
242 else
243         get_inode_number() { stat -c '%i' "$1"; }
244 fi
245 cd "${tmpdir}/inodes" || die "cd failed unexpectedly"
246 while read -r x ; do
247         inode_link=$(get_inode_number "${x}") || die "stat failed unexpectedly"
248         echo "${x}" >> "${inode_link}" || die "echo failed unexpectedly"
249 done < <(
250         # Use sort -u to eliminate duplicates for bug #445336.
251         (
252                 scanelf -yqRBF '#k%F' -k '.symtab' "$@"
253                 find "$@" -type f ! -type l -name '*.a'
254         ) | LC_ALL=C sort -u
255 )
256
257 # Now we look for unstripped binaries.
258 for inode_link in $(shopt -s nullglob; echo *) ; do
259 while read -r x
260 do
261
262         if ! ${banner} ; then
263                 __vecho "strip: ${STRIP} ${PORTAGE_STRIP_FLAGS}"
264                 banner=true
265         fi
266
267         (
268         __multijob_child_init
269         f=$(file "${x}") || exit 0
270         [[ -z ${f} ]] && exit 0
271
272         if ! ${SKIP_STRIP} ; then
273                 # The noglob funk is to support STRIP_MASK="/*/booga" and to keep
274                 #  the for loop from expanding the globs.
275                 # The eval echo is to support STRIP_MASK="/*/{booga,bar}" sex.
276                 set -o noglob
277                 strip_this=true
278                 for m in $(eval echo ${STRIP_MASK}) ; do
279                         [[ /${x#${ED}} == ${m} ]] && strip_this=false && break
280                 done
281                 set +o noglob
282         else
283                 strip_this=false
284         fi
285
286         # In Prefix we are usually an unprivileged user, so we can't strip
287         # unwritable objects.  Make them temporarily writable for the
288         # stripping.
289         was_not_writable=false
290         if [[ ! -w ${x} ]] ; then
291                 was_not_writable=true
292                 chmod u+w "${x}"
293         fi
294
295         # only split debug info for final linked objects
296         # or kernel modules as debuginfo for intermediatary
297         # files (think crt*.o from gcc/glibc) is useless and
298         # actually causes problems.  install sources for all
299         # elf types though cause that stuff is good.
300
301         buildid=
302         if [[ ${f} == *"current ar archive"* ]] ; then
303                 __vecho "   ${x:${#ED}}"
304                 if ${strip_this} ; then
305                         # hmm, can we split debug/sources for .a ?
306                         ${STRIP} -g "${x}"
307                 fi
308         elif [[ ${f} == *"SB executable"* || ${f} == *"SB shared object"* ]] ; then
309                 process_elf "${x}" "${inode_link}" ${PORTAGE_STRIP_FLAGS}
310         elif [[ ${f} == *"SB relocatable"* ]] ; then
311                 process_elf "${x}" "${inode_link}" ${SAFE_STRIP_FLAGS}
312         fi
313
314         if ${was_not_writable} ; then
315                 chmod u-w "${x}"
316         fi
317         ) &
318         __multijob_post_fork
319
320 done < "${inode_link}"
321 done
322
323 # With a bit more work, we could run the rsync processes below in
324 # parallel, but not sure that'd be an overall improvement.
325 __multijob_finish
326
327 cd "${tmpdir}"/sources/ && cat * > "${tmpdir}/debug.sources" 2>/dev/null
328 if [[ -s ${tmpdir}/debug.sources ]] && \
329    ${FEATURES_installsources} && \
330    ! ${RESTRICT_installsources} && \
331    ${debugedit_found}
332 then
333         __vecho "installsources: rsyncing source files"
334         [[ -d ${D}${prepstrip_sources_dir} ]] || mkdir -p "${D}${prepstrip_sources_dir}"
335         grep -zv '/<[^/>]*>$' "${tmpdir}"/debug.sources | \
336                 (cd "${WORKDIR}"; LANG=C sort -z -u | \
337                 rsync -tL0 --chmod=ugo-st,a+r,go-w,Da+x,Fa-x --files-from=- "${WORKDIR}/" "${D}${prepstrip_sources_dir}/" )
338
339         # Preserve directory structure.
340         # Needed after running save_elf_sources.
341         # https://bugzilla.redhat.com/show_bug.cgi?id=444310
342         while read -r -d $'\0' emptydir
343         do
344                 >> "${emptydir}"/.keepdir
345         done < <(find "${D}${prepstrip_sources_dir}/" -type d -empty -print0)
346 fi
347
348 cd "${T}"
349 rm -rf "${tmpdir}"