[Buildroot] Relocatable internal toolchain

Wolfgang Grandegger wg at grandegger.com
Tue Feb 21 08:53:32 UTC 2017


Hello Samuel, all

more on that topic...

Am 26.01.2017 um 11:47 schrieb Samuel Martin:
> Hi Wolfgang, all,
> 
> On Thu, Jan 26, 2017 at 9:25 AM, Wolfgang Grandegger <wg at grandegger.com> wrote:
>> Hello,
>>
>> I prefer a relocatable (internal) toolchain and before digging deeper... Are
>> there any plans in that direction? I realized some attempts (patches) to
>> make the buildroot toolchain relocatable but they have not (yet) been
>> accepted. What are the pros and cons? Are there principle problems?
> 
> Yes, there are plans for this.
> 
> There had been a couple of the series posted on this topic (the latest one [1]).
> And we talked about this during the last Buildroot Meeting, you can
> check the report [2] for detailed conclusions.
> To sum-up:
> - Producing the relocatable host tools will be addressed step-by-step;
> - Preparatory changes making buildroot using absolute canonical paths
> are already merged [4];
> - Next step: fixing RPATH in host tools binaries thanks to some
> patchelf [3] features to be implemented and upstreamed (this should be
> enough to meet Buildroot needs);

I have now implemented "patchelf --make-rpath-relative ROOTDIR ..:"
following closely your related patches. Before starting the up-streaming
process, I first want to get some feedback here. Does it fit our needs?
Any other ideas or comments?

It would be nice to get rid of the "readelf" scripts as well. We could
call "patchelf" on any regular file and simply ignore the errors (not
an ELF file, not dynamic or not shared). That's fast but maybe a bit
too hackish. Having an extra option for that purpose might be
better/cleaner.

Below is the preliminary patchelf patch.

Wolfgang.

>From a045ca4c8b7341a188b99bc54dc167dabed5849f Mon Sep 17 00:00:00 2001
From: Wolfgang Grandegger <wg at grandegger.com>
Date: Mon, 20 Feb 2017 16:29:24 +0100
Subject: [PATCH] Add option to make the rpath relative within 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 build/SDK relocatable.

>From de067ffa52e97b9b2720452e7f78d16220b1c91a Mon Sep 17 00:00:00 2001
From: Wolfgang Grandegger <wg at grandegger.com>
Date: Mon, 20 Feb 2017 16:29:24 +0100
Subject: [PATCH] Add option to make the rpath relative within 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 build/SDK relocatable.

RPATHDIR 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.

RPATHDIR starts 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/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.

[1] http://lists.busybox.net/pipermail/buildroot/2016-April/159422.html
---
 src/patchelf.cc | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 148 insertions(+), 10 deletions(-)

diff --git a/src/patchelf.cc b/src/patchelf.cc
index 619d403..ab7e50e 100644
--- a/src/patchelf.cc
+++ b/src/patchelf.cc
@@ -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;
         }
-- 
1.9.1



More information about the buildroot mailing list