@@ -44,12 +44,15 @@ static bool debugMode = false;
44
44
45
45
static bool forceRPath = false ;
46
46
47
+ static bool noStandardLibDirs = false ;
48
+
49
+ static bool relativeToFile = false ;
50
+
47
51
static std::string fileName;
48
52
static int pageSize = PAGESIZE;
49
53
50
54
typedef std::shared_ptr<std::vector<unsigned char >> FileContents;
51
55
52
-
53
56
#define ElfFileParams class Elf_Ehdr , class Elf_Phdr , class Elf_Shdr , class Elf_Addr , class Elf_Off , class Elf_Dyn , class Elf_Sym , class Elf_Verneed
54
57
#define ElfFileParamNames Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Addr, Elf_Off, Elf_Dyn, Elf_Sym, Elf_Verneed
55
58
@@ -83,6 +86,49 @@ static unsigned int getPageSize()
83
86
return pageSize;
84
87
}
85
88
89
+ static bool absolutePathExists (const std::string & path, std::string & canonicalPath)
90
+ {
91
+ char *cpath = realpath (path.c_str (), NULL );
92
+ if (cpath) {
93
+ canonicalPath = cpath;
94
+ free (cpath);
95
+ return true ;
96
+ } else {
97
+ return false ;
98
+ }
99
+ }
100
+
101
+ static std::string makePathRelative (const std::string & path,
102
+ const std::string & refPath)
103
+ {
104
+ std::string relPath = " $ORIGIN" ;
105
+ std::string p = path, refP = refPath;
106
+ std::size_t pos;
107
+
108
+ /* Strip the common part of path and refPath */
109
+ while (true ) {
110
+ pos = p.find_first_of (' /' , 1 );
111
+ if (refP.find_first_of (' /' , 1 ) != pos)
112
+ break ;
113
+ if (p.substr (0 , pos) != refP.substr (0 , pos))
114
+ break ;
115
+ if (pos == std::string::npos)
116
+ break ;
117
+ p = p.substr (pos);
118
+ refP = refP.substr (pos);
119
+ }
120
+ /* Check if both pathes are equal */
121
+ if (p != refP) {
122
+ pos = 0 ;
123
+ while (pos != std::string::npos) {
124
+ pos =refP.find_first_of (' /' , pos + 1 );
125
+ relPath.append (" /.." );
126
+ }
127
+ relPath.append (p);
128
+ }
129
+
130
+ return relPath;
131
+ }
86
132
87
133
template <ElfFileParams>
88
134
class ElfFile
@@ -191,9 +237,15 @@ class ElfFile
191
237
192
238
void setInterpreter (const std::string & newInterpreter);
193
239
194
- typedef enum { rpPrint, rpShrink, rpSet, rpRemove } RPathOp;
240
+ typedef enum { rpPrint, rpShrink, rpMakeRelative, rpSet, rpRemove} RPathOp;
241
+
242
+ bool libFoundInRPath (const std::string & dirName,
243
+ const std::vector<std::string> neededLibs,
244
+ std::vector<bool > & neededLibFound);
195
245
196
- void modifyRPath (RPathOp op, const std::vector<std::string> & allowedRpathPrefixes, std::string newRPath);
246
+ void modifyRPath (RPathOp op,
247
+ const std::vector<std::string> & allowedRpathPrefixes,
248
+ std::string rootDir, std::string newRPath);
197
249
198
250
void addNeeded (const std::set<std::string> & libs);
199
251
@@ -1099,10 +1151,33 @@ static void concatToRPath(std::string & rpath, const std::string & path)
1099
1151
rpath += path;
1100
1152
}
1101
1153
1154
+ template <ElfFileParams>
1155
+ bool ElfFile<ElfFileParamNames>::libFoundInRPath(const std::string & dirName,
1156
+ const std::vector<std::string> neededLibs, std::vector<bool > & neededLibFound)
1157
+ {
1158
+ /* For each library that we haven't found yet, see if it
1159
+ exists in this directory. */
1160
+ bool libFound = false ;
1161
+ for (unsigned int j = 0 ; j < neededLibs.size (); ++j)
1162
+ if (!neededLibFound[j]) {
1163
+ std::string libName = dirName + " /" + neededLibs[j];
1164
+ try {
1165
+ if (getElfType (readFile (libName, sizeof (Elf32_Ehdr))).machine == rdi (hdr->e_machine )) {
1166
+ neededLibFound[j] = true ;
1167
+ libFound = true ;
1168
+ } else
1169
+ debug (" ignoring library '%s' because its machine type differs\n " , libName.c_str ());
1170
+ } catch (SysError & e) {
1171
+ if (e.errNo != ENOENT) throw ;
1172
+ }
1173
+ }
1174
+ return libFound;
1175
+ }
1102
1176
1103
1177
template <ElfFileParams>
1104
1178
void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op,
1105
- const std::vector<std::string> & allowedRpathPrefixes, std::string newRPath)
1179
+ const std::vector<std::string> & allowedRpathPrefixes,
1180
+ std::string rootDir, std::string newRPath)
1106
1181
{
1107
1182
Elf_Shdr & shdrDynamic = findSection (" .dynamic" );
1108
1183
@@ -1153,6 +1228,10 @@ void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op,
1153
1228
return ;
1154
1229
}
1155
1230
1231
+ if (op == rpMakeRelative && !rpath) {
1232
+ debug (" no RPATH to make relative\n " );
1233
+ return ;
1234
+ }
1156
1235
1157
1236
/* For each directory in the RPATH, check if it contains any
1158
1237
needed library. */
@@ -1177,27 +1256,79 @@ void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op,
1177
1256
continue ;
1178
1257
}
1179
1258
1180
- /* For each library that we haven't found yet, see if it
1181
- exists in this directory. */
1182
- bool libFound = false ;
1183
- for (unsigned int j = 0 ; j < neededLibs.size (); ++j)
1184
- if (!neededLibFound[j]) {
1185
- std::string libName = dirName + " /" + neededLibs[j];
1186
- try {
1187
- if (getElfType (readFile (libName, sizeof (Elf32_Ehdr))).machine == rdi (hdr->e_machine )) {
1188
- neededLibFound[j] = true ;
1189
- libFound = true ;
1190
- } else
1191
- debug (" ignoring library '%s' because its machine type differs\n " , libName.c_str ());
1192
- } catch (SysError & e) {
1193
- if (e.errNo != ENOENT) throw ;
1194
- }
1259
+ if (!libFoundInRPath (dirName, neededLibs, neededLibFound))
1260
+ debug (" removing directory '%s' from RPATH\n " , dirName.c_str ());
1261
+ else
1262
+ concatToRPath (newRPath, dirName);
1263
+ }
1264
+ }
1265
+
1266
+ /* Make the the RPATH relative to the specified path */
1267
+ if (op == rpMakeRelative) {
1268
+ std::vector<bool > neededLibFound (neededLibs.size (), false );
1269
+ std::string fileDir = fileName.substr (0 , fileName.find_last_of (" /" ));
1270
+ newRPath = " " ;
1271
+
1272
+ for (auto & dirName : splitColonDelimitedString (rpath)) {
1273
+ std::string canonicalPath;
1274
+ std::string path;
1275
+
1276
+ /* Figure out if we should keep or discard the path; there are several
1277
+ cases to handled:
1278
+ "dirName" starts with "$ORIGIN":
1279
+ The original build-system already took care of setting a relative
1280
+ RPATH, resolve it and test if it is worthwhile to keep it.
1281
+ "dirName" start with "rootDir":
1282
+ The original build-system added some absolute RPATH (absolute on
1283
+ the build machine). While this is wrong, it can still be fixed; so
1284
+ test if it is worthwhile to keep it.
1285
+ "rootDir"/"dirName" exists:
1286
+ The original build-system already took care of setting an absolute
1287
+ RPATH (absolute in the final rootfs), resolve it and test if it is
1288
+ worthwhile to keep it;
1289
+ "dirName" points somewhere else:
1290
+ (can be anywhere: build trees, staging tree, host location,
1291
+ non-existing location, etc.). Just discard such a path. */
1292
+ if (!dirName.compare (0 , 7 , " $ORIGIN" )) {
1293
+ path = fileDir + dirName.substr (7 );
1294
+ if (!absolutePathExists (path, canonicalPath)) {
1295
+ debug (" removing directory '%s' from RPATH because it doesn't exist\n " , dirName.c_str ());
1296
+ continue ;
1297
+ }
1298
+ } else if (!dirName.compare (0 , rootDir.length (), rootDir)) {
1299
+ if (!absolutePathExists (dirName, canonicalPath)) {
1300
+ debug (" removing directory '%s' from RPATH because it doesn't exist\n " , dirName.c_str ());
1301
+ continue ;
1302
+ }
1303
+ } else {
1304
+ path = rootDir + dirName;
1305
+ if (!absolutePathExists (path, canonicalPath)) {
1306
+ debug (" removing directory '%s' from RPATH because it's not under the root directory\n " ,
1307
+ dirName.c_str ());
1308
+ continue ;
1195
1309
}
1310
+ }
1311
+
1312
+ if (noStandardLibDirs) {
1313
+ if (!canonicalPath.compare (rootDir + " /lib" ) ||
1314
+ !canonicalPath.compare (rootDir + " /usr/lib" )) {
1315
+ debug (" removing directory '%s' from RPATH because it's a standard library directory\n " ,
1316
+ dirName.c_str ());
1317
+ continue ;
1318
+ }
1319
+ }
1196
1320
1197
- if (!libFound)
1321
+ if (!libFoundInRPath (canonicalPath, neededLibs, neededLibFound)) {
1198
1322
debug (" removing directory '%s' from RPATH\n " , dirName.c_str ());
1323
+ continue ;
1324
+ }
1325
+
1326
+ /* Finally make "canonicalPath" relative to "filedir" in "rootDir" */
1327
+ if (relativeToFile)
1328
+ concatToRPath (newRPath, makePathRelative (canonicalPath, fileDir));
1199
1329
else
1200
- concatToRPath (newRPath, dirName);
1330
+ concatToRPath (newRPath, canonicalPath.substr (rootDir.length ()));
1331
+ debug (" keeping relative path of %s\n " , canonicalPath.c_str ());
1201
1332
}
1202
1333
}
1203
1334
@@ -1528,7 +1659,9 @@ static std::vector<std::string> allowedRpathPrefixes;
1528
1659
static bool removeRPath = false ;
1529
1660
static bool setRPath = false ;
1530
1661
static bool printRPath = false ;
1662
+ static bool makeRPathRelative = false ;
1531
1663
static std::string newRPath;
1664
+ static std::string rootDir;
1532
1665
static std::set<std::string> neededLibsToRemove;
1533
1666
static std::map<std::string, std::string> neededLibsToReplace;
1534
1667
static std::set<std::string> neededLibsToAdd;
@@ -1551,14 +1684,16 @@ static void patchElf2(ElfFile && elfFile)
1551
1684
elfFile.setInterpreter (newInterpreter);
1552
1685
1553
1686
if (printRPath)
1554
- elfFile.modifyRPath (elfFile.rpPrint , {}, " " );
1687
+ elfFile.modifyRPath (elfFile.rpPrint , {}, {}, " " );
1555
1688
1556
1689
if (shrinkRPath)
1557
- elfFile.modifyRPath (elfFile.rpShrink , allowedRpathPrefixes, " " );
1690
+ elfFile.modifyRPath (elfFile.rpShrink , allowedRpathPrefixes, " " , " " );
1558
1691
else if (removeRPath)
1559
- elfFile.modifyRPath (elfFile.rpRemove , {}, " " );
1692
+ elfFile.modifyRPath (elfFile.rpRemove , {}, " " , " " );
1560
1693
else if (setRPath)
1561
- elfFile.modifyRPath (elfFile.rpSet , {}, newRPath);
1694
+ elfFile.modifyRPath (elfFile.rpSet , {}, " " , newRPath);
1695
+ else if (makeRPathRelative)
1696
+ elfFile.modifyRPath (elfFile.rpMakeRelative , {}, rootDir, " " );
1562
1697
1563
1698
if (printNeeded) elfFile.printNeededLibs ();
1564
1699
@@ -1604,6 +1739,9 @@ void showHelp(const std::string & progName)
1604
1739
[--remove-rpath]\n \
1605
1740
[--shrink-rpath]\n \
1606
1741
[--allowed-rpath-prefixes PREFIXES]\t\t With '--shrink-rpath', reject rpath entries not starting with the allowed prefix\n \
1742
+ [--make-rpath-relative ROOTDIR]\n \
1743
+ [--no-standard-lib-dirs]\n \
1744
+ [--relative-to-file]\n \
1607
1745
[--print-rpath]\n \
1608
1746
[--force-rpath]\n \
1609
1747
[--add-needed LIBRARY]\n \
@@ -1664,6 +1802,17 @@ int mainWrapped(int argc, char * * argv)
1664
1802
setRPath = true ;
1665
1803
newRPath = argv[i];
1666
1804
}
1805
+ else if (arg == " --make-rpath-relative" ) {
1806
+ if (++i == argc) error (" missing argument to --make-rpath-relative" );
1807
+ makeRPathRelative = true ;
1808
+ rootDir = argv[i];
1809
+ }
1810
+ else if (arg == " --no-standard-lib-dirs" ) {
1811
+ noStandardLibDirs = true ;
1812
+ }
1813
+ else if (arg == " --relative-to-file" ) {
1814
+ relativeToFile = true ;
1815
+ }
1667
1816
else if (arg == " --print-rpath" ) {
1668
1817
printRPath = true ;
1669
1818
}
0 commit comments