nix-patchtools icon indicating copy to clipboard operation
nix-patchtools copied to clipboard

Original RPATH is discarded, even if useful

Open Erudition opened this issue 2 years ago • 1 comments

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

Erudition avatar Jan 08 '22 21:01 Erudition

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
}

Kreijstal avatar Dec 26 '23 14:12 Kreijstal