| From 6e8915572db65cf63b7a82f9b24af6f9cad92ba7 Mon Sep 17 00:00:00 2001 |
| From: Wolfgang Grandegger <wg@grandegger.com> |
| 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 <wg@grandegger.com> |
| [Fabrice: update for 0.13] |
| Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com> |
| [Dario: make the patch to be applied with fuzz factor 0] |
| Signed-off-by: Dario Binacchi <dario.binacchi@amarulasolutions.com> |
| --- |
| 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<std::string> fileNames; |
| static std::string outputFileName; |
| static bool alwaysWrite = false; |
| @@ -81,6 +85,49 @@ static bool hasAllowedPrefix(const std::string & s, const std::vector<std::strin |
| return std::any_of(allowedPrefixes.begin(), allowedPrefixes.end(), [&](const std::string & i) { return !s.compare(0, i.size(), i); }); |
| } |
| |
| +static bool absolutePathExists(const std::string & path, std::string & canonicalPath) |
| +{ |
| + char *cpath = realpath(path.c_str(), NULL); |
| + if (cpath) { |
| + canonicalPath = cpath; |
| + free(cpath); |
| + return true; |
| + } else { |
| + return false; |
| + } |
| +} |
| + |
| +static std::string makePathRelative(const std::string & path, |
| + const std::string & refPath) |
| +{ |
| + std::string relPath = "$ORIGIN"; |
| + std::string p = path, refP = refPath; |
| + size_t pos; |
| + |
| + /* Strip the common part of path and refPath */ |
| + while (true) { |
| + pos = p.find_first_of('/', 1); |
| + if (refP.find_first_of('/', 1) != pos) |
| + break; |
| + if (p.substr(0, pos) != refP.substr(0, pos)) |
| + break; |
| + if (pos == std::string::npos) |
| + break; |
| + p = p.substr(pos); |
| + refP = refP.substr(pos); |
| + } |
| + /* Check if both pathes are equal */ |
| + if (p != refP) { |
| + pos = 0; |
| + while (pos != std::string::npos) { |
| + pos =refP.find_first_of('/', pos + 1); |
| + relPath.append("/.."); |
| + } |
| + relPath.append(p); |
| + } |
| + |
| + return relPath; |
| +} |
| |
| template<ElfFileParams> |
| 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<std::string> & allowedRpathPrefixes, std::string newRPath); |
| + bool libFoundInRPath(const std::string & dirName, |
| + const std::vector<std::string> neededLibs, |
| + std::vector<bool> & neededLibFound); |
| + |
| + void modifyRPath(RPathOp op, std::string rootDir, const std::vector<std::string> & allowedRpathPrefixes, std::string newRPath, const std::string & fileName); |
| |
| void addNeeded(const std::set<std::string> & libs); |
| |
| @@ -1267,8 +1318,28 @@ static void concatToRPath(std::string & rpath, const std::string & path) |
| |
| |
| template<ElfFileParams> |
| -void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op, |
| - const std::vector<std::string> & allowedRpathPrefixes, std::string newRPath) |
| +bool ElfFile<ElfFileParamNames>::libFoundInRPath(const std::string & dirName, |
| + const std::vector<std::string> neededLibs, std::vector<bool> & 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<ElfFileParams> |
| +void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op, std::string rootDir, |
| + const std::vector<std::string> & allowedRpathPrefixes, std::string newRPath, const std::string & fileName) |
| { |
| auto shdrDynamic = findSection(".dynamic"); |
| |
| @@ -1314,6 +1385,11 @@ void ElfFile<ElfFileParamNames>::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<ElfFileParamNames>::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<bool> neededLibFound(neededLibs.size(), false); |
| + std::string fileDir = fileName.substr(0, fileName.find_last_of("/")); |
| + |
| + newRPath = ""; |
| + |
| + std::vector<std::string> rpathDirs = splitColonDelimitedString(rpath); |
| + for (std::vector<std::string>::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<std::string> neededLibsToRemove; |
| static std::map<std::string, std::string> neededLibsToReplace; |
| static std::set<std::string> 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 |
| |