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