@@ -49,6 +49,8 @@ static int pageSize = PAGESIZE;
typedef std::shared_ptr<std::vector<unsigned char>> FileContents;
+#define MODIFY_FLAG_NO_STD_LIB_DIRS 0x1
+static int modifyFlags;
#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
#define ElfFileParamNames Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Addr, Elf_Off, Elf_Dyn, Elf_Sym, Elf_Verneed
@@ -84,6 +86,39 @@ static unsigned int getPageSize()
}
+static bool absolutePathExists(const std::string & path,
+ std::string & canonicalPath)
+{
+ char *cpath = realpath(path.c_str(), NULL);
+ if (cpath) {
+ canonicalPath = cpath;
+ free(cpath);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static std::string makePathRelative(const std::string & path,
+ const std::string & refPath,
+ const std::string & rootDir)
+{
+ std::string relPath = "$ORIGIN";
+
+ /* Strip root path first */
+ std::string p = path.substr(rootDir.length());
+ std::string refP = refPath.substr(rootDir.length());
+
+ std::size_t pos = refP.find_first_of('/');
+ while (pos != std::string::npos) {
+ pos =refP.find_first_of('/', pos + 1);
+ relPath.append("/..");
+ }
+ relPath.append(p);
+
+ return relPath;
+}
+
template<ElfFileParams>
class ElfFile
{
@@ -191,9 +226,11 @@ public:
void setInterpreter(const std::string & newInterpreter);
- typedef enum { rpPrint, rpShrink, rpSet, rpRemove } RPathOp;
+ typedef enum { rpPrint, rpShrink, rpMakeRelative, rpSet, rpRemove} RPathOp;
- void modifyRPath(RPathOp op, const std::vector<std::string> & allowedRpathPrefixes, std::string newRPath);
+ void modifyRPath(RPathOp op,
+ const std::vector<std::string> & allowedRpathPrefixes,
+ std::string rootDir, int flags, std::string newRPath);
void addNeeded(const std::set<std::string> & libs);
@@ -1114,7 +1151,8 @@ static void concatToRPath(std::string & rpath, const std::string & path)
template<ElfFileParams>
void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op,
- const std::vector<std::string> & allowedRpathPrefixes, std::string newRPath)
+ const std::vector<std::string> & allowedRpathPrefixes,
+ std::string rootDir, int flags, std::string newRPath)
{
Elf_Shdr & shdrDynamic = findSection(".dynamic");
@@ -1174,6 +1212,10 @@ void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op,
return;
}
+ if (op == rpMakeRelative && !rpath) {
+ debug("no RPATH to make relative\n");
+ return;
+ }
/* For each directory in the RPATH, check if it contains any
needed library. */
@@ -1197,8 +1239,8 @@ void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op,
debug("removing directory '%s' from RPATH because of non-allowed prefix\n", dirName.c_str());
continue;
}
-
- /* For each library that we haven't found yet, see if it
+
+ /* 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)
@@ -1219,7 +1261,89 @@ void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op,
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) {
+ std::vector<bool> neededLibFound(neededLibs.size(), false);
+ std::string fileDir = fileName.substr(0, fileName.find_last_of("/"));
+ newRPath = "";
+
+ for (auto & dirName : splitColonDelimitedString(rpath)) {
+ std::string canonicalPath;
+ std::string path;
+
+ /* Figure out if we should keep or discard the path; there are several
+ cases to handled:
+ "dirName" starts with "$ORIGIN":
+ The original build-system already took care of setting a relative
+ RPATH, resolve it and test if it is worthwhile to keep it.
+ "dirName" start with "rootDir":
+ The original build-system added some absolute RPATH (absolute on
+ the build machine). While this is wrong, it can still be fixed; so
+ test if it is worthwhile to keep it.
+ "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 is
+ worthwhile to keep it;
+ "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")) {
+ path = fileDir + dirName.substr(7);
+ if (!absolutePathExists(path, canonicalPath)) {
+ debug("removing directory '%s' from RPATH because it doesn't exist\n", dirName.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 {
+ path = rootDir + dirName;
+ if (!absolutePathExists(path, canonicalPath)) {
+ debug("removing directory '%s' from RPATH because it's not in the root directory\n",
+ dirName.c_str());
+ continue;
+ }
+ }
+
+ if (flags & MODIFY_FLAG_NO_STD_LIB_DIRS) {
+ 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;
+ }
+ }
+
+ /* 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]) {
+ std::string libName = canonicalPath + "/" + neededLibs[j];
+ try {
+ if (getElfType(readFile(libName, sizeof(Elf32_Ehdr))).machine == rdi(hdr->e_machine)) {
+ neededLibFound[j] = true;
+ libFound = true;
+ } else
+ debug("ignoring library '%s' because its machine type differs\n", libName.c_str());
+ } catch (SysError & e) {
+ if (e.errNo != ENOENT) throw;
+ }
+ }
+
+ if (!libFound) {
+ debug("removing directory '%s' from RPATH\n", dirName.c_str());
+ continue;
+ }
+
+ /* Finally make "canonicalPath" relative to "filedir" in "rootDir" */
+ concatToRPath(newRPath, makePathRelative(canonicalPath, fileDir, rootDir));
+ }
}
if (op == rpRemove) {
@@ -1549,7 +1673,9 @@ static std::vector<std::string> allowedRpathPrefixes;
static bool removeRPath = false;
static bool setRPath = false;
static bool printRPath = false;
+static bool makeRPathRelative = false;
static std::string newRPath;
+static std::string rootDir;
static std::set<std::string> neededLibsToRemove;
static std::map<std::string, std::string> neededLibsToReplace;
static std::set<std::string> neededLibsToAdd;
@@ -1572,14 +1698,16 @@ static void patchElf2(ElfFile && elfFile)
elfFile.setInterpreter(newInterpreter);
if (printRPath)
- elfFile.modifyRPath(elfFile.rpPrint, {}, "");
+ elfFile.modifyRPath(elfFile.rpPrint, {}, {}, modifyFlags, "");
if (shrinkRPath)
- elfFile.modifyRPath(elfFile.rpShrink, allowedRpathPrefixes, "");
+ elfFile.modifyRPath(elfFile.rpShrink, allowedRpathPrefixes, "", modifyFlags, "");
else if (removeRPath)
- elfFile.modifyRPath(elfFile.rpRemove, {}, "");
+ elfFile.modifyRPath(elfFile.rpRemove, {}, "", modifyFlags, "");
else if (setRPath)
- elfFile.modifyRPath(elfFile.rpSet, {}, newRPath);
+ elfFile.modifyRPath(elfFile.rpSet, {}, "", modifyFlags, newRPath);
+ else if (makeRPathRelative)
+ elfFile.modifyRPath(elfFile.rpMakeRelative, {}, rootDir, modifyFlags, "");
if (printNeeded) elfFile.printNeededLibs();
@@ -1625,6 +1753,8 @@ void showHelp(const std::string & progName)
[--remove-rpath]\n\
[--shrink-rpath]\n\
[--allowed-rpath-prefixes PREFIXES]\t\tWith '--shrink-rpath', reject rpath entries not starting with the allowed prefix\n\
+ [--make-rpath-relative ROOTDIR]\n\
+ [--no-standard-lib-dirs]\n\
[--print-rpath]\n\
[--force-rpath]\n\
[--add-needed LIBRARY]\n\
@@ -1685,6 +1815,14 @@ int mainWrapped(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") {
+ modifyFlags |= MODIFY_FLAG_NO_STD_LIB_DIRS;
+ }
else if (arg == "--print-rpath") {
printRPath = true;
}