about summary refs log tree commit diff
path: root/pkgs/build-support/auto-patchelf/setup-hook.sh
blob: 0592e9982a36ca70df5fcbfc049e933b48c49100 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
declare -a autoPatchelfLibs

gatherLibraries() {
    autoPatchelfLibs+=("$1/lib")
}

addEnvHooks "$targetOffset" 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'
}

populateCacheWithRecursiveDeps() {
    local so found foundso
    for so in "${cachedDependencies[@]}"; do
        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
}

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

findDependency() {
    local filename="$1"
    local arch="$2"
    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
            if [ "$(getSoArch "$dep")" = "$arch" ]; then
                foundDependency="$dep"
                return 0
            fi
        fi
    done

    if [ $doneRecursiveSearch -eq 0 ]; then
        populateCacheWithRecursiveDeps
        doneRecursiveSearch=1
        findDependency "$filename" "$arch" || return 1
        return 0
    fi
    return 1
}

autoPatchelfFile() {
    local dep rpath="" toPatch="$1"

    local interpreter="$(< "$NIX_CC/nix-support/dynamic-linker")"
    if isExecutable "$toPatch"; then
        @patchelf@ --set-interpreter "$interpreter" "$toPatch"
        if [ -n "$runtimeDependencies" ]; then
            for dep in $runtimeDependencies; do
                rpath="$rpath${rpath:+:}$dep/lib"
            done
        fi
    fi

    echo "searching for dependencies of $toPatch:" >&2

    @patchelf@ --remove-rpath "$toPatch"

    local missing="$(
        @ldd@ "$toPatch" 2> /dev/null | \
            @sed@ -n -e 's/^[\t ]*\([^ ]\+\) => not found.*/\1/p'
    )"

    local -i depNotFound=0

    for dep in $missing; do
        echo -n "  $dep -> " >&2
        if findDependency "$dep" "$(getSoArch "$toPatch")"; then
            rpath="$rpath${rpath:+:}${foundDependency%/*}"
            echo "found: $foundDependency" >&2
        else
            echo "not found!" >&2
            depNotFound=1
        fi
    done

    [ $depNotFound -eq 0 ]

    if [ -n "$rpath" ]; then
        echo "setting RPATH to: $rpath" >&2
        @patchelf@ --set-rpath "$rpath" "$toPatch"
    fi
}

autoPatchelf() {
    echo "automatically fixing dependencies for ELF files" >&2

    cachedDependencies+=(
        $(find "$out" \! -type d \( -name '*.so' -o -name '*.so.*' \))
    )
    local elffile
    findElfs "$prefix" | while read -r elffile; do
        autoPatchelfFile "$elffile"
    done
}

postInstallHooks+=(autoPatchelf)