Merge pull request #137886 from impl/autopatchelfhook-arch-abi-compat

autoPatchelfHook: improve arch/ABI compatibility, fix packages that use stdenvNoCC
This commit is contained in:
Guillaume Girol 2021-09-22 18:57:40 +00:00 committed by GitHub
commit 8ce8c97a19
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 82 additions and 13 deletions

View file

@ -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() {
@ -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,
@ -127,6 +170,7 @@ getSoArch() {
findDependency() {
local filename="$1"
local arch="$2"
local osabi="$3"
local lib dep
if [ $depCacheInitialised -eq 0 ]; then
@ -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
@ -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
@ -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
@ -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

View file

@ -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 {