hc
2023-11-20 2e7bd41e4e8ab3d1efdabd9e263a2f7fe79bff8c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
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>
---
 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 string fileName;
 static int pageSize = PAGESIZE;
 
@@ -77,6 +81,49 @@ static unsigned int getPageSize(){
     return pageSize;
 }
 
+static bool absolutePathExists(const string & path, string & canonicalPath)
+{
+    char *cpath = realpath(path.c_str(), NULL);
+    if (cpath) {
+        canonicalPath = cpath;
+        free(cpath);
+        return true;
+    } else {
+        return false;
+    }
+}
+
+static string makePathRelative(const string & path,
+    const string & refPath)
+{
+    string relPath = "$ORIGIN";
+    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 == string::npos)
+            break;
+        p = p.substr(pos);
+        refP = refP.substr(pos);
+    }
+    /* Check if both pathes are equal */
+    if (p != refP) {
+        pos = 0;
+        while (pos != 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, rpRemove } RPathOp;
+    typedef enum { rpPrint, rpShrink, rpMakeRelative, rpSet, rpRemove} RPathOp;
+
+    bool libFoundInRPath(const string & dirName,
+                         const vector<string> neededLibs,
+                         vector<bool> & neededLibFound);
 
-    void modifyRPath(RPathOp op, string newRPath);
+    void modifyRPath(RPathOp op, string rootDir, string newRPath);
 
     void addNeeded(set<string> libs);
 
@@ -1041,7 +1092,27 @@ static void concatToRPath(string & rpath, const string & path)
 
 
 template<ElfFileParams>
-void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op, string newRPath)
+bool ElfFile<ElfFileParamNames>::libFoundInRPath(const string & dirName,
+    const vector<string> neededLibs, 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]) {
+            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, string rootDir, string newRPath)
 {
     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,26 +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]) {
-                    string libName = dirName + "/" + neededLibs[j];
-                    struct stat st;
-                    if (stat(libName.c_str(), &st) == 0) {
-                        neededLibFound[j] = true;
-                        libFound = true;
-                    }
-                }
-
-            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) {
+        vector<bool> neededLibFound(neededLibs.size(), false);
+        string fileDir = fileName.substr(0, fileName.find_last_of("/"));
+
+        newRPath = "";
+
+        vector<string> rpathDirs = splitColonDelimitedString(rpath);
+        for (vector<string>::iterator it = rpathDirs.begin(); it != rpathDirs.end(); ++it) {
+            const string & dirName = *it;
+
+            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")) {
+                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 {
+                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");
@@ -1413,7 +1543,9 @@ static bool shrinkRPath = false;
 static bool removeRPath = false;
 static bool setRPath = false;
 static bool printRPath = false;
+static bool makeRPathRelative = false;
 static string newRPath;
+static string rootDir;
 static set<string> neededLibsToRemove;
 static map<string, string> neededLibsToReplace;
 static set<string> neededLibsToAdd;
@@ -1438,14 +1570,16 @@ static void patchElf2(ElfFile & elfFile)
         elfFile.setInterpreter(newInterpreter);
 
     if (printRPath)
-        elfFile.modifyRPath(elfFile.rpPrint, "");
+        elfFile.modifyRPath(elfFile.rpPrint, "", "");
 
     if (shrinkRPath)
-        elfFile.modifyRPath(elfFile.rpShrink, "");
+        elfFile.modifyRPath(elfFile.rpShrink, "", "");
     else if (removeRPath)
-        elfFile.modifyRPath(elfFile.rpRemove, "");
+        elfFile.modifyRPath(elfFile.rpRemove, "", "");
     else if (setRPath)
-        elfFile.modifyRPath(elfFile.rpSet, newRPath);
+        elfFile.modifyRPath(elfFile.rpSet, "", newRPath);
+    else if (makeRPathRelative)
+        elfFile.modifyRPath(elfFile.rpMakeRelative, rootDir, "");
 
     if (printNeeded) elfFile.printNeededLibs();
 
@@ -1508,6 +1642,9 @@ void showHelp(const string & progName)
   [--set-rpath RPATH]\n\
   [--remove-rpath]\n\
   [--shrink-rpath]\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\
@@ -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