| From 618220bfb55c875d6a4d197cb24fe632ac93ec85 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> |
| --- |
| src/patchelf.cc | 196 ++++++++++++++++++++++++++++++++++++++++++++++++++------ |
| 1 file changed, 175 insertions(+), 21 deletions(-) |
| |
| diff --git a/src/patchelf.cc b/src/patchelf.cc |
| index 1d9a772..35b4a33 100644 |
| --- a/src/patchelf.cc |
| +++ b/src/patchelf.cc |
| @@ -46,6 +46,10 @@ static bool debugMode = false; |
| |
| static bool forceRPath = false; |
| |
| +static bool noStandardLibDirs = false; |
| + |
| +static bool relativeToFile = false; |
| + |
| static string fileName; |
| static int pageSize = PAGESIZE; |
| |
| @@ -77,6 +81,49 @@ static unsigned int getPageSize(){ |
| return pageSize; |
| } |
| |
| +static bool absolutePathExists(const string & path, string & canonicalPath) |
| +{ |
| + char *cpath = realpath(path.c_str(), NULL); |
| + if (cpath) { |
| + canonicalPath = cpath; |
| + free(cpath); |
| + return true; |
| + } else { |
| + return false; |
| + } |
| +} |
| + |
| +static string makePathRelative(const string & path, |
| + const string & refPath) |
| +{ |
| + string relPath = "$ORIGIN"; |
| + 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 == string::npos) |
| + break; |
| + p = p.substr(pos); |
| + refP = refP.substr(pos); |
| + } |
| + /* Check if both pathes are equal */ |
| + if (p != refP) { |
| + pos = 0; |
| + while (pos != string::npos) { |
| + pos =refP.find_first_of('/', pos + 1); |
| + relPath.append("/.."); |
| + } |
| + relPath.append(p); |
| + } |
| + |
| + return relPath; |
| +} |
| |
| template<ElfFileParams> |
| class ElfFile |
| @@ -183,9 +230,13 @@ public: |
| |
| void setInterpreter(const string & newInterpreter); |
| |
| - typedef enum { rpPrint, rpShrink, rpSet, rpRemove } RPathOp; |
| + typedef enum { rpPrint, rpShrink, rpMakeRelative, rpSet, rpRemove} RPathOp; |
| + |
| + bool libFoundInRPath(const string & dirName, |
| + const vector<string> neededLibs, |
| + vector<bool> & neededLibFound); |
| |
| - void modifyRPath(RPathOp op, string newRPath); |
| + void modifyRPath(RPathOp op, string rootDir, string newRPath); |
| |
| void addNeeded(set<string> libs); |
| |
| @@ -1041,7 +1092,27 @@ static void concatToRPath(string & rpath, const string & path) |
| |
| |
| template<ElfFileParams> |
| -void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op, string newRPath) |
| +bool ElfFile<ElfFileParamNames>::libFoundInRPath(const string & dirName, |
| + const vector<string> neededLibs, 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]) { |
| + 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, string rootDir, string newRPath) |
| { |
| Elf_Shdr & shdrDynamic = findSection(".dynamic"); |
| |
| @@ -1096,6 +1167,11 @@ void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op, string newRPath) |
| return; |
| } |
| |
| + if (op == rpMakeRelative && !rpath) { |
| + debug("no RPATH to make relative\n"); |
| + return; |
| + } |
| + |
| if (op == rpShrink && !rpath) { |
| debug("no RPATH to shrink\n"); |
| return; |
| @@ -1120,26 +1196,80 @@ void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op, string newRPath) |
| 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]) { |
| - string libName = dirName + "/" + neededLibs[j]; |
| - struct stat st; |
| - if (stat(libName.c_str(), &st) == 0) { |
| - neededLibFound[j] = true; |
| - libFound = true; |
| - } |
| - } |
| - |
| - 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) { |
| + vector<bool> neededLibFound(neededLibs.size(), false); |
| + string fileDir = fileName.substr(0, fileName.find_last_of("/")); |
| + |
| + newRPath = ""; |
| + |
| + vector<string> rpathDirs = splitColonDelimitedString(rpath); |
| + for (vector<string>::iterator it = rpathDirs.begin(); it != rpathDirs.end(); ++it) { |
| + const string & dirName = *it; |
| + |
| + 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")) { |
| + 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 { |
| + 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"); |
| @@ -1413,7 +1543,9 @@ static bool shrinkRPath = false; |
| static bool removeRPath = false; |
| static bool setRPath = false; |
| static bool printRPath = false; |
| +static bool makeRPathRelative = false; |
| static string newRPath; |
| +static string rootDir; |
| static set<string> neededLibsToRemove; |
| static map<string, string> neededLibsToReplace; |
| static set<string> neededLibsToAdd; |
| @@ -1438,14 +1570,16 @@ static void patchElf2(ElfFile & elfFile) |
| elfFile.setInterpreter(newInterpreter); |
| |
| if (printRPath) |
| - elfFile.modifyRPath(elfFile.rpPrint, ""); |
| + elfFile.modifyRPath(elfFile.rpPrint, "", ""); |
| |
| if (shrinkRPath) |
| - elfFile.modifyRPath(elfFile.rpShrink, ""); |
| + elfFile.modifyRPath(elfFile.rpShrink, "", ""); |
| else if (removeRPath) |
| - elfFile.modifyRPath(elfFile.rpRemove, ""); |
| + elfFile.modifyRPath(elfFile.rpRemove, "", ""); |
| else if (setRPath) |
| - elfFile.modifyRPath(elfFile.rpSet, newRPath); |
| + elfFile.modifyRPath(elfFile.rpSet, "", newRPath); |
| + else if (makeRPathRelative) |
| + elfFile.modifyRPath(elfFile.rpMakeRelative, rootDir, ""); |
| |
| if (printNeeded) elfFile.printNeededLibs(); |
| |
| @@ -1508,6 +1642,9 @@ void showHelp(const string & progName) |
| [--set-rpath RPATH]\n\ |
| [--remove-rpath]\n\ |
| [--shrink-rpath]\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\ |
| @@ -1564,6 +1701,17 @@ int main(int argc, char * * argv) |
| setRPath = 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; |
| } |
| -- |
| 1.9.1 |
| |