From 6e8915572db65cf63b7a82f9b24af6f9cad92ba7 Mon Sep 17 00:00:00 2001 From: Wolfgang Grandegger Date: Mon, 20 Feb 2017 16:29:24 +0100 Subject: [PATCH] Add option to make the rpath relative under a specified root directory Running "patchelf" with the option "--make-rpath-relative ROOTDIR" will modify or delete the RPATHDIRs according the following rules similar to Martin's patches [1] making the Buildroot toolchaing/SDK relocatable. RPATHDIR starts with "$ORIGIN": The original build-system already took care of setting a relative RPATH, resolve it and test if it's valid (does exist) RPATHDIR starts with ROOTDIR: The original build-system added some absolute RPATH (absolute on the build machine). Test if it's valid (does exist). ROOTDIR/RPATHDIR exists: The original build-system already took care of setting an absolute RPATH (absolute in the final rootfs), resolve it and test if it's valid (does exist). RPATHDIR points somewhere else: (can be anywhere: build trees, staging tree, host location, non-existing location, etc.). Just discard such a path. The option "--no-standard-libs" will discard RPATHDIRs ROOTDIR/lib and ROOTDIR/usr/lib. Like "--shrink-rpath", RPATHDIRs are also discarded if the directories do not contain a library referenced by the DT_NEEDED fields. If the option "--relative-to-file" is given, the rpath will start with "$ORIGIN" making it relative to the ELF file, otherwise an absolute path relative to ROOTDIR will be used. A pull request for a similar patch [2] for mainline inclusion is pending. [1] http://lists.busybox.net/pipermail/buildroot/2016-April/159422.html [2] https://github.com/NixOS/patchelf/pull/118 Signed-off-by: Wolfgang Grandegger [Fabrice: update for 0.13] Signed-off-by: Fabrice Fontaine [Dario: make the patch to be applied with fuzz factor 0] Signed-off-by: Dario Binacchi --- src/patchelf.cc | 199 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 171 insertions(+), 28 deletions(-) diff --git a/src/patchelf.cc b/src/patchelf.cc index fd1e7b7b61c3..f3b7ba8867bf 100644 --- a/src/patchelf.cc +++ b/src/patchelf.cc @@ -45,6 +45,10 @@ static bool debugMode = false; static bool forceRPath = false; +static bool noStandardLibDirs = false; + +static bool relativeToFile = false; + static std::vector fileNames; static std::string outputFileName; static bool alwaysWrite = false; @@ -81,6 +85,49 @@ static bool hasAllowedPrefix(const std::string & s, const std::vector class ElfFile @@ -197,9 +244,13 @@ public: void setInterpreter(const std::string & newInterpreter); - typedef enum { rpPrint, rpShrink, rpSet, rpAdd, rpRemove } RPathOp; + typedef enum { rpPrint, rpShrink, rpMakeRelative, rpSet, rpAdd, rpRemove} RPathOp; - void modifyRPath(RPathOp op, const std::vector & allowedRpathPrefixes, std::string newRPath); + bool libFoundInRPath(const std::string & dirName, + const std::vector neededLibs, + std::vector & neededLibFound); + + void modifyRPath(RPathOp op, std::string rootDir, const std::vector & allowedRpathPrefixes, std::string newRPath, const std::string & fileName); void addNeeded(const std::set & libs); @@ -1267,8 +1318,28 @@ static void concatToRPath(std::string & rpath, const std::string & path) template -void ElfFile::modifyRPath(RPathOp op, - const std::vector & allowedRpathPrefixes, std::string newRPath) +bool ElfFile::libFoundInRPath(const std::string & dirName, + const std::vector neededLibs, std::vector & neededLibFound) +{ + /* For each library that we haven't found yet, see if it + exists in this directory. */ + bool libFound = false; + for (unsigned int j = 0; j < neededLibs.size(); ++j) + if (!neededLibFound[j]) { + std::string libName = dirName + "/" + neededLibs[j]; + struct stat st; + if (stat(libName.c_str(), &st) == 0) { + neededLibFound[j] = true; + libFound = true; + } + } + return libFound; +} + + +template +void ElfFile::modifyRPath(RPathOp op, std::string rootDir, + const std::vector & allowedRpathPrefixes, std::string newRPath, const std::string & fileName) { auto shdrDynamic = findSection(".dynamic"); @@ -1314,6 +1385,11 @@ void ElfFile::modifyRPath(RPathOp op, return; } + if (op == rpMakeRelative && !rpath) { + debug("no RPATH to make relative\n"); + return; + } + if (op == rpShrink && !rpath) { debug("no RPATH to shrink\n"); return; @@ -1343,31 +1419,80 @@ void ElfFile::modifyRPath(RPathOp op, continue; } - /* For each library that we haven't found yet, see if it - exists in this directory. */ - bool libFound = false; - for (unsigned int j = 0; j < neededLibs.size(); ++j) - if (!neededLibFound[j]) { - std::string libName = dirName + "/" + neededLibs[j]; - try { - Elf32_Half library_e_machine = getElfType(readFile(libName, sizeof(Elf32_Ehdr))).machine; - if (rdi(library_e_machine) == rdi(hdr->e_machine)) { - neededLibFound[j] = true; - libFound = true; - } else - debug("ignoring library '%s' because its machine type differs\n", libName.c_str()); - } catch (SysError & e) { - if (e.errNo != ENOENT) throw; - } - } - - if (!libFound) + if (!libFoundInRPath(dirName, neededLibs, neededLibFound)) debug("removing directory '%s' from RPATH\n", dirName.c_str()); else concatToRPath(newRPath, dirName); } } + /* Make the the RPATH relative to the specified path */ + if (op == rpMakeRelative) { + std::vector neededLibFound(neededLibs.size(), false); + std::string fileDir = fileName.substr(0, fileName.find_last_of("/")); + + newRPath = ""; + + std::vector rpathDirs = splitColonDelimitedString(rpath); + for (std::vector::iterator it = rpathDirs.begin(); it != rpathDirs.end(); ++it) { + const std::string & dirName = *it; + + std::string canonicalPath; + + /* Figure out if we should keep or discard the path. There are several + cases to be handled: + "dirName" starts with "$ORIGIN": + The original build-system already took care of setting a relative + RPATH. Resolve it and test if it's valid (does exist). + "dirName" start with "rootDir": + The original build-system added some absolute RPATH (absolute on + the build machine). Test if it's valid (does exist). + "rootDir"/"dirName" exists: + The original build-system already took care of setting an absolute + RPATH (absolute in the final rootfs). Resolve it and test if it's + valid (does exist). + "dirName" points somewhere else: + (can be anywhere: build trees, staging tree, host location, + non-existing location, etc.). Just discard such a path. */ + if (!dirName.compare(0, 7, "$ORIGIN")) { + std::string path = fileDir + dirName.substr(7); + if (!absolutePathExists(path, canonicalPath)) { + debug("removing directory '%s' from RPATH because '%s' doesn't exist\n", + dirName.c_str(), path.c_str()); + continue; + } + } else if (!dirName.compare(0, rootDir.length(), rootDir)) { + if (!absolutePathExists(dirName, canonicalPath)) { + debug("removing directory '%s' from RPATH because it doesn't exist\n", dirName.c_str()); + continue; + } + } else { + std::string path = rootDir + dirName; + if (!absolutePathExists(path, canonicalPath)) { + debug("removing directory '%s' from RPATH because it's not in rootdir\n", + dirName.c_str()); + continue; + } + } + + if (noStandardLibDirs) { + if (!canonicalPath.compare(rootDir + "/lib") || + !canonicalPath.compare(rootDir + "/usr/lib")) { + debug("removing directory '%s' from RPATH because it's a standard library directory\n", + dirName.c_str()); + continue; + } + } + + /* Finally make "canonicalPath" relative to "filedir" in "rootDir" */ + if (relativeToFile) + concatToRPath(newRPath, makePathRelative(canonicalPath, fileDir)); + else + concatToRPath(newRPath, canonicalPath.substr(rootDir.length())); + debug("keeping relative path of %s\n", canonicalPath.c_str()); + } + } + if (op == rpRemove) { if (!rpath) { debug("no RPATH to delete\n"); @@ -1736,7 +1861,9 @@ static bool removeRPath = false; static bool setRPath = false; static bool addRPath = false; static bool printRPath = false; +static bool makeRPathRelative = false; static std::string newRPath; +static std::string rootDir; static std::set neededLibsToRemove; static std::map neededLibsToReplace; static std::set neededLibsToAdd; @@ -1760,16 +1887,18 @@ static void patchElf2(ElfFile && elfFile, const FileContents & fileContents, con elfFile.setInterpreter(newInterpreter); if (printRPath) - elfFile.modifyRPath(elfFile.rpPrint, {}, ""); + elfFile.modifyRPath(elfFile.rpPrint, "", {}, "", fileName); if (shrinkRPath) - elfFile.modifyRPath(elfFile.rpShrink, allowedRpathPrefixes, ""); + elfFile.modifyRPath(elfFile.rpShrink, "", allowedRpathPrefixes, "", fileName); else if (removeRPath) - elfFile.modifyRPath(elfFile.rpRemove, {}, ""); + elfFile.modifyRPath(elfFile.rpRemove, "", {}, "", fileName); else if (setRPath) - elfFile.modifyRPath(elfFile.rpSet, {}, newRPath); + elfFile.modifyRPath(elfFile.rpSet, "", {}, newRPath, fileName); else if (addRPath) - elfFile.modifyRPath(elfFile.rpAdd, {}, newRPath); + elfFile.modifyRPath(elfFile.rpAdd, "", {}, newRPath, fileName); + else if (makeRPathRelative) + elfFile.modifyRPath(elfFile.rpMakeRelative, rootDir, {}, "", fileName); if (printNeeded) elfFile.printNeededLibs(); @@ -1821,6 +1950,9 @@ void showHelp(const std::string & progName) [--remove-rpath]\n\ [--shrink-rpath]\n\ [--allowed-rpath-prefixes PREFIXES]\t\tWith '--shrink-rpath', reject rpath entries not starting with the allowed prefix\n\ + [--make-rpath-relative ROOTDIR]\n\ + [--no-standard-lib-dirs]\n\ + [--relative-to-file]\n\ [--print-rpath]\n\ [--force-rpath]\n\ [--add-needed LIBRARY]\n\ @@ -1889,6 +2021,17 @@ int mainWrapped(int argc, char * * argv) addRPath = true; newRPath = argv[i]; } + else if (arg == "--make-rpath-relative") { + if (++i == argc) error("missing argument to --make-rpath-relative"); + makeRPathRelative = true; + rootDir = argv[i]; + } + else if (arg == "--no-standard-lib-dirs") { + noStandardLibDirs = true; + } + else if (arg == "--relative-to-file") { + relativeToFile = true; + } else if (arg == "--print-rpath") { printRPath = true; } -- 2.43.0