Skip to content

Commit 80c4234

Browse files
committed
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: RPATHDIR starts with "$ORIGIN": The original build-system already took care of setting a relative RPATH, resolve it and test if it's valid according to the rules below. RPATHDIR starts with ROOTDIR: The original build-system added some absolute RPATH (absolute on the build machine). While this is not OK , it can still be fixed; so test if it is worthwhile to keep it. 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 worthwhile to keep it. RPATHDIR points somewhere else: (can be anywhere: build trees, staging tree, host location, non-existing location, etc.). Just discard such a path. In addition, the option "--no-standard-libs" will discard RPATHDIRs ROOTDIR/lib and ROOTDIR/usr/lib. Like "--shrink-rpath", RPATHDIRs are discarded if the directories do not contain a library referenced by DT_NEEDED fields. This option is useful to sanitize the ELF files of a target root filesystem and to help making a SDK/toolchain relocatable, e.g. of the buildroot project [1]. [1] http://lists.busybox.net/pipermail/buildroot/2016-April/159422.html
1 parent 382246e commit 80c4234

File tree

2 files changed

+166
-26
lines changed

2 files changed

+166
-26
lines changed

README

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,19 @@ libraries. In particular, it can do the following:
2727

2828
$ patchelf --shrink-rpath --allowed-rpath-prefixes /usr/lib:/foo/lib my-program
2929

30+
* Sanitize and make the RPATH relative to a specified root directory:
31+
32+
$ patchelf --make-rpath-relative rootdir my-program
33+
34+
This removes from the RPATH all directories, which are not under the
35+
specified rootdir. "$ORIGIN" and directories already relative to
36+
rootdir will be resolved first. If the option '--no-standard-lib' is
37+
given, "rootdir/lib" and "rootdir/usr/lib" directories are discarded
38+
as well. Furthermore, all directories that do not contain a library
39+
referenced by DT_NEEDED fields of the executable or library will be
40+
removed. Finally, the directory path is converted to a path relative
41+
to rootdir starting with "$ORIGIN".
42+
3043
* Remove declared dependencies on dynamic libraries (DT_NEEDED
3144
entries):
3245

src/patchelf.cc

Lines changed: 153 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ static int pageSize = PAGESIZE;
4949

5050
typedef std::shared_ptr<std::vector<unsigned char>> FileContents;
5151

52+
#define MODIFY_FLAG_NO_STD_LIB_DIRS 0x1
53+
static int modifyFlags;
5254

5355
#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
5456
#define ElfFileParamNames Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Addr, Elf_Off, Elf_Dyn, Elf_Sym, Elf_Verneed
@@ -83,6 +85,36 @@ static unsigned int getPageSize()
8385
return pageSize;
8486
}
8587

88+
static bool absolutePathExists(const std::string & path, std::string & canonicalPath)
89+
{
90+
char *cpath = realpath(path.c_str(), NULL);
91+
if (cpath) {
92+
canonicalPath = cpath;
93+
free(cpath);
94+
return true;
95+
} else {
96+
return false;
97+
}
98+
}
99+
100+
static std::string makePathRelative(const std::string & path,
101+
const std::string & refPath, const std::string & rootDir)
102+
{
103+
std::string relPath = "$ORIGIN";
104+
105+
/* Strip root path first */
106+
std::string p = path.substr(rootDir.length());
107+
std::string refP = refPath.substr(rootDir.length());
108+
109+
std::size_t pos = refP.find_first_of('/');
110+
while (pos != std::string::npos) {
111+
pos =refP.find_first_of('/', pos + 1);
112+
relPath.append("/..");
113+
}
114+
relPath.append(p);
115+
116+
return relPath;
117+
}
86118

87119
template<ElfFileParams>
88120
class ElfFile
@@ -191,9 +223,14 @@ class ElfFile
191223

192224
void setInterpreter(const std::string & newInterpreter);
193225

194-
typedef enum { rpPrint, rpShrink, rpSet, rpRemove } RPathOp;
226+
typedef enum { rpPrint, rpShrink, rpMakeRelative, rpSet, rpRemove} RPathOp;
195227

196-
void modifyRPath(RPathOp op, const std::vector<std::string> & allowedRpathPrefixes, std::string newRPath);
228+
bool libFoundInRPath(const std::string & dirName,
229+
const std::vector<std::string> neededLibs);
230+
231+
void modifyRPath(RPathOp op,
232+
const std::vector<std::string> & allowedRpathPrefixes,
233+
std::string rootDir, int flags, std::string newRPath);
197234

198235
void addNeeded(const std::set<std::string> & libs);
199236

@@ -1099,10 +1136,35 @@ static void concatToRPath(std::string & rpath, const std::string & path)
10991136
rpath += path;
11001137
}
11011138

1139+
template<ElfFileParams>
1140+
bool ElfFile<ElfFileParamNames>::libFoundInRPath(const std::string & dirName,
1141+
const std::vector<std::string> neededLibs)
1142+
{
1143+
std::vector<bool> neededLibFound(neededLibs.size(), false);
1144+
1145+
/* For each library that we haven't found yet, see if it
1146+
exists in this directory. */
1147+
bool libFound = false;
1148+
for (unsigned int j = 0; j < neededLibs.size(); ++j)
1149+
if (!neededLibFound[j]) {
1150+
std::string libName = dirName + "/" + neededLibs[j];
1151+
try {
1152+
if (getElfType(readFile(libName, sizeof(Elf32_Ehdr))).machine == rdi(hdr->e_machine)) {
1153+
neededLibFound[j] = true;
1154+
libFound = true;
1155+
} else
1156+
debug("ignoring library '%s' because its machine type differs\n", libName.c_str());
1157+
} catch (SysError & e) {
1158+
if (e.errNo != ENOENT) throw;
1159+
}
1160+
}
1161+
return libFound;
1162+
}
11021163

11031164
template<ElfFileParams>
11041165
void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op,
1105-
const std::vector<std::string> & allowedRpathPrefixes, std::string newRPath)
1166+
const std::vector<std::string> & allowedRpathPrefixes,
1167+
std::string rootDir, int flags, std::string newRPath)
11061168
{
11071169
Elf_Shdr & shdrDynamic = findSection(".dynamic");
11081170

@@ -1153,11 +1215,14 @@ void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op,
11531215
return;
11541216
}
11551217

1218+
if (op == rpMakeRelative && !rpath) {
1219+
debug("no RPATH to make relative\n");
1220+
return;
1221+
}
11561222

11571223
/* For each directory in the RPATH, check if it contains any
11581224
needed library. */
11591225
if (op == rpShrink) {
1160-
std::vector<bool> neededLibFound(neededLibs.size(), false);
11611226

11621227
newRPath = "";
11631228

@@ -1177,30 +1242,78 @@ void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op,
11771242
continue;
11781243
}
11791244

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-
}
1195-
}
1196-
1197-
if (!libFound)
1245+
if (!libFoundInRPath(dirName, neededLibs))
11981246
debug("removing directory '%s' from RPATH\n", dirName.c_str());
11991247
else
12001248
concatToRPath(newRPath, dirName);
12011249
}
12021250
}
12031251

1252+
/* Make the the RPATH relative to the specified path */
1253+
if (op == rpMakeRelative) {
1254+
std::string fileDir = fileName.substr(0, fileName.find_last_of("/"));
1255+
newRPath = "";
1256+
1257+
for (auto & dirName : splitColonDelimitedString(rpath)) {
1258+
std::string canonicalPath;
1259+
std::string path;
1260+
1261+
/* Figure out if we should keep or discard the path; there are several
1262+
cases to handled:
1263+
"dirName" starts with "$ORIGIN":
1264+
The original build-system already took care of setting a relative
1265+
RPATH, resolve it and test if it is worthwhile to keep it.
1266+
"dirName" start with "rootDir":
1267+
The original build-system added some absolute RPATH (absolute on
1268+
the build machine). While this is wrong, it can still be fixed; so
1269+
test if it is worthwhile to keep it.
1270+
"rootDir"/"dirName" exists:
1271+
The original build-system already took care of setting an absolute
1272+
RPATH (absolute in the final rootfs), resolve it and test if it is
1273+
worthwhile to keep it;
1274+
"dirName" points somewhere else:
1275+
(can be anywhere: build trees, staging tree, host location,
1276+
non-existing location, etc.). Just discard such a path. */
1277+
if (!dirName.compare(0, 7, "$ORIGIN")) {
1278+
path = fileDir + dirName.substr(7);
1279+
if (!absolutePathExists(path, canonicalPath)) {
1280+
debug("removing directory '%s' from RPATH because it doesn't exist\n", dirName.c_str());
1281+
continue;
1282+
}
1283+
} else if (!dirName.compare(0, rootDir.length(), rootDir)) {
1284+
if (!absolutePathExists(dirName, canonicalPath)) {
1285+
debug("removing directory '%s' from RPATH because it doesn't exist\n", dirName.c_str());
1286+
continue;
1287+
}
1288+
} else {
1289+
path = rootDir + dirName;
1290+
if (!absolutePathExists(path, canonicalPath)) {
1291+
debug("removing directory '%s' from RPATH because it's not under the root directory\n",
1292+
dirName.c_str());
1293+
continue;
1294+
}
1295+
}
1296+
1297+
if (flags & MODIFY_FLAG_NO_STD_LIB_DIRS) {
1298+
if (!canonicalPath.compare(rootDir + "/lib") ||
1299+
!canonicalPath.compare(rootDir + "/usr/lib")) {
1300+
debug("removing directory '%s' from RPATH because it's a standard library directory\n",
1301+
dirName.c_str());
1302+
continue;
1303+
}
1304+
}
1305+
1306+
if (!libFoundInRPath(canonicalPath, neededLibs)) {
1307+
debug("removing directory '%s' from RPATH\n", dirName.c_str());
1308+
continue;
1309+
}
1310+
1311+
/* Finally make "canonicalPath" relative to "filedir" in "rootDir" */
1312+
concatToRPath(newRPath, makePathRelative(canonicalPath, fileDir, rootDir));
1313+
debug("keeping relative path of %s\n", canonicalPath.c_str());
1314+
}
1315+
}
1316+
12041317
if (op == rpRemove) {
12051318
if (!rpath) {
12061319
debug("no RPATH to delete\n");
@@ -1528,7 +1641,9 @@ static std::vector<std::string> allowedRpathPrefixes;
15281641
static bool removeRPath = false;
15291642
static bool setRPath = false;
15301643
static bool printRPath = false;
1644+
static bool makeRPathRelative = false;
15311645
static std::string newRPath;
1646+
static std::string rootDir;
15321647
static std::set<std::string> neededLibsToRemove;
15331648
static std::map<std::string, std::string> neededLibsToReplace;
15341649
static std::set<std::string> neededLibsToAdd;
@@ -1551,14 +1666,16 @@ static void patchElf2(ElfFile && elfFile)
15511666
elfFile.setInterpreter(newInterpreter);
15521667

15531668
if (printRPath)
1554-
elfFile.modifyRPath(elfFile.rpPrint, {}, "");
1669+
elfFile.modifyRPath(elfFile.rpPrint, {}, {}, modifyFlags, "");
15551670

15561671
if (shrinkRPath)
1557-
elfFile.modifyRPath(elfFile.rpShrink, allowedRpathPrefixes, "");
1672+
elfFile.modifyRPath(elfFile.rpShrink, allowedRpathPrefixes, "", modifyFlags, "");
15581673
else if (removeRPath)
1559-
elfFile.modifyRPath(elfFile.rpRemove, {}, "");
1674+
elfFile.modifyRPath(elfFile.rpRemove, {}, "", modifyFlags, "");
15601675
else if (setRPath)
1561-
elfFile.modifyRPath(elfFile.rpSet, {}, newRPath);
1676+
elfFile.modifyRPath(elfFile.rpSet, {}, "", modifyFlags, newRPath);
1677+
else if (makeRPathRelative)
1678+
elfFile.modifyRPath(elfFile.rpMakeRelative, {}, rootDir, modifyFlags, "");
15621679

15631680
if (printNeeded) elfFile.printNeededLibs();
15641681

@@ -1604,6 +1721,8 @@ void showHelp(const std::string & progName)
16041721
[--remove-rpath]\n\
16051722
[--shrink-rpath]\n\
16061723
[--allowed-rpath-prefixes PREFIXES]\t\tWith '--shrink-rpath', reject rpath entries not starting with the allowed prefix\n\
1724+
[--make-rpath-relative ROOTDIR]\n\
1725+
[--no-standard-lib-dirs]\n\
16071726
[--print-rpath]\n\
16081727
[--force-rpath]\n\
16091728
[--add-needed LIBRARY]\n\
@@ -1664,6 +1783,14 @@ int mainWrapped(int argc, char * * argv)
16641783
setRPath = true;
16651784
newRPath = argv[i];
16661785
}
1786+
else if (arg == "--make-rpath-relative") {
1787+
if (++i == argc) error("missing argument to --make-rpath-relative");
1788+
makeRPathRelative = true;
1789+
rootDir = argv[i];
1790+
}
1791+
else if (arg == "--no-standard-lib-dirs") {
1792+
modifyFlags |= MODIFY_FLAG_NO_STD_LIB_DIRS;
1793+
}
16671794
else if (arg == "--print-rpath") {
16681795
printRPath = true;
16691796
}

0 commit comments

Comments
 (0)