From 2e3fdc2030c75c19df6fc2924083cfad53856562 Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Fri, 3 Jun 2016 23:03:51 +0300 Subject: Extract a function for splitting a colon-separated string We're going to need this logic in another place, so make a function of this. --- src/patchelf.cc | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/patchelf.cc b/src/patchelf.cc index ca8fb49..b4e3e18 100644 --- a/src/patchelf.cc +++ b/src/patchelf.cc @@ -57,6 +57,22 @@ unsigned char * contents = 0; #define ElfFileParamNames Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Addr, Elf_Off, Elf_Dyn, Elf_Sym, Elf_Verneed +static vector splitColonDelimitedString(const char * s){ + vector parts; + const char * pos = s; + while (*pos) { + const char * end = strchr(pos, ':'); + if (!end) end = strchr(pos, 0); + + parts.push_back(string(pos, end - pos)); + if (*end == ':') ++end; + pos = end; + } + + return parts; +} + + static unsigned int getPageSize(){ return pageSize; } @@ -1095,15 +1111,9 @@ void ElfFile::modifyRPath(RPathOp op, string newRPath) newRPath = ""; - char * pos = rpath; - while (*pos) { - char * end = strchr(pos, ':'); - if (!end) end = strchr(pos, 0); - - /* Get the name of the directory. */ - string dirName(pos, end - pos); - if (*end == ':') ++end; - pos = end; + vector rpathDirs = splitColonDelimitedString(rpath); + for (vector::iterator it = rpathDirs.begin(); it != rpathDirs.end(); ++it) { + const string & dirName = *it; /* Non-absolute entries are allowed (e.g., the special "$ORIGIN" hack). */ -- cgit v0.12 From 48143414941d91016efca2b0ef6f32475d6eaa75 Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Sat, 4 Jun 2016 00:25:02 +0300 Subject: Add '--allowed-rpath-prefixes' option to '--shrink-rpath' Fixes #97. In essence, the problem is that some packages in Nixpkgs have RPATHs pointing to both $NIX_BUILD_TOP and $out, e.g.: /tmp/nix-build-openldap-2.4.44.drv-0/openldap-2.4.44/libraries/libldap_r/.libs /tmp/nix-build-openldap-2.4.44.drv-0/openldap-2.4.44/libraries/liblber/.libs /nix/store/bfkmdxmv3a3f0g3d2q8jkdz2wam93c5z-openldap-2.4.44/lib /nix/store/bfkmdxmv3a3f0g3d2q8jkdz2wam93c5z-openldap-2.4.44/lib64 Currently, running `patchelf --shrink-rpath` does the wrong thing by keeping the /tmp/ paths and deleting the /nix/store ones. Now we can fix the problem by using patchelf --shrink-rpath --allowed-rpath-prefixes $NIX_STORE_DIR in the Nixpkgs fixupPhase instead. --- README | 8 +++++ patchelf.1 | 6 ++++ src/patchelf.cc | 32 ++++++++++++++++---- tests/Makefile.am | 2 +- tests/shrink-rpath-with-allowed-prefixes.sh | 47 +++++++++++++++++++++++++++++ 5 files changed, 88 insertions(+), 7 deletions(-) create mode 100755 tests/shrink-rpath-with-allowed-prefixes.sh diff --git a/README b/README index eb291ba..406cefd 100644 --- a/README +++ b/README @@ -19,6 +19,14 @@ libraries. In particular, it can do the following: an RPATH "/lib:/usr/lib:/foo/lib", and libfoo.so can only be found in /foo/lib, then the new RPATH will be "/foo/lib". + In addition, the '--allowed-rpath-prefixes' option can be used for + further rpath tuning. For instance, if an executable has an RPATH + "/tmp/build-foo/.libs:/foo/lib", it is probably desirable to keep + the "/foo/lib" reference instead of the "/tmp" entry. To accomplish + that, use: + + $ patchelf --shrink-rpath --allowed-rpath-prefixes /usr/lib:/foo/lib my-program + * Remove declared dependencies on dynamic libraries (DT_NEEDED entries): diff --git a/patchelf.1 b/patchelf.1 index 8067dff..ecf4217 100644 --- a/patchelf.1 +++ b/patchelf.1 @@ -53,6 +53,12 @@ For instance, if an executable references one library libfoo.so, has an RPATH "/lib:/usr/lib:/foo/lib", and libfoo.so can only be found in /foo/lib, then the new RPATH will be "/foo/lib". +.IP "--allowed-rpath-prefixes PREFIXES" +Combined with the "--shrink-rpath" option, this can be used for +further rpath tuning. For instance, if an executable has an RPATH +"/tmp/build-foo/.libs:/foo/lib", it is probably desirable to keep +the "/foo/lib" reference instead of the "/tmp" entry. + .IP --print-rpath Prints the RPATH for an executable or library. diff --git a/src/patchelf.cc b/src/patchelf.cc index b4e3e18..a59c12d 100644 --- a/src/patchelf.cc +++ b/src/patchelf.cc @@ -72,6 +72,13 @@ static vector splitColonDelimitedString(const char * s){ return parts; } +static bool hasAllowedPrefix(const string & s, const vector & allowedPrefixes){ + for (vector::const_iterator it = allowedPrefixes.begin(); it != allowedPrefixes.end(); ++it) { + if (!s.compare(0, it->size(), *it)) return true; + } + return false; +} + static unsigned int getPageSize(){ return pageSize; @@ -185,7 +192,7 @@ public: typedef enum { rpPrint, rpShrink, rpSet, rpRemove } RPathOp; - void modifyRPath(RPathOp op, string newRPath); + void modifyRPath(RPathOp op, vector allowedRpathPrefixes, string newRPath); void addNeeded(set libs); @@ -1043,7 +1050,7 @@ static void concatToRPath(string & rpath, const string & path) template -void ElfFile::modifyRPath(RPathOp op, string newRPath) +void ElfFile::modifyRPath(RPathOp op, vector allowedRpathPrefixes, string newRPath) { Elf_Shdr & shdrDynamic = findSection(".dynamic"); @@ -1122,6 +1129,13 @@ void ElfFile::modifyRPath(RPathOp op, string newRPath) continue; } + /* If --allowed-rpath-prefixes was given, reject directories + not starting with any of the (colon-delimited) prefixes. */ + if (!allowedRpathPrefixes.empty() && !hasAllowedPrefix(dirName, allowedRpathPrefixes)) { + debug("removing directory '%s' from RPATH because of non-allowed prefix\n", dirName.c_str()); + continue; + } + /* For each library that we haven't found yet, see if it exists in this directory. */ bool libFound = false; @@ -1465,6 +1479,7 @@ static bool setSoname = false; static string newSoname; static string newInterpreter; static bool shrinkRPath = false; +static vector allowedRpathPrefixes; static bool removeRPath = false; static bool setRPath = false; static bool printRPath = false; @@ -1493,14 +1508,14 @@ static void patchElf2(ElfFile & elfFile) elfFile.setInterpreter(newInterpreter); if (printRPath) - elfFile.modifyRPath(elfFile.rpPrint, ""); + elfFile.modifyRPath(elfFile.rpPrint, vector(), ""); if (shrinkRPath) - elfFile.modifyRPath(elfFile.rpShrink, ""); + elfFile.modifyRPath(elfFile.rpShrink, allowedRpathPrefixes, ""); else if (removeRPath) - elfFile.modifyRPath(elfFile.rpRemove, ""); + elfFile.modifyRPath(elfFile.rpRemove, vector(), ""); else if (setRPath) - elfFile.modifyRPath(elfFile.rpSet, newRPath); + elfFile.modifyRPath(elfFile.rpSet, vector(), newRPath); if (printNeeded) elfFile.printNeededLibs(); @@ -1563,6 +1578,7 @@ void showHelp(const string & progName) [--set-rpath RPATH]\n\ [--remove-rpath]\n\ [--shrink-rpath]\n\ + [--allowed-rpath-prefixes PREFIXES]\t\tWith '--shrink-rpath', reject rpath entries not starting with the allowed prefix\n\ [--print-rpath]\n\ [--force-rpath]\n\ [--add-needed LIBRARY]\n\ @@ -1614,6 +1630,10 @@ int main(int argc, char * * argv) else if (arg == "--shrink-rpath") { shrinkRPath = true; } + else if (arg == "--allowed-rpath-prefixes") { + if (++i == argc) error("missing argument"); + allowedRpathPrefixes = splitColonDelimitedString(argv[i]); + } else if (arg == "--set-rpath") { if (++i == argc) error("missing argument"); setRPath = true; diff --git a/tests/Makefile.am b/tests/Makefile.am index 4290fe3..32218e8 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -21,7 +21,7 @@ no_rpath_arch_TESTS = \ src_TESTS = \ plain-fail.sh plain-run.sh shrink-rpath.sh set-interpreter-short.sh \ set-interpreter-long.sh set-rpath.sh no-rpath.sh big-dynstr.sh \ - set-rpath-library.sh soname.sh + set-rpath-library.sh soname.sh shrink-rpath-with-allowed-prefixes.sh build_TESTS = \ $(no_rpath_arch_TESTS) diff --git a/tests/shrink-rpath-with-allowed-prefixes.sh b/tests/shrink-rpath-with-allowed-prefixes.sh new file mode 100755 index 0000000..db24da2 --- /dev/null +++ b/tests/shrink-rpath-with-allowed-prefixes.sh @@ -0,0 +1,47 @@ +#! /bin/sh -e +SCRATCH=scratch/$(basename $0 .sh) + +rm -rf ${SCRATCH} +mkdir -p ${SCRATCH} +mkdir -p ${SCRATCH}/libsA +mkdir -p ${SCRATCH}/libsB + +cp main ${SCRATCH}/ +cp libfoo.so libbar.so ${SCRATCH}/libsA/ +cp libfoo.so libbar.so ${SCRATCH}/libsB/ + +oldRPath=$(../src/patchelf --print-rpath ${SCRATCH}/main) +if test -z "$oldRPath"; then oldRPath="/oops"; fi +pathA="$(pwd)/${SCRATCH}/libsA" +pathB="$(pwd)/${SCRATCH}/libsB" +../src/patchelf --force-rpath --set-rpath $oldRPath:$pathA:$pathB ${SCRATCH}/main + +cp ${SCRATCH}/main ${SCRATCH}/mainA +cp ${SCRATCH}/main ${SCRATCH}/mainB + +../src/patchelf --shrink-rpath ${SCRATCH}/main +../src/patchelf --shrink-rpath --allowed-rpath-prefixes $oldRPath:$pathA ${SCRATCH}/mainA +../src/patchelf --shrink-rpath --allowed-rpath-prefixes $oldRPath:$pathB ${SCRATCH}/mainB + +check() { + exe=$1 + mustContain=$2 + mustNotContain=$3 + + rpath=$(../src/patchelf --print-rpath $exe) + echo "RPATH of $exe after: $rpath" + + if ! echo "$rpath" | grep -q $mustContain; then + echo "RPATH didn't contain '$mustContain' when it should have" + exit 1 + fi + + if echo "$rpath" | grep -q $mustNotContain; then + echo "RPATH contained '$mustNotContain' when it shouldn't have" + exit 1 + fi +} + +check ${SCRATCH}/main $pathA $pathB +check ${SCRATCH}/mainA $pathA $pathB +check ${SCRATCH}/mainB $pathB $pathA -- cgit v0.12