Skip to content

Commit

Permalink
Merge pull request #137886 from impl/autopatchelfhook-arch-abi-compat
Browse files Browse the repository at this point in the history
autoPatchelfHook: improve arch/ABI compatibility, fix packages that use stdenvNoCC
  • Loading branch information
symphorien authored Sep 22, 2021
2 parents 42ade1c + a7f5e83 commit 8ce8c97
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 13 deletions.
92 changes: 80 additions & 12 deletions pkgs/build-support/setup-hooks/auto-patchelf.sh
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,9 @@ 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 $?
IFS=':' read -ra rpath < <(patchelf --print-rpath "$1" 2> /dev/null) || return $?

local IFS=':'
printf "%s\n" $rpath
printf "%s\n" "${rpath[@]}"
}

populateCacheForDep() {
Expand Down Expand Up @@ -115,8 +114,52 @@ populateCacheWithRecursiveDeps() {
done
}

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

# Returns the specific OS ABI for an ELF file in the format produced by
# readelf(1), like "UNIX - System V" or "UNIX - GNU".
getBinOsabi() {
$READELF -h "$1" 2> /dev/null | sed -ne 's/^[ \t]*OS\/ABI:[ \t]*\(.*\)/\1/p'
}

# Tests whether two OS ABIs are compatible, taking into account the generally
# accepted compatibility of SVR4 ABI with other ABIs.
areBinOsabisCompatible() {
local wanted="$1"
local got="$2"

if [[ -z "$wanted" || -z "$got" ]]; then
# One of the types couldn't be detected, so as a fallback we'll assume
# they're compatible.
return 0
fi

# Generally speaking, the base ABI (0x00), which is represented by
# readelf(1) as "UNIX - System V", indicates broad compatibility with other
# ABIs.
#
# TODO: This isn't always true. For example, some OSes embed ABI
# compatibility into SHT_NOTE sections like .note.tag and .note.ABI-tag.
# It would be prudent to add these to the detection logic to produce better
# ABI information.
if [[ "$wanted" == "UNIX - System V" ]]; then
return 0
fi

# Similarly here, we should be able to link against a superset of features,
# so even if the target has another ABI, this should be fine.
if [[ "$got" == "UNIX - System V" ]]; then
return 0
fi

# Otherwise, we simply return whether the ABIs are identical.
if [[ "$wanted" == "$got" ]]; then
return 0
fi

return 1
}

# NOTE: If you want to use this function outside of the autoPatchelf function,
Expand All @@ -127,6 +170,7 @@ getSoArch() {
findDependency() {
local filename="$1"
local arch="$2"
local osabi="$3"
local lib dep

if [ $depCacheInitialised -eq 0 ]; then
Expand All @@ -138,7 +182,7 @@ findDependency() {

for dep in "${autoPatchelfCachedDeps[@]}"; do
if [ "$filename" = "${dep##*/}" ]; then
if [ "$(getSoArch "$dep")" = "$arch" ]; then
if [ "$(getBinArch "$dep")" = "$arch" ] && areBinOsabisCompatible "$osabi" "$(getBinOsabi "$dep")"; then
foundDependency="$dep"
return 0
fi
Expand All @@ -162,7 +206,24 @@ autoPatchelfFile() {
local dep rpath="" toPatch="$1"

local interpreter
interpreter="$(< "$NIX_CC/nix-support/dynamic-linker")"
interpreter="$(< "$NIX_BINTOOLS/nix-support/dynamic-linker")"

local interpreterArch interpreterOsabi toPatchArch toPatchOsabi
interpreterArch="$(getBinArch "$interpreter")"
interpreterOsabi="$(getBinOsabi "$interpreter")"
toPatchArch="$(getBinArch "$toPatch")"
toPatchOsabi="$(getBinOsabi "$toPatch")"

if [ "$interpreterArch" != "$toPatchArch" ]; then
# Our target architecture is different than this file's architecture,
# so skip it.
echo "skipping $toPatch because its architecture ($toPatchArch) differs from target ($interpreterArch)" >&2
return 0
elif ! areBinOsabisCompatible "$interpreterOsabi" "$toPatchOsabi"; then
echo "skipping $toPatch because its OS ABI ($toPatchOsabi) is not compatible with target ($interpreterOsabi)" >&2
return 0
fi

if isExecutable "$toPatch"; then
runPatchelf --set-interpreter "$interpreter" "$toPatch"
# shellcheck disable=SC2154
Expand All @@ -175,7 +236,7 @@ autoPatchelfFile() {
fi

local libcLib
libcLib="$(< "$NIX_CC/nix-support/orig-libc")/lib"
libcLib="$(< "$NIX_BINTOOLS/nix-support/orig-libc")/lib"

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

Expand All @@ -187,14 +248,21 @@ autoPatchelfFile() {
# 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
if [[ "$dep" == /* ]]; then
# This is an absolute path. If it exists, just use it. Otherwise,
# we probably want this to produce an error when checked (because
# just updating the rpath won't satisfy it).
if [ -f "$dep" ]; then
continue
fi
elif [ -f "$libcLib/$dep" ]; then
# This library exists in libc, and will be correctly resolved by
# the linker.
continue
fi

echo -n " $dep -> " >&2
if findDependency "$dep" "$(getSoArch "$toPatch")"; then
if findDependency "$dep" "$toPatchArch" "$toPatchOsabi"; then
rpath="$rpath${rpath:+:}${foundDependency%/*}"
echo "found: $foundDependency" >&2
else
Expand Down
3 changes: 2 additions & 1 deletion pkgs/top-level/all-packages.nix
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ with pkgs;

autorestic = callPackage ../tools/backup/autorestic { };

autoPatchelfHook = makeSetupHook { name = "auto-patchelf-hook"; }
autoPatchelfHook = makeSetupHook
{ name = "auto-patchelf-hook"; deps = [ bintools ]; }
../build-support/setup-hooks/auto-patchelf.sh;

appimageTools = callPackage ../build-support/appimage {
Expand Down

0 comments on commit 8ce8c97

Please sign in to comment.