kumquat-buildroot/package/patchelf/0001-Add-option-to-make-the-rpath-relative-under-a-specif.patch

339 lines
13 KiB
Diff
Raw Normal View History

From 618220bfb55c875d6a4d197cb24fe632ac93ec85 Mon Sep 17 00:00:00 2001
From: Wolfgang Grandegger <wg@grandegger.com>
Date: Mon, 20 Feb 2017 16:29:24 +0100
Subject: [PATCH] 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
similar to Martin's patches [1] making the Buildroot toolchaing/SDK
relocatable.
RPATHDIR starts with "$ORIGIN":
The original build-system already took care of setting a relative
RPATH, resolve it and test if it's valid (does exist)
RPATHDIR starts with ROOTDIR:
The original build-system added some absolute RPATH (absolute on
the build machine). Test if it's valid (does exist).
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
valid (does exist).
RPATHDIR points somewhere else:
(can be anywhere: build trees, staging tree, host location,
non-existing location, etc.). Just discard such a path.
The option "--no-standard-libs" will discard RPATHDIRs ROOTDIR/lib and
ROOTDIR/usr/lib. Like "--shrink-rpath", RPATHDIRs are also discarded
if the directories do not contain a library referenced by the
DT_NEEDED fields.
If the option "--relative-to-file" is given, the rpath will start
with "$ORIGIN" making it relative to the ELF file, otherwise an
absolute path relative to ROOTDIR will be used.
A pull request for a similar patch [2] for mainline inclusion is
pending.
[1] http://lists.busybox.net/pipermail/buildroot/2016-April/159422.html
[2] https://github.com/NixOS/patchelf/pull/118
Signed-off-by: Wolfgang Grandegger <wg@grandegger.com>
[Fabrice: update for 0.13]
Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
---
src/patchelf.cc | 196 ++++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 175 insertions(+), 21 deletions(-)
diff --git a/src/patchelf.cc b/src/patchelf.cc
index 1d9a772..35b4a33 100644
--- a/src/patchelf.cc
+++ b/src/patchelf.cc
@@ -46,6 +46,10 @@ static bool debugMode = false;
static bool forceRPath = false;
+static bool noStandardLibDirs = false;
+
+static bool relativeToFile = false;
+
static std::vector<std::string> fileNames;
static std::string outputFileName;
static bool alwaysWrite = false;
@@ -77,6 +81,49 @@ static unsigned int getPageSize(){
return pageSize;
}
+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)
+{
+ std::string relPath = "$ORIGIN";
+ std::string p = path, refP = refPath;
+ size_t pos;
+
+ /* Strip the common part of path and refPath */
+ while (true) {
+ pos = p.find_first_of('/', 1);
+ if (refP.find_first_of('/', 1) != pos)
+ break;
+ if (p.substr(0, pos) != refP.substr(0, pos))
+ break;
+ if (pos == std::string::npos)
+ break;
+ p = p.substr(pos);
+ refP = refP.substr(pos);
+ }
+ /* Check if both pathes are equal */
+ if (p != refP) {
+ pos = 0;
+ while (pos != std::string::npos) {
+ pos =refP.find_first_of('/', pos + 1);
+ relPath.append("/..");
+ }
+ relPath.append(p);
+ }
+
+ return relPath;
+}
template<ElfFileParams>
class ElfFile
@@ -183,9 +230,13 @@ public:
void setInterpreter(const string & newInterpreter);
- typedef enum { rpPrint, rpShrink, rpSet, rpAdd, rpRemove } RPathOp;
+ typedef enum { rpPrint, rpShrink, rpMakeRelative, rpSet, rpAdd, rpRemove} RPathOp;
+
+ bool libFoundInRPath(const std::string & dirName,
+ const std::vector<std::string> neededLibs,
+ std::vector<bool> & neededLibFound);
- void modifyRPath(RPathOp op, const std::vector<std::string> & allowedRpathPrefixes, std::string newRPath);
+ void modifyRPath(RPathOp op, std::string rootDir, const std::vector<std::string> & allowedRpathPrefixes, std::string newRPath, const std::string & fileName);
void addNeeded(set<string> libs);
@@ -1041,8 +1092,28 @@ static void concatToRPath(string & rpath, const string & path)
template<ElfFileParams>
-void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op,
- const std::vector<std::string> & allowedRpathPrefixes, std::string newRPath)
+bool ElfFile<ElfFileParamNames>::libFoundInRPath(const std::string & dirName,
+ const std::vector<std::string> neededLibs, std::vector<bool> & neededLibFound)
+{
+ /* 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 = dirName + "/" + neededLibs[j];
+ struct stat st;
+ if (stat(libName.c_str(), &st) == 0) {
+ neededLibFound[j] = true;
+ libFound = true;
+ }
+ }
+ return libFound;
+}
+
+
+template<ElfFileParams>
+void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op, std::string rootDir,
+ const std::vector<std::string> & allowedRpathPrefixes, std::string newRPath, const std::string & fileName)
{
Elf_Shdr & shdrDynamic = findSection(".dynamic");
@@ -1096,6 +1167,11 @@ void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op, string newRPath)
return;
}
+ if (op == rpMakeRelative && !rpath) {
+ debug("no RPATH to make relative\n");
+ return;
+ }
+
if (op == rpShrink && !rpath) {
debug("no RPATH to shrink\n");
return;
@@ -1120,31 +1196,80 @@ void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op, string newRPath)
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 = dirName + "/" + neededLibs[j];
- try {
- Elf32_Half library_e_machine = getElfType(readFile(libName, sizeof(Elf32_Ehdr))).machine;
- if (rdi(library_e_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)
+ if (!libFoundInRPath(dirName, neededLibs, neededLibFound))
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 = "";
+
+ std::vector<std::string> rpathDirs = splitColonDelimitedString(rpath);
+ for (std::vector<std::string>::iterator it = rpathDirs.begin(); it != rpathDirs.end(); ++it) {
+ const std::string & dirName = *it;
+
+ std::string canonicalPath;
+
+ /* Figure out if we should keep or discard the path. There are several
+ cases to be handled:
+ "dirName" starts with "$ORIGIN":
+ The original build-system already took care of setting a relative
+ RPATH. Resolve it and test if it's valid (does exist).
+ "dirName" start with "rootDir":
+ The original build-system added some absolute RPATH (absolute on
+ the build machine). Test if it's valid (does exist).
+ "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's
+ valid (does exist).
+ "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")) {
+ std::string path = fileDir + dirName.substr(7);
+ if (!absolutePathExists(path, canonicalPath)) {
+ debug("removing directory '%s' from RPATH because '%s' doesn't exist\n",
+ dirName.c_str(), path.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 {
+ std::string path = rootDir + dirName;
+ if (!absolutePathExists(path, canonicalPath)) {
+ debug("removing directory '%s' from RPATH because it's not in rootdir\n",
+ dirName.c_str());
+ continue;
+ }
+ }
+
+ if (noStandardLibDirs) {
+ 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;
+ }
+ }
+
+ /* Finally make "canonicalPath" relative to "filedir" in "rootDir" */
+ if (relativeToFile)
+ concatToRPath(newRPath, makePathRelative(canonicalPath, fileDir));
+ else
+ concatToRPath(newRPath, canonicalPath.substr(rootDir.length()));
+ debug("keeping relative path of %s\n", canonicalPath.c_str());
+ }
+ }
+
if (op == rpRemove) {
if (!rpath) {
debug("no RPATH to delete\n");
package/patchelf: keep RPATH entries even without DT_NEEDED libraries Our patch 0003-Add-option-to-make-the-rpath-relative-under-a-specif.patch adds an option --make-rpath-relative, which we use to tweak RPATH of target binaries. However, one of the effect of this option is that it drops RPATH entries if the corresponding directory does not contain a library that is referenced by a DT_NEEDED entry of the binary. This unfortunately isn't correct, as RPATH entries are not only used by the dynamic linker to resolve the location of libraries listed through DT_NEEDED entries: RPATH entries are also used by dlopen() when resolving the location of libraries that are loaded at runtime. Therefore, the removal of RPATH entries that don't correspond to directories containing libraries referenced by DT_NEEDED entries break legitimate uses of RPATH for dlopen()ed libraries. This issue was even pointed out during the review of the upstream pull request: https://github.com/NixOS/patchelf/pull/118#discussion_r329660138 This fixes tst-origin uClibc-ng unit test: https://github.com/wbx-github/uclibc-ng-test/blob/master/test/dlopen/Makefile.in#L25 https://github.com/wbx-github/uclibc-ng-test/blob/master/test/dlopen/tst-origin.c#L15 Without this patch: $ gcc -o toto toto.c -Wl,-rpath,/tmp/test/bar $ readelf -d toto | grep PATH 0x000000000000000f (RPATH) Library rpath: [/tmp/test/bar] $ ./output/host/bin/patchelf --debug --make-rpath-relative /tmp/ toto patching ELF file `toto' Kernel page size is 4096 bytes removing directory '/tmp/test/bar' from RPATH because it does not contain needed libs new rpath is `' $ readelf -d toto | grep PATH 0x000000000000001d (RUNPATH) Library runpath: [] With the patch applied: $ gcc -o toto toto.c -Wl,-rpath,/tmp/test/bar $ readelf -d toto | grep PATH 0x000000000000000f (RPATH) Library rpath: [/tmp/test/bar] $ ./output/host/bin/patchelf --debug --make-rpath-relative /tmp/ toto patching ELF file `toto' Kernel page size is 4096 bytes keeping relative path of /tmp/test/bar new rpath is `test/bar' $ readelf -d toto | grep PATH 0x000000000000001d (RUNPATH) Library runpath: [test/bar] Signed-off-by: Yann Sionneau <ysionneau@kalray.eu> Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
2019-10-01 14:33:30 +02:00
@@ -1413,7 +1543,9 @@ static bool shrinkRPath = false;
static bool setRPath = false;
static bool addRPath = 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;
@@ -1438,16 +1570,18 @@ static void patchElf2(ElfFile & elfFile)
elfFile.setInterpreter(newInterpreter);
if (printRPath)
- elfFile.modifyRPath(elfFile.rpPrint, {}, "");
+ elfFile.modifyRPath(elfFile.rpPrint, "", {}, "", fileName);
if (shrinkRPath)
- elfFile.modifyRPath(elfFile.rpShrink, allowedRpathPrefixes, "");
+ elfFile.modifyRPath(elfFile.rpShrink, "", allowedRpathPrefixes, "", fileName);
else if (removeRPath)
- elfFile.modifyRPath(elfFile.rpRemove, {}, "");
+ elfFile.modifyRPath(elfFile.rpRemove, "", {}, "", fileName);
else if (setRPath)
- elfFile.modifyRPath(elfFile.rpSet, {}, newRPath);
+ elfFile.modifyRPath(elfFile.rpSet, "", {}, newRPath, fileName);
else if (addRPath)
- elfFile.modifyRPath(elfFile.rpAdd, {}, newRPath);
+ elfFile.modifyRPath(elfFile.rpAdd, "", {}, newRPath, fileName);
+ else if (makeRPathRelative)
+ elfFile.modifyRPath(elfFile.rpMakeRelative, rootDir, {}, "", fileName);
if (printNeeded) elfFile.printNeededLibs();
package/patchelf: keep RPATH entries even without DT_NEEDED libraries Our patch 0003-Add-option-to-make-the-rpath-relative-under-a-specif.patch adds an option --make-rpath-relative, which we use to tweak RPATH of target binaries. However, one of the effect of this option is that it drops RPATH entries if the corresponding directory does not contain a library that is referenced by a DT_NEEDED entry of the binary. This unfortunately isn't correct, as RPATH entries are not only used by the dynamic linker to resolve the location of libraries listed through DT_NEEDED entries: RPATH entries are also used by dlopen() when resolving the location of libraries that are loaded at runtime. Therefore, the removal of RPATH entries that don't correspond to directories containing libraries referenced by DT_NEEDED entries break legitimate uses of RPATH for dlopen()ed libraries. This issue was even pointed out during the review of the upstream pull request: https://github.com/NixOS/patchelf/pull/118#discussion_r329660138 This fixes tst-origin uClibc-ng unit test: https://github.com/wbx-github/uclibc-ng-test/blob/master/test/dlopen/Makefile.in#L25 https://github.com/wbx-github/uclibc-ng-test/blob/master/test/dlopen/tst-origin.c#L15 Without this patch: $ gcc -o toto toto.c -Wl,-rpath,/tmp/test/bar $ readelf -d toto | grep PATH 0x000000000000000f (RPATH) Library rpath: [/tmp/test/bar] $ ./output/host/bin/patchelf --debug --make-rpath-relative /tmp/ toto patching ELF file `toto' Kernel page size is 4096 bytes removing directory '/tmp/test/bar' from RPATH because it does not contain needed libs new rpath is `' $ readelf -d toto | grep PATH 0x000000000000001d (RUNPATH) Library runpath: [] With the patch applied: $ gcc -o toto toto.c -Wl,-rpath,/tmp/test/bar $ readelf -d toto | grep PATH 0x000000000000000f (RPATH) Library rpath: [/tmp/test/bar] $ ./output/host/bin/patchelf --debug --make-rpath-relative /tmp/ toto patching ELF file `toto' Kernel page size is 4096 bytes keeping relative path of /tmp/test/bar new rpath is `test/bar' $ readelf -d toto | grep PATH 0x000000000000001d (RUNPATH) Library runpath: [test/bar] Signed-off-by: Yann Sionneau <ysionneau@kalray.eu> Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
2019-10-01 14:33:30 +02:00
@@ -1508,6 +1642,9 @@ void showHelp(const 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\
+ [--relative-to-file]\n\
[--print-rpath]\n\
[--force-rpath]\n\
[--add-needed LIBRARY]\n\
package/patchelf: keep RPATH entries even without DT_NEEDED libraries Our patch 0003-Add-option-to-make-the-rpath-relative-under-a-specif.patch adds an option --make-rpath-relative, which we use to tweak RPATH of target binaries. However, one of the effect of this option is that it drops RPATH entries if the corresponding directory does not contain a library that is referenced by a DT_NEEDED entry of the binary. This unfortunately isn't correct, as RPATH entries are not only used by the dynamic linker to resolve the location of libraries listed through DT_NEEDED entries: RPATH entries are also used by dlopen() when resolving the location of libraries that are loaded at runtime. Therefore, the removal of RPATH entries that don't correspond to directories containing libraries referenced by DT_NEEDED entries break legitimate uses of RPATH for dlopen()ed libraries. This issue was even pointed out during the review of the upstream pull request: https://github.com/NixOS/patchelf/pull/118#discussion_r329660138 This fixes tst-origin uClibc-ng unit test: https://github.com/wbx-github/uclibc-ng-test/blob/master/test/dlopen/Makefile.in#L25 https://github.com/wbx-github/uclibc-ng-test/blob/master/test/dlopen/tst-origin.c#L15 Without this patch: $ gcc -o toto toto.c -Wl,-rpath,/tmp/test/bar $ readelf -d toto | grep PATH 0x000000000000000f (RPATH) Library rpath: [/tmp/test/bar] $ ./output/host/bin/patchelf --debug --make-rpath-relative /tmp/ toto patching ELF file `toto' Kernel page size is 4096 bytes removing directory '/tmp/test/bar' from RPATH because it does not contain needed libs new rpath is `' $ readelf -d toto | grep PATH 0x000000000000001d (RUNPATH) Library runpath: [] With the patch applied: $ gcc -o toto toto.c -Wl,-rpath,/tmp/test/bar $ readelf -d toto | grep PATH 0x000000000000000f (RPATH) Library rpath: [/tmp/test/bar] $ ./output/host/bin/patchelf --debug --make-rpath-relative /tmp/ toto patching ELF file `toto' Kernel page size is 4096 bytes keeping relative path of /tmp/test/bar new rpath is `test/bar' $ readelf -d toto | grep PATH 0x000000000000001d (RUNPATH) Library runpath: [test/bar] Signed-off-by: Yann Sionneau <ysionneau@kalray.eu> Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
2019-10-01 14:33:30 +02:00
@@ -1564,6 +1701,17 @@ int main(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") {
+ noStandardLibDirs = true;
+ }
+ else if (arg == "--relative-to-file") {
+ relativeToFile = true;
+ }
else if (arg == "--print-rpath") {
printRPath = true;
}
--
1.9.1