Skip to content

Commit

Permalink
Merge pull request #137351 from impl/autopatchelfhook-cross-fix
Browse files Browse the repository at this point in the history
autoPatchelfHook: fix detection under crossSystem
  • Loading branch information
symphorien authored Sep 14, 2021
2 parents b00dfce + b79483d commit 3413d61
Showing 1 changed file with 71 additions and 24 deletions.
95 changes: 71 additions & 24 deletions pkgs/build-support/setup-hooks/auto-patchelf.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ isExecutable() {
declare -Ag autoPatchelfCachedDepsAssoc
declare -ag autoPatchelfCachedDeps


addToDepCache() {
if [[ ${autoPatchelfCachedDepsAssoc[$1]+f} ]]; then return; fi

Expand All @@ -54,25 +53,70 @@ declare -gi depCacheInitialised=0
declare -gi doneRecursiveSearch=0
declare -g foundDependency

getDepsFromSo() {
ldd "$1" 2> /dev/null | sed -n -e 's/[^=]*=> *\(.\+\) \+([^)]*)$/\1/p'
getDepsFromElfBinary() {
# NOTE: This does not use runPatchelf because it may encounter non-ELF
# files. Caller is expected to check the return code if needed.
patchelf --print-needed "$1" 2> /dev/null
}

populateCacheWithRecursiveDeps() {
local so found foundso
for so in "${autoPatchelfCachedDeps[@]}"; do
for found in $(getDepsFromSo "$so"); do
local base="${found##*/}"
local soname="${base%.so*}"
for foundso in "${found%/*}/$soname".so*; do
getRpathFromElfBinary() {
# NOTE: This does not use runPatchelf because it may encounter non-ELF
# files. Caller is expected to check the return code if needed.
local rpath
rpath="$(patchelf --print-rpath "$1" 2> /dev/null)" || return $?

local IFS=':'
printf "%s\n" $rpath
}

populateCacheForDep() {
local so="$1"
local rpath found
rpath="$(getRpathFromElfBinary "$so")" || return 1

for found in $(getDepsFromElfBinary "$so"); do
local rpathElem
for rpathElem in $rpath; do
# Ignore empty element or $ORIGIN magic variable which should be
# deterministically resolved by adding this package's library
# files early anyway.
#
# shellcheck disable=SC2016
# (Expressions don't expand in single quotes, use double quotes for
# that.)
if [[ -z "$rpathElem" || "$rpathElem" == *'$ORIGIN'* ]]; then
continue
fi

local soname="${found%.so*}"
local foundso=
for foundso in "$rpathElem/$soname".so*; do
addToDepCache "$foundso"
done

# Found in this element of the rpath, no need to check others.
if [ -n "$foundso" ]; then
break
fi
done
done

# Not found in any rpath element.
return 1
}

populateCacheWithRecursiveDeps() {
# Dependencies may add more to the end of this array, so we use a counter
# with while instead of a regular for loop here.
local -i i=0
while [ $i -lt ${#autoPatchelfCachedDeps[@]} ]; do
populateCacheForDep "${autoPatchelfCachedDeps[$i]}"
i=$i+1
done
}

getSoArch() {
objdump -f "$1" | sed -ne 's/^architecture: *\([^,]\+\).*/\1/p'
$OBJDUMP -f "$1" | sed -ne 's/^architecture: *\([^,]\+\).*/\1/p'
}

# NOTE: If you want to use this function outside of the autoPatchelf function,
Expand Down Expand Up @@ -130,25 +174,25 @@ autoPatchelfFile() {
fi
fi

echo "searching for dependencies of $toPatch" >&2
local libcLib
libcLib="$(< "$NIX_CC/nix-support/orig-libc")/lib"

# We're going to find all dependencies based on ldd output, so we need to
# clear the RPATH first.
runPatchelf --remove-rpath "$toPatch"
echo "searching for dependencies of $toPatch" >&2

# If the file is not a dynamic executable, ldd/sed will fail,
# in which case we return, since there is nothing left to do.
local missing
missing="$(
ldd "$toPatch" 2> /dev/null | \
sed -n -e 's/^[\t ]*\([^ ]\+\) => not found.*/\1/p'
)" || return 0
missing="$(getDepsFromElfBinary "$toPatch")" || return 0

# This ensures that we get the output of all missing dependencies instead
# of failing at the first one, because it's more useful when working on a
# new package where you don't yet know its dependencies.

for dep in $missing; do
# Check whether this library exists in libc. If so, we don't need to do
# any futher searching -- it will be resolved correctly by the linker.
if [ -f "$libcLib/$dep" ]; then
continue
fi

echo -n " $dep -> " >&2
if findDependency "$dep" "$(getSoArch "$toPatch")"; then
rpath="$rpath${rpath:+:}${foundDependency%/*}"
Expand Down Expand Up @@ -185,7 +229,7 @@ addAutoPatchelfSearchPath() {
done

while IFS= read -r -d '' file; do
addToDepCache "$file"
addToDepCache "$file"
done < <(find "$@" "${findOpts[@]}" \! -type d \
\( -name '*.so' -o -name '*.so.*' \) -print0)
}
Expand Down Expand Up @@ -221,10 +265,10 @@ autoPatchelf() {
segmentHeaders="$(LANG=C $READELF -l "$file")"
# Skip if the ELF file doesn't have segment headers (eg. object files).
# not using grep -q, because it can cause Broken pipe
[ -n "$(echo "$segmentHeaders" | grep '^Program Headers:')" ] || continue
grep -q '^Program Headers:' <<<"$segmentHeaders" || continue
if isExecutable "$file"; then
# Skip if the executable is statically linked.
[ -n "$(echo "$segmentHeaders" | grep "^ *INTERP\\>")" ] || continue
grep -q "^ *INTERP\\>" <<<"$segmentHeaders" || continue
fi
# Jump file if patchelf is unable to parse it
# Some programs contain binary blobs for testing,
Expand Down Expand Up @@ -256,6 +300,9 @@ autoPatchelf() {
# So what we do here is basically run in postFixup and emulate the same
# behaviour as fixupOutputHooks because the setup hook for patchelf is run in
# fixupOutput and the postFixup hook runs later.
#
# shellcheck disable=SC2016
# (Expressions don't expand in single quotes, use double quotes for that.)
postFixupHooks+=('
if [ -z "${dontAutoPatchelf-}" ]; then
autoPatchelf -- $(for output in $outputs; do
Expand Down

0 comments on commit 3413d61

Please sign in to comment.