1*4882a593SmuzhiyunFrom 618220bfb55c875d6a4d197cb24fe632ac93ec85 Mon Sep 17 00:00:00 2001
2*4882a593SmuzhiyunFrom: Wolfgang Grandegger <wg@grandegger.com>
3*4882a593SmuzhiyunDate: Mon, 20 Feb 2017 16:29:24 +0100
4*4882a593SmuzhiyunSubject: [PATCH] Add option to make the rpath relative under a specified root
5*4882a593Smuzhiyun directory
6*4882a593Smuzhiyun
7*4882a593SmuzhiyunRunning "patchelf" with the option "--make-rpath-relative ROOTDIR" will
8*4882a593Smuzhiyunmodify or delete the RPATHDIRs according the following rules
9*4882a593Smuzhiyunsimilar to Martin's patches [1] making the Buildroot toolchaing/SDK
10*4882a593Smuzhiyunrelocatable.
11*4882a593Smuzhiyun
12*4882a593SmuzhiyunRPATHDIR starts with "$ORIGIN":
13*4882a593Smuzhiyun    The original build-system already took care of setting a relative
14*4882a593Smuzhiyun    RPATH, resolve it and test if it's valid (does exist)
15*4882a593Smuzhiyun
16*4882a593SmuzhiyunRPATHDIR starts with ROOTDIR:
17*4882a593Smuzhiyun    The original build-system added some absolute RPATH (absolute on
18*4882a593Smuzhiyun    the build machine). Test if it's valid (does exist).
19*4882a593Smuzhiyun
20*4882a593SmuzhiyunROOTDIR/RPATHDIR exists:
21*4882a593Smuzhiyun    The original build-system already took care of setting an absolute
22*4882a593Smuzhiyun    RPATH (absolute in the final rootfs), resolve it and test if it's
23*4882a593Smuzhiyun    valid (does exist).
24*4882a593Smuzhiyun
25*4882a593SmuzhiyunRPATHDIR points somewhere else:
26*4882a593Smuzhiyun    (can be anywhere: build trees, staging tree, host location,
27*4882a593Smuzhiyun    non-existing location, etc.). Just discard such a path.
28*4882a593Smuzhiyun
29*4882a593SmuzhiyunThe option "--no-standard-libs" will discard RPATHDIRs ROOTDIR/lib and
30*4882a593SmuzhiyunROOTDIR/usr/lib. Like "--shrink-rpath", RPATHDIRs are also discarded
31*4882a593Smuzhiyunif the directories do not contain a library referenced by the
32*4882a593SmuzhiyunDT_NEEDED fields.
33*4882a593SmuzhiyunIf the option "--relative-to-file" is given, the rpath will start
34*4882a593Smuzhiyunwith "$ORIGIN" making it relative to the ELF file, otherwise an
35*4882a593Smuzhiyunabsolute path relative to ROOTDIR will be used.
36*4882a593Smuzhiyun
37*4882a593SmuzhiyunA pull request for a similar patch [2] for mainline inclusion is
38*4882a593Smuzhiyunpending.
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun[1] http://lists.busybox.net/pipermail/buildroot/2016-April/159422.html
41*4882a593Smuzhiyun[2] https://github.com/NixOS/patchelf/pull/118
42*4882a593Smuzhiyun
43*4882a593SmuzhiyunSigned-off-by: Wolfgang Grandegger <wg@grandegger.com>
44*4882a593Smuzhiyun---
45*4882a593Smuzhiyun src/patchelf.cc | 196 ++++++++++++++++++++++++++++++++++++++++++++++++++------
46*4882a593Smuzhiyun 1 file changed, 175 insertions(+), 21 deletions(-)
47*4882a593Smuzhiyun
48*4882a593Smuzhiyundiff --git a/src/patchelf.cc b/src/patchelf.cc
49*4882a593Smuzhiyunindex 1d9a772..35b4a33 100644
50*4882a593Smuzhiyun--- a/src/patchelf.cc
51*4882a593Smuzhiyun+++ b/src/patchelf.cc
52*4882a593Smuzhiyun@@ -46,6 +46,10 @@ static bool debugMode = false;
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun static bool forceRPath = false;
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun+static bool noStandardLibDirs = false;
57*4882a593Smuzhiyun+
58*4882a593Smuzhiyun+static bool relativeToFile = false;
59*4882a593Smuzhiyun+
60*4882a593Smuzhiyun static string fileName;
61*4882a593Smuzhiyun static int pageSize = PAGESIZE;
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun@@ -77,6 +81,49 @@ static unsigned int getPageSize(){
64*4882a593Smuzhiyun     return pageSize;
65*4882a593Smuzhiyun }
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun+static bool absolutePathExists(const string & path, string & canonicalPath)
68*4882a593Smuzhiyun+{
69*4882a593Smuzhiyun+    char *cpath = realpath(path.c_str(), NULL);
70*4882a593Smuzhiyun+    if (cpath) {
71*4882a593Smuzhiyun+        canonicalPath = cpath;
72*4882a593Smuzhiyun+        free(cpath);
73*4882a593Smuzhiyun+        return true;
74*4882a593Smuzhiyun+    } else {
75*4882a593Smuzhiyun+        return false;
76*4882a593Smuzhiyun+    }
77*4882a593Smuzhiyun+}
78*4882a593Smuzhiyun+
79*4882a593Smuzhiyun+static string makePathRelative(const string & path,
80*4882a593Smuzhiyun+    const string & refPath)
81*4882a593Smuzhiyun+{
82*4882a593Smuzhiyun+    string relPath = "$ORIGIN";
83*4882a593Smuzhiyun+    string p = path, refP = refPath;
84*4882a593Smuzhiyun+    size_t pos;
85*4882a593Smuzhiyun+
86*4882a593Smuzhiyun+    /* Strip the common part of path and refPath */
87*4882a593Smuzhiyun+    while (true) {
88*4882a593Smuzhiyun+        pos = p.find_first_of('/', 1);
89*4882a593Smuzhiyun+        if (refP.find_first_of('/', 1) != pos)
90*4882a593Smuzhiyun+            break;
91*4882a593Smuzhiyun+        if (p.substr(0, pos) != refP.substr(0, pos))
92*4882a593Smuzhiyun+            break;
93*4882a593Smuzhiyun+        if (pos == string::npos)
94*4882a593Smuzhiyun+            break;
95*4882a593Smuzhiyun+        p = p.substr(pos);
96*4882a593Smuzhiyun+        refP = refP.substr(pos);
97*4882a593Smuzhiyun+    }
98*4882a593Smuzhiyun+    /* Check if both pathes are equal */
99*4882a593Smuzhiyun+    if (p != refP) {
100*4882a593Smuzhiyun+        pos = 0;
101*4882a593Smuzhiyun+        while (pos != string::npos) {
102*4882a593Smuzhiyun+            pos =refP.find_first_of('/', pos + 1);
103*4882a593Smuzhiyun+            relPath.append("/..");
104*4882a593Smuzhiyun+        }
105*4882a593Smuzhiyun+        relPath.append(p);
106*4882a593Smuzhiyun+    }
107*4882a593Smuzhiyun+
108*4882a593Smuzhiyun+    return relPath;
109*4882a593Smuzhiyun+}
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun template<ElfFileParams>
112*4882a593Smuzhiyun class ElfFile
113*4882a593Smuzhiyun@@ -183,9 +230,13 @@ public:
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun     void setInterpreter(const string & newInterpreter);
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun-    typedef enum { rpPrint, rpShrink, rpSet, rpRemove } RPathOp;
118*4882a593Smuzhiyun+    typedef enum { rpPrint, rpShrink, rpMakeRelative, rpSet, rpRemove} RPathOp;
119*4882a593Smuzhiyun+
120*4882a593Smuzhiyun+    bool libFoundInRPath(const string & dirName,
121*4882a593Smuzhiyun+                         const vector<string> neededLibs,
122*4882a593Smuzhiyun+                         vector<bool> & neededLibFound);
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun-    void modifyRPath(RPathOp op, string newRPath);
125*4882a593Smuzhiyun+    void modifyRPath(RPathOp op, string rootDir, string newRPath);
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun     void addNeeded(set<string> libs);
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun@@ -1041,7 +1092,27 @@ static void concatToRPath(string & rpath, const string & path)
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun template<ElfFileParams>
133*4882a593Smuzhiyun-void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op, string newRPath)
134*4882a593Smuzhiyun+bool ElfFile<ElfFileParamNames>::libFoundInRPath(const string & dirName,
135*4882a593Smuzhiyun+    const vector<string> neededLibs, vector<bool> & neededLibFound)
136*4882a593Smuzhiyun+{
137*4882a593Smuzhiyun+    /* For each library that we haven't found yet, see if it
138*4882a593Smuzhiyun+       exists in this directory. */
139*4882a593Smuzhiyun+    bool libFound = false;
140*4882a593Smuzhiyun+    for (unsigned int j = 0; j < neededLibs.size(); ++j)
141*4882a593Smuzhiyun+        if (!neededLibFound[j]) {
142*4882a593Smuzhiyun+            string libName = dirName + "/" + neededLibs[j];
143*4882a593Smuzhiyun+            struct stat st;
144*4882a593Smuzhiyun+            if (stat(libName.c_str(), &st) == 0) {
145*4882a593Smuzhiyun+                neededLibFound[j] = true;
146*4882a593Smuzhiyun+                libFound = true;
147*4882a593Smuzhiyun+            }
148*4882a593Smuzhiyun+        }
149*4882a593Smuzhiyun+    return libFound;
150*4882a593Smuzhiyun+}
151*4882a593Smuzhiyun+
152*4882a593Smuzhiyun+
153*4882a593Smuzhiyun+template<ElfFileParams>
154*4882a593Smuzhiyun+void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op, string rootDir, string newRPath)
155*4882a593Smuzhiyun {
156*4882a593Smuzhiyun     Elf_Shdr & shdrDynamic = findSection(".dynamic");
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun@@ -1096,6 +1167,11 @@ void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op, string newRPath)
159*4882a593Smuzhiyun         return;
160*4882a593Smuzhiyun     }
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun+    if (op == rpMakeRelative && !rpath) {
163*4882a593Smuzhiyun+        debug("no RPATH to make relative\n");
164*4882a593Smuzhiyun+        return;
165*4882a593Smuzhiyun+    }
166*4882a593Smuzhiyun+
167*4882a593Smuzhiyun     if (op == rpShrink && !rpath) {
168*4882a593Smuzhiyun         debug("no RPATH to shrink\n");
169*4882a593Smuzhiyun         return;
170*4882a593Smuzhiyun@@ -1120,26 +1196,80 @@ void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op, string newRPath)
171*4882a593Smuzhiyun                 continue;
172*4882a593Smuzhiyun             }
173*4882a593Smuzhiyun
174*4882a593Smuzhiyun-            /* For each library that we haven't found yet, see if it
175*4882a593Smuzhiyun-               exists in this directory. */
176*4882a593Smuzhiyun-            bool libFound = false;
177*4882a593Smuzhiyun-            for (unsigned int j = 0; j < neededLibs.size(); ++j)
178*4882a593Smuzhiyun-                if (!neededLibFound[j]) {
179*4882a593Smuzhiyun-                    string libName = dirName + "/" + neededLibs[j];
180*4882a593Smuzhiyun-                    struct stat st;
181*4882a593Smuzhiyun-                    if (stat(libName.c_str(), &st) == 0) {
182*4882a593Smuzhiyun-                        neededLibFound[j] = true;
183*4882a593Smuzhiyun-                        libFound = true;
184*4882a593Smuzhiyun-                    }
185*4882a593Smuzhiyun-                }
186*4882a593Smuzhiyun-
187*4882a593Smuzhiyun-            if (!libFound)
188*4882a593Smuzhiyun+            if (!libFoundInRPath(dirName, neededLibs, neededLibFound))
189*4882a593Smuzhiyun                 debug("removing directory `%s' from RPATH\n", dirName.c_str());
190*4882a593Smuzhiyun             else
191*4882a593Smuzhiyun                 concatToRPath(newRPath, dirName);
192*4882a593Smuzhiyun         }
193*4882a593Smuzhiyun     }
194*4882a593Smuzhiyun
195*4882a593Smuzhiyun+    /* Make the the RPATH relative to the specified path */
196*4882a593Smuzhiyun+    if (op == rpMakeRelative) {
197*4882a593Smuzhiyun+        vector<bool> neededLibFound(neededLibs.size(), false);
198*4882a593Smuzhiyun+        string fileDir = fileName.substr(0, fileName.find_last_of("/"));
199*4882a593Smuzhiyun+
200*4882a593Smuzhiyun+        newRPath = "";
201*4882a593Smuzhiyun+
202*4882a593Smuzhiyun+        vector<string> rpathDirs = splitColonDelimitedString(rpath);
203*4882a593Smuzhiyun+        for (vector<string>::iterator it = rpathDirs.begin(); it != rpathDirs.end(); ++it) {
204*4882a593Smuzhiyun+            const string & dirName = *it;
205*4882a593Smuzhiyun+
206*4882a593Smuzhiyun+            string canonicalPath;
207*4882a593Smuzhiyun+
208*4882a593Smuzhiyun+            /* Figure out if we should keep or discard the path. There are several
209*4882a593Smuzhiyun+               cases to be handled:
210*4882a593Smuzhiyun+               "dirName" starts with "$ORIGIN":
211*4882a593Smuzhiyun+                   The original build-system already took care of setting a relative
212*4882a593Smuzhiyun+                   RPATH. Resolve it and test if it's valid (does exist).
213*4882a593Smuzhiyun+               "dirName" start with "rootDir":
214*4882a593Smuzhiyun+                   The original build-system added some absolute RPATH (absolute on
215*4882a593Smuzhiyun+                   the build machine). Test if it's valid (does exist).
216*4882a593Smuzhiyun+               "rootDir"/"dirName" exists:
217*4882a593Smuzhiyun+                    The original build-system already took care of setting an absolute
218*4882a593Smuzhiyun+                    RPATH (absolute in the final rootfs). Resolve it and test if it's
219*4882a593Smuzhiyun+                    valid (does exist).
220*4882a593Smuzhiyun+               "dirName" points somewhere else:
221*4882a593Smuzhiyun+                    (can be anywhere: build trees, staging tree, host location,
222*4882a593Smuzhiyun+                    non-existing location, etc.). Just discard such a path. */
223*4882a593Smuzhiyun+            if (!dirName.compare(0, 7, "$ORIGIN")) {
224*4882a593Smuzhiyun+                string path = fileDir + dirName.substr(7);
225*4882a593Smuzhiyun+                if (!absolutePathExists(path, canonicalPath)) {
226*4882a593Smuzhiyun+                    debug("removing directory '%s' from RPATH because '%s' doesn't exist\n",
227*4882a593Smuzhiyun+                          dirName.c_str(), path.c_str());
228*4882a593Smuzhiyun+                    continue;
229*4882a593Smuzhiyun+                }
230*4882a593Smuzhiyun+            } else if (!dirName.compare(0, rootDir.length(), rootDir)) {
231*4882a593Smuzhiyun+                if (!absolutePathExists(dirName, canonicalPath)) {
232*4882a593Smuzhiyun+                    debug("removing directory '%s' from RPATH because it doesn't exist\n", dirName.c_str());
233*4882a593Smuzhiyun+                    continue;
234*4882a593Smuzhiyun+                }
235*4882a593Smuzhiyun+            } else {
236*4882a593Smuzhiyun+                string path = rootDir + dirName;
237*4882a593Smuzhiyun+                if (!absolutePathExists(path, canonicalPath)) {
238*4882a593Smuzhiyun+                    debug("removing directory '%s' from RPATH because it's not in rootdir\n",
239*4882a593Smuzhiyun+                          dirName.c_str());
240*4882a593Smuzhiyun+                    continue;
241*4882a593Smuzhiyun+                }
242*4882a593Smuzhiyun+            }
243*4882a593Smuzhiyun+
244*4882a593Smuzhiyun+            if (noStandardLibDirs) {
245*4882a593Smuzhiyun+                if (!canonicalPath.compare(rootDir + "/lib") ||
246*4882a593Smuzhiyun+                    !canonicalPath.compare(rootDir + "/usr/lib")) {
247*4882a593Smuzhiyun+                    debug("removing directory '%s' from RPATH because it's a standard library directory\n",
248*4882a593Smuzhiyun+                         dirName.c_str());
249*4882a593Smuzhiyun+                    continue;
250*4882a593Smuzhiyun+                }
251*4882a593Smuzhiyun+            }
252*4882a593Smuzhiyun+
253*4882a593Smuzhiyun+            /* Finally make "canonicalPath" relative to "filedir" in "rootDir" */
254*4882a593Smuzhiyun+            if (relativeToFile)
255*4882a593Smuzhiyun+                concatToRPath(newRPath, makePathRelative(canonicalPath, fileDir));
256*4882a593Smuzhiyun+            else
257*4882a593Smuzhiyun+                concatToRPath(newRPath, canonicalPath.substr(rootDir.length()));
258*4882a593Smuzhiyun+            debug("keeping relative path of %s\n", canonicalPath.c_str());
259*4882a593Smuzhiyun+        }
260*4882a593Smuzhiyun+    }
261*4882a593Smuzhiyun+
262*4882a593Smuzhiyun     if (op == rpRemove) {
263*4882a593Smuzhiyun         if (!rpath) {
264*4882a593Smuzhiyun             debug("no RPATH to delete\n");
265*4882a593Smuzhiyun@@ -1413,7 +1543,9 @@ static bool shrinkRPath = false;
266*4882a593Smuzhiyun static bool removeRPath = false;
267*4882a593Smuzhiyun static bool setRPath = false;
268*4882a593Smuzhiyun static bool printRPath = false;
269*4882a593Smuzhiyun+static bool makeRPathRelative = false;
270*4882a593Smuzhiyun static string newRPath;
271*4882a593Smuzhiyun+static string rootDir;
272*4882a593Smuzhiyun static set<string> neededLibsToRemove;
273*4882a593Smuzhiyun static map<string, string> neededLibsToReplace;
274*4882a593Smuzhiyun static set<string> neededLibsToAdd;
275*4882a593Smuzhiyun@@ -1438,14 +1570,16 @@ static void patchElf2(ElfFile & elfFile)
276*4882a593Smuzhiyun         elfFile.setInterpreter(newInterpreter);
277*4882a593Smuzhiyun
278*4882a593Smuzhiyun     if (printRPath)
279*4882a593Smuzhiyun-        elfFile.modifyRPath(elfFile.rpPrint, "");
280*4882a593Smuzhiyun+        elfFile.modifyRPath(elfFile.rpPrint, "", "");
281*4882a593Smuzhiyun
282*4882a593Smuzhiyun     if (shrinkRPath)
283*4882a593Smuzhiyun-        elfFile.modifyRPath(elfFile.rpShrink, "");
284*4882a593Smuzhiyun+        elfFile.modifyRPath(elfFile.rpShrink, "", "");
285*4882a593Smuzhiyun     else if (removeRPath)
286*4882a593Smuzhiyun-        elfFile.modifyRPath(elfFile.rpRemove, "");
287*4882a593Smuzhiyun+        elfFile.modifyRPath(elfFile.rpRemove, "", "");
288*4882a593Smuzhiyun     else if (setRPath)
289*4882a593Smuzhiyun-        elfFile.modifyRPath(elfFile.rpSet, newRPath);
290*4882a593Smuzhiyun+        elfFile.modifyRPath(elfFile.rpSet, "", newRPath);
291*4882a593Smuzhiyun+    else if (makeRPathRelative)
292*4882a593Smuzhiyun+        elfFile.modifyRPath(elfFile.rpMakeRelative, rootDir, "");
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun     if (printNeeded) elfFile.printNeededLibs();
295*4882a593Smuzhiyun
296*4882a593Smuzhiyun@@ -1508,6 +1642,9 @@ void showHelp(const string & progName)
297*4882a593Smuzhiyun   [--set-rpath RPATH]\n\
298*4882a593Smuzhiyun   [--remove-rpath]\n\
299*4882a593Smuzhiyun   [--shrink-rpath]\n\
300*4882a593Smuzhiyun+  [--make-rpath-relative ROOTDIR]\n\
301*4882a593Smuzhiyun+  [--no-standard-lib-dirs]\n\
302*4882a593Smuzhiyun+  [--relative-to-file]\n\
303*4882a593Smuzhiyun   [--print-rpath]\n\
304*4882a593Smuzhiyun   [--force-rpath]\n\
305*4882a593Smuzhiyun   [--add-needed LIBRARY]\n\
306*4882a593Smuzhiyun@@ -1564,6 +1701,17 @@ int main(int argc, char * * argv)
307*4882a593Smuzhiyun             setRPath = true;
308*4882a593Smuzhiyun             newRPath = argv[i];
309*4882a593Smuzhiyun         }
310*4882a593Smuzhiyun+        else if (arg == "--make-rpath-relative") {
311*4882a593Smuzhiyun+            if (++i == argc) error("missing argument to --make-rpath-relative");
312*4882a593Smuzhiyun+            makeRPathRelative = true;
313*4882a593Smuzhiyun+            rootDir = argv[i];
314*4882a593Smuzhiyun+        }
315*4882a593Smuzhiyun+        else if (arg == "--no-standard-lib-dirs") {
316*4882a593Smuzhiyun+            noStandardLibDirs = true;
317*4882a593Smuzhiyun+        }
318*4882a593Smuzhiyun+        else if (arg == "--relative-to-file") {
319*4882a593Smuzhiyun+            relativeToFile = true;
320*4882a593Smuzhiyun+        }
321*4882a593Smuzhiyun         else if (arg == "--print-rpath") {
322*4882a593Smuzhiyun             printRPath = true;
323*4882a593Smuzhiyun         }
324*4882a593Smuzhiyun--
325*4882a593Smuzhiyun1.9.1
326*4882a593Smuzhiyun
327