about summary refs log tree commit diff
path: root/nixpkgs/pkgs/build-support/setup-hooks/auto-patchelf.sh
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/pkgs/build-support/setup-hooks/auto-patchelf.sh')
-rw-r--r--nixpkgs/pkgs/build-support/setup-hooks/auto-patchelf.sh95
1 files changed, 71 insertions, 24 deletions
diff --git a/nixpkgs/pkgs/build-support/setup-hooks/auto-patchelf.sh b/nixpkgs/pkgs/build-support/setup-hooks/auto-patchelf.sh
index 70b1fc802b56..d310f8255224 100644
--- a/nixpkgs/pkgs/build-support/setup-hooks/auto-patchelf.sh
+++ b/nixpkgs/pkgs/build-support/setup-hooks/auto-patchelf.sh
@@ -38,7 +38,6 @@ isExecutable() {
 declare -Ag autoPatchelfCachedDepsAssoc
 declare -ag autoPatchelfCachedDeps
 
-
 addToDepCache() {
     if [[ ${autoPatchelfCachedDepsAssoc[$1]+f} ]]; then return; fi
 
@@ -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,
@@ -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%/*}"
@@ -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)
 }
@@ -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,
@@ -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