about summary refs log tree commit diff
path: root/pkgs/games/build-support/setup-hooks/auto-patchelf.sh
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/games/build-support/setup-hooks/auto-patchelf.sh')
-rw-r--r--pkgs/games/build-support/setup-hooks/auto-patchelf.sh135
1 files changed, 135 insertions, 0 deletions
diff --git a/pkgs/games/build-support/setup-hooks/auto-patchelf.sh b/pkgs/games/build-support/setup-hooks/auto-patchelf.sh
new file mode 100644
index 00000000..b0982a45
--- /dev/null
+++ b/pkgs/games/build-support/setup-hooks/auto-patchelf.sh
@@ -0,0 +1,135 @@
+declare -a autoPatchelfLibs
+
+gatherLibraries() {
+    autoPatchelfLibs+=("$1/lib")
+}
+
+envHooks+=(gatherLibraries)
+
+isExecutable() {
+    [ "$(file -b -N --mime-type "$1")" = application/x-executable ]
+}
+
+findElfs() {
+    find "$1" -type f -exec "$SHELL" -c '
+        while [ -n "$1" ]; do
+            mimeType="$(file -b -N --mime-type "$1")"
+            if [ "$mimeType" = application/x-executable \
+              -o "$mimeType" = application/x-sharedlib ]; then
+                echo "$1"
+            fi
+            shift
+        done
+    ' -- {} +
+}
+
+declare -a cachedDependencies
+
+addToDepCache() {
+    local existing
+    for existing in "${cachedDependencies[@]}"; do
+        if [ "$existing" = "$1" ]; then return; fi
+    done
+    cachedDependencies+=("$1")
+}
+
+declare -gi depCacheInitialised=0
+declare -gi doneRecursiveSearch=0
+declare -g foundDependency
+
+getDepsFromSo() {
+    ldd "$1" 2> /dev/null | sed -n -e 's/[^=]*=> *\(.\+\) \+([^)]*)$/\1/p'
+}
+
+checkElfDep() {
+    local errors ldout="$(ldd "$1")"
+    if errors="$(echo "$ldout" | grep -F "not found")"; then
+        echo -e "Library dependencies missing for $1:\n$errors"
+    fi
+}
+
+populateCacheWithRecursiveDeps() {
+    local so found foundso
+    for so in "${cachedDependencies[@]}"; do
+        local IFS=$'\n'
+        for found in $(getDepsFromSo "$so"); do
+            local libdir="${found%/*}"
+            local base="${found##*/}"
+            local soname="${base%.so*}"
+            for foundso in "${found%/*}/$soname".so*; do
+                addToDepCache "$foundso"
+            done
+        done
+    done
+}
+
+findDependency() {
+    local filename="$1"
+    local lib dep
+
+    if [ $depCacheInitialised -eq 0 ]; then
+        for lib in "${autoPatchelfLibs[@]}"; do
+            for so in "$lib/"*.so*; do addToDepCache "$so"; done
+        done
+        depCacheInitialised=1
+    fi
+
+    for dep in "${cachedDependencies[@]}"; do
+        if [ "$filename" = "${dep##*/}" ]; then
+            foundDependency="$dep"
+            return 0
+        fi
+    done
+
+    if [ $doneRecursiveSearch -eq 0 ]; then
+        populateCacheWithRecursiveDeps
+        doneRecursiveSearch=1
+        findDependency "$filename" || return 1
+        return 0
+    fi
+    return 1
+}
+
+autoPatchelfFile() {
+    local toPatch="$1"
+    local dep
+
+    local interpreter="$(cat $NIX_CC/nix-support/dynamic-linker)"
+    if isExecutable "$toPatch"; then
+        patchelf --set-interpreter "$interpreter" "$toPatch"
+    fi
+
+    patchelf --remove-rpath "$toPatch"
+
+    local rpath=""
+    local missing="$(
+        ldd "$toPatch" | sed -n -e 's/^[\t ]*\([^ ]\+\) => not found.*/\1/p'
+    )"
+    local IFS=$'\n'
+    for dep in $missing; do
+        echo -n "searching for dependency $dep..." >&2
+        if findDependency "$dep"; then
+            rpath="$rpath${rpath:+:}${foundDependency%/*}"
+            echo " found: $foundDependency" >&2
+        else
+            echo " not found" >&2
+        fi
+    done
+
+    if [ -n "$rpath" ]; then
+        echo "setting RPATH of $toPatch to $rpath" >&2
+        patchelf --set-rpath "$rpath" "$toPatch"
+    fi
+}
+
+autoPatchelf() {
+    echo "automatically fixing dependencies for ELF files" >&2
+
+    local i IFS=$'\n'
+    cachedDependencies+=(
+        $(find "$out" \! -type d \( -name '*.so' -o -name '*.so.*' \))
+    )
+    for i in $(findElfs "$prefix"); do autoPatchelfFile "$i"; done
+}
+
+postInstallHooks+=(autoPatchelf)