1From 618220bfb55c875d6a4d197cb24fe632ac93ec85 Mon Sep 17 00:00:00 2001 2From: Wolfgang Grandegger <wg@grandegger.com> 3Date: Mon, 20 Feb 2017 16:29:24 +0100 4Subject: [PATCH] Add option to make the rpath relative under a specified root 5 directory 6 7Running "patchelf" with the option "--make-rpath-relative ROOTDIR" will 8modify or delete the RPATHDIRs according the following rules 9similar to Martin's patches [1] making the Buildroot toolchaing/SDK 10relocatable. 11 12RPATHDIR starts with "$ORIGIN": 13 The original build-system already took care of setting a relative 14 RPATH, resolve it and test if it's valid (does exist) 15 16RPATHDIR starts with ROOTDIR: 17 The original build-system added some absolute RPATH (absolute on 18 the build machine). Test if it's valid (does exist). 19 20ROOTDIR/RPATHDIR exists: 21 The original build-system already took care of setting an absolute 22 RPATH (absolute in the final rootfs), resolve it and test if it's 23 valid (does exist). 24 25RPATHDIR points somewhere else: 26 (can be anywhere: build trees, staging tree, host location, 27 non-existing location, etc.). Just discard such a path. 28 29The option "--no-standard-libs" will discard RPATHDIRs ROOTDIR/lib and 30ROOTDIR/usr/lib. Like "--shrink-rpath", RPATHDIRs are also discarded 31if the directories do not contain a library referenced by the 32DT_NEEDED fields. 33If the option "--relative-to-file" is given, the rpath will start 34with "$ORIGIN" making it relative to the ELF file, otherwise an 35absolute path relative to ROOTDIR will be used. 36 37A pull request for a similar patch [2] for mainline inclusion is 38pending. 39 40[1] http://lists.busybox.net/pipermail/buildroot/2016-April/159422.html 41[2] https://github.com/NixOS/patchelf/pull/118 42 43Signed-off-by: Wolfgang Grandegger <wg@grandegger.com> 44--- 45 src/patchelf.cc | 196 ++++++++++++++++++++++++++++++++++++++++++++++++++------ 46 1 file changed, 175 insertions(+), 21 deletions(-) 47 48diff --git a/src/patchelf.cc b/src/patchelf.cc 49index 1d9a772..35b4a33 100644 50--- a/src/patchelf.cc 51+++ b/src/patchelf.cc 52@@ -46,6 +46,10 @@ static bool debugMode = false; 53 54 static bool forceRPath = false; 55 56+static bool noStandardLibDirs = false; 57+ 58+static bool relativeToFile = false; 59+ 60 static string fileName; 61 static int pageSize = PAGESIZE; 62 63@@ -77,6 +81,49 @@ static unsigned int getPageSize(){ 64 return pageSize; 65 } 66 67+static bool absolutePathExists(const string & path, string & canonicalPath) 68+{ 69+ char *cpath = realpath(path.c_str(), NULL); 70+ if (cpath) { 71+ canonicalPath = cpath; 72+ free(cpath); 73+ return true; 74+ } else { 75+ return false; 76+ } 77+} 78+ 79+static string makePathRelative(const string & path, 80+ const string & refPath) 81+{ 82+ string relPath = "$ORIGIN"; 83+ string p = path, refP = refPath; 84+ size_t pos; 85+ 86+ /* Strip the common part of path and refPath */ 87+ while (true) { 88+ pos = p.find_first_of('/', 1); 89+ if (refP.find_first_of('/', 1) != pos) 90+ break; 91+ if (p.substr(0, pos) != refP.substr(0, pos)) 92+ break; 93+ if (pos == string::npos) 94+ break; 95+ p = p.substr(pos); 96+ refP = refP.substr(pos); 97+ } 98+ /* Check if both pathes are equal */ 99+ if (p != refP) { 100+ pos = 0; 101+ while (pos != string::npos) { 102+ pos =refP.find_first_of('/', pos + 1); 103+ relPath.append("/.."); 104+ } 105+ relPath.append(p); 106+ } 107+ 108+ return relPath; 109+} 110 111 template<ElfFileParams> 112 class ElfFile 113@@ -183,9 +230,13 @@ public: 114 115 void setInterpreter(const string & newInterpreter); 116 117- typedef enum { rpPrint, rpShrink, rpSet, rpRemove } RPathOp; 118+ typedef enum { rpPrint, rpShrink, rpMakeRelative, rpSet, rpRemove} RPathOp; 119+ 120+ bool libFoundInRPath(const string & dirName, 121+ const vector<string> neededLibs, 122+ vector<bool> & neededLibFound); 123 124- void modifyRPath(RPathOp op, string newRPath); 125+ void modifyRPath(RPathOp op, string rootDir, string newRPath); 126 127 void addNeeded(set<string> libs); 128 129@@ -1041,7 +1092,27 @@ static void concatToRPath(string & rpath, const string & path) 130 131 132 template<ElfFileParams> 133-void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op, string newRPath) 134+bool ElfFile<ElfFileParamNames>::libFoundInRPath(const string & dirName, 135+ const vector<string> neededLibs, vector<bool> & neededLibFound) 136+{ 137+ /* For each library that we haven't found yet, see if it 138+ exists in this directory. */ 139+ bool libFound = false; 140+ for (unsigned int j = 0; j < neededLibs.size(); ++j) 141+ if (!neededLibFound[j]) { 142+ string libName = dirName + "/" + neededLibs[j]; 143+ struct stat st; 144+ if (stat(libName.c_str(), &st) == 0) { 145+ neededLibFound[j] = true; 146+ libFound = true; 147+ } 148+ } 149+ return libFound; 150+} 151+ 152+ 153+template<ElfFileParams> 154+void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op, string rootDir, string newRPath) 155 { 156 Elf_Shdr & shdrDynamic = findSection(".dynamic"); 157 158@@ -1096,6 +1167,11 @@ void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op, string newRPath) 159 return; 160 } 161 162+ if (op == rpMakeRelative && !rpath) { 163+ debug("no RPATH to make relative\n"); 164+ return; 165+ } 166+ 167 if (op == rpShrink && !rpath) { 168 debug("no RPATH to shrink\n"); 169 return; 170@@ -1120,26 +1196,80 @@ void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op, string newRPath) 171 continue; 172 } 173 174- /* For each library that we haven't found yet, see if it 175- exists in this directory. */ 176- bool libFound = false; 177- for (unsigned int j = 0; j < neededLibs.size(); ++j) 178- if (!neededLibFound[j]) { 179- string libName = dirName + "/" + neededLibs[j]; 180- struct stat st; 181- if (stat(libName.c_str(), &st) == 0) { 182- neededLibFound[j] = true; 183- libFound = true; 184- } 185- } 186- 187- if (!libFound) 188+ if (!libFoundInRPath(dirName, neededLibs, neededLibFound)) 189 debug("removing directory `%s' from RPATH\n", dirName.c_str()); 190 else 191 concatToRPath(newRPath, dirName); 192 } 193 } 194 195+ /* Make the the RPATH relative to the specified path */ 196+ if (op == rpMakeRelative) { 197+ vector<bool> neededLibFound(neededLibs.size(), false); 198+ string fileDir = fileName.substr(0, fileName.find_last_of("/")); 199+ 200+ newRPath = ""; 201+ 202+ vector<string> rpathDirs = splitColonDelimitedString(rpath); 203+ for (vector<string>::iterator it = rpathDirs.begin(); it != rpathDirs.end(); ++it) { 204+ const string & dirName = *it; 205+ 206+ string canonicalPath; 207+ 208+ /* Figure out if we should keep or discard the path. There are several 209+ cases to be handled: 210+ "dirName" starts with "$ORIGIN": 211+ The original build-system already took care of setting a relative 212+ RPATH. Resolve it and test if it's valid (does exist). 213+ "dirName" start with "rootDir": 214+ The original build-system added some absolute RPATH (absolute on 215+ the build machine). Test if it's valid (does exist). 216+ "rootDir"/"dirName" exists: 217+ The original build-system already took care of setting an absolute 218+ RPATH (absolute in the final rootfs). Resolve it and test if it's 219+ valid (does exist). 220+ "dirName" points somewhere else: 221+ (can be anywhere: build trees, staging tree, host location, 222+ non-existing location, etc.). Just discard such a path. */ 223+ if (!dirName.compare(0, 7, "$ORIGIN")) { 224+ string path = fileDir + dirName.substr(7); 225+ if (!absolutePathExists(path, canonicalPath)) { 226+ debug("removing directory '%s' from RPATH because '%s' doesn't exist\n", 227+ dirName.c_str(), path.c_str()); 228+ continue; 229+ } 230+ } else if (!dirName.compare(0, rootDir.length(), rootDir)) { 231+ if (!absolutePathExists(dirName, canonicalPath)) { 232+ debug("removing directory '%s' from RPATH because it doesn't exist\n", dirName.c_str()); 233+ continue; 234+ } 235+ } else { 236+ string path = rootDir + dirName; 237+ if (!absolutePathExists(path, canonicalPath)) { 238+ debug("removing directory '%s' from RPATH because it's not in rootdir\n", 239+ dirName.c_str()); 240+ continue; 241+ } 242+ } 243+ 244+ if (noStandardLibDirs) { 245+ if (!canonicalPath.compare(rootDir + "/lib") || 246+ !canonicalPath.compare(rootDir + "/usr/lib")) { 247+ debug("removing directory '%s' from RPATH because it's a standard library directory\n", 248+ dirName.c_str()); 249+ continue; 250+ } 251+ } 252+ 253+ /* Finally make "canonicalPath" relative to "filedir" in "rootDir" */ 254+ if (relativeToFile) 255+ concatToRPath(newRPath, makePathRelative(canonicalPath, fileDir)); 256+ else 257+ concatToRPath(newRPath, canonicalPath.substr(rootDir.length())); 258+ debug("keeping relative path of %s\n", canonicalPath.c_str()); 259+ } 260+ } 261+ 262 if (op == rpRemove) { 263 if (!rpath) { 264 debug("no RPATH to delete\n"); 265@@ -1413,7 +1543,9 @@ static bool shrinkRPath = false; 266 static bool removeRPath = false; 267 static bool setRPath = false; 268 static bool printRPath = false; 269+static bool makeRPathRelative = false; 270 static string newRPath; 271+static string rootDir; 272 static set<string> neededLibsToRemove; 273 static map<string, string> neededLibsToReplace; 274 static set<string> neededLibsToAdd; 275@@ -1438,14 +1570,16 @@ static void patchElf2(ElfFile & elfFile) 276 elfFile.setInterpreter(newInterpreter); 277 278 if (printRPath) 279- elfFile.modifyRPath(elfFile.rpPrint, ""); 280+ elfFile.modifyRPath(elfFile.rpPrint, "", ""); 281 282 if (shrinkRPath) 283- elfFile.modifyRPath(elfFile.rpShrink, ""); 284+ elfFile.modifyRPath(elfFile.rpShrink, "", ""); 285 else if (removeRPath) 286- elfFile.modifyRPath(elfFile.rpRemove, ""); 287+ elfFile.modifyRPath(elfFile.rpRemove, "", ""); 288 else if (setRPath) 289- elfFile.modifyRPath(elfFile.rpSet, newRPath); 290+ elfFile.modifyRPath(elfFile.rpSet, "", newRPath); 291+ else if (makeRPathRelative) 292+ elfFile.modifyRPath(elfFile.rpMakeRelative, rootDir, ""); 293 294 if (printNeeded) elfFile.printNeededLibs(); 295 296@@ -1508,6 +1642,9 @@ void showHelp(const string & progName) 297 [--set-rpath RPATH]\n\ 298 [--remove-rpath]\n\ 299 [--shrink-rpath]\n\ 300+ [--make-rpath-relative ROOTDIR]\n\ 301+ [--no-standard-lib-dirs]\n\ 302+ [--relative-to-file]\n\ 303 [--print-rpath]\n\ 304 [--force-rpath]\n\ 305 [--add-needed LIBRARY]\n\ 306@@ -1564,6 +1701,17 @@ int main(int argc, char * * argv) 307 setRPath = true; 308 newRPath = argv[i]; 309 } 310+ else if (arg == "--make-rpath-relative") { 311+ if (++i == argc) error("missing argument to --make-rpath-relative"); 312+ makeRPathRelative = true; 313+ rootDir = argv[i]; 314+ } 315+ else if (arg == "--no-standard-lib-dirs") { 316+ noStandardLibDirs = true; 317+ } 318+ else if (arg == "--relative-to-file") { 319+ relativeToFile = true; 320+ } 321 else if (arg == "--print-rpath") { 322 printRPath = true; 323 } 324-- 3251.9.1 326 327