nix-patchtools
nix-patchtools copied to clipboard
Original RPATH is discarded, even if useful
The script always overwrites the binary's RPATH, even if the old value contains paths that allow access to libraries not found in the global $libs
search path.
Instead, perhaps we should append the new RPATH to the old one. Or, better yet, include the old RPATH when searching for libraries, so the new path will only include paths that have found something. This logic would need to respect the $ORIGIN variable sometimes found in RPATHs
I believe this will help you
autoPatchBinary()
{
local binary="$1"
local searchPathVariable="$2"
echo "Auto patching ELF binary: $binary" >&2
local binaryArch=$(guessArch "$binary")
# For each needed library, look for a package that provides and compose an RPATH string for all the paths
local archSpecificSearchPathVariable="${searchPathVariable}_${binaryArch}"
local originalRpath=$(patchelf --print-rpath "$binary")
local neededLibraries=$(patchelf --print-needed "$binary")
# Resolve $ORIGIN in original RPATH
local resolvedOriginalRpath=${originalRpath//\$ORIGIN/$(dirname "$binary")}
# Include the original RPATH in the search paths
local searchLibraryPaths="$(dirname "$binary"):$resolvedOriginalRpath:${!archSpecificSearchPathVariable}:${!searchPathVariable}"
local newRpath=
declare -A uniqueLibPaths
for lib in $neededLibraries
do
local foundLibPath=
if [ -e "$(dirname "$binary")/$lib" ]
then
foundLibPath="$(dirname "$binary")"
else
IFS=':'
for libPath in $searchLibraryPaths
do
if [ -e "$libPath/$lib" ]
then
foundLibArch=$(guessArch "$libPath/$lib")
if [ "$foundLibArch" = "$binaryArch" ]
then
foundLibPath="$libPath"
break
else
echo "WARNING: candidate library found but has architecture: $foundLibArch, while the binary has: $binaryArch" >&2
fi
fi
done
unset IFS
fi
if [ "$foundLibPath" = "" ]
then
echo "No package found that provides library: $lib" >&2
exit 1
else
if [ -z "${uniqueLibPaths[$foundLibPath]}" ]; then
uniqueLibPaths[$foundLibPath]=1
newRpath="$newRpath${newRpath:+:}$foundLibPath"
fi
fi
done
local finalRpath="$originalRpath${newRpath:+:}$newRpath"
if [ "$finalRpath" != "" ]
then
echo "Setting RPATH to: $finalRpath" >&2
patchelf --set-rpath "$finalRpath" "$binary"
fi
# Check whether the binary requires a program interpreter, and if so, patch it
if readelf -l "$binary" | grep -q "Requesting program interpreter:"
then
IFS=':'
for libPath in $searchLibraryPaths
do
local interpreterLibrary=$(basename $(patchelf --print-interpreter "$binary"))
local interpreterSubstitute="$libPath/$interpreterLibrary"
if [ -e "$interpreterSubstitute" ]
then
echo "Changing program interpreter to: $interpreterSubstitute" >&2
patchelf --set-interpreter "$interpreterSubstitute" "$binary"
local interpreterPatched=1
break
fi
done
unset IFS
if [ "$interpreterPatched" != "1" ]
then
echo "Cannot find substitute interpreter: '$interpreterSubstitute'. Did you provide a compatible version of libc in the library search paths?" >&2
exit 1
fi
fi
}