1 |
commit: 9a9adbf228320fdc135ad1457bb8bf586c71279c |
2 |
Author: Mike Frysinger <vapier <AT> chromium <DOT> org> |
3 |
AuthorDate: Thu Mar 26 18:32:57 2020 +0000 |
4 |
Commit: Mike Frysinger <vapier <AT> gentoo <DOT> org> |
5 |
CommitDate: Thu Apr 15 23:10:24 2021 +0000 |
6 |
URL: https://gitweb.gentoo.org/proj/pax-utils.git/commit/?id=9a9adbf2 |
7 |
|
8 |
pylintrc: adjust python code to 4 space indent |
9 |
|
10 |
This aligns with the latest Google/PEP standards. |
11 |
This doesn't add any pylint warnings as we've been disabling long |
12 |
lines in here for a long time. We don't do any other reformatting |
13 |
to try and cut down on git log/diff noise. |
14 |
|
15 |
Looking at this with --word-diff=color shows only whitespace changes. |
16 |
|
17 |
Signed-off-by: Mike Frysinger <vapier <AT> gentoo.org> |
18 |
|
19 |
.pylintrc | 2 +- |
20 |
lddtree.py | 1270 ++++++++++++++++++++++++++++++------------------------------ |
21 |
pylint | 46 +-- |
22 |
3 files changed, 659 insertions(+), 659 deletions(-) |
23 |
|
24 |
diff --git a/.pylintrc b/.pylintrc |
25 |
index cf1379d..577641f 100644 |
26 |
--- a/.pylintrc |
27 |
+++ b/.pylintrc |
28 |
@@ -36,7 +36,7 @@ score=no |
29 |
|
30 |
[FORMAT] |
31 |
max-line-length=80 |
32 |
-indent-string=' ' |
33 |
+indent-string = ' ' |
34 |
|
35 |
[BASIC] |
36 |
bad-functions= |
37 |
|
38 |
diff --git a/lddtree.py b/lddtree.py |
39 |
index 141195b..cdb3f1c 100755 |
40 |
--- a/lddtree.py |
41 |
+++ b/lddtree.py |
42 |
@@ -55,107 +55,107 @@ from elftools.common import exceptions |
43 |
|
44 |
|
45 |
def warn(msg, prefix='warning'): |
46 |
- """Write |msg| to stderr with a |prefix| before it""" |
47 |
- print('%s: %s: %s' % (os.path.basename(sys.argv[0]), prefix, msg), file=sys.stderr) |
48 |
+ """Write |msg| to stderr with a |prefix| before it""" |
49 |
+ print('%s: %s: %s' % (os.path.basename(sys.argv[0]), prefix, msg), file=sys.stderr) |
50 |
|
51 |
|
52 |
def err(msg, status=1): |
53 |
- """Write |msg| to stderr and exit with |status|""" |
54 |
- warn(msg, prefix='error') |
55 |
- sys.exit(status) |
56 |
+ """Write |msg| to stderr and exit with |status|""" |
57 |
+ warn(msg, prefix='error') |
58 |
+ sys.exit(status) |
59 |
|
60 |
|
61 |
def dbg(debug, *args, **kwargs): |
62 |
- """Pass |args| and |kwargs| to print() when |debug| is True""" |
63 |
- if debug: |
64 |
- print(*args, **kwargs) |
65 |
+ """Pass |args| and |kwargs| to print() when |debug| is True""" |
66 |
+ if debug: |
67 |
+ print(*args, **kwargs) |
68 |
|
69 |
|
70 |
def bstr(buf): |
71 |
- """Decode the byte string into a string""" |
72 |
- if isinstance(buf, str): |
73 |
- return buf |
74 |
- return buf.decode('utf-8') |
75 |
+ """Decode the byte string into a string""" |
76 |
+ if isinstance(buf, str): |
77 |
+ return buf |
78 |
+ return buf.decode('utf-8') |
79 |
|
80 |
|
81 |
def normpath(path): |
82 |
- """Normalize a path |
83 |
+ """Normalize a path |
84 |
|
85 |
- Python's os.path.normpath() doesn't handle some cases: |
86 |
- // -> // |
87 |
- //..// -> // |
88 |
- //..//..// -> /// |
89 |
- """ |
90 |
- return os.path.normpath(path).replace('//', '/') |
91 |
+ Python's os.path.normpath() doesn't handle some cases: |
92 |
+ // -> // |
93 |
+ //..// -> // |
94 |
+ //..//..// -> /// |
95 |
+ """ |
96 |
+ return os.path.normpath(path).replace('//', '/') |
97 |
|
98 |
|
99 |
def readlink(path, root, prefixed=False): |
100 |
- """Like os.readlink(), but relative to a |root| |
101 |
+ """Like os.readlink(), but relative to a |root| |
102 |
|
103 |
- This does not currently handle the pathological case: |
104 |
- /lib/foo.so -> ../../../../../../../foo.so |
105 |
- This relies on the .. entries in / to point to itself. |
106 |
+ This does not currently handle the pathological case: |
107 |
+ /lib/foo.so -> ../../../../../../../foo.so |
108 |
+ This relies on the .. entries in / to point to itself. |
109 |
|
110 |
- Args: |
111 |
- path: The symlink to read |
112 |
- root: The path to use for resolving absolute symlinks |
113 |
- prefixed: When False, the |path| must not have |root| prefixed to it, nor |
114 |
- will the return value have |root| prefixed. When True, |path| |
115 |
- must have |root| prefixed, and the return value will have |root| |
116 |
- added. |
117 |
+ Args: |
118 |
+ path: The symlink to read |
119 |
+ root: The path to use for resolving absolute symlinks |
120 |
+ prefixed: When False, the |path| must not have |root| prefixed to it, nor |
121 |
+ will the return value have |root| prefixed. When True, |path| |
122 |
+ must have |root| prefixed, and the return value will have |root| |
123 |
+ added. |
124 |
|
125 |
- Returns: |
126 |
- A fully resolved symlink path |
127 |
- """ |
128 |
- root = root.rstrip('/') |
129 |
- if prefixed: |
130 |
- path = path[len(root):] |
131 |
+ Returns: |
132 |
+ A fully resolved symlink path |
133 |
+ """ |
134 |
+ root = root.rstrip('/') |
135 |
+ if prefixed: |
136 |
+ path = path[len(root):] |
137 |
|
138 |
- while os.path.islink(root + path): |
139 |
- path = os.path.join(os.path.dirname(path), os.readlink(root + path)) |
140 |
+ while os.path.islink(root + path): |
141 |
+ path = os.path.join(os.path.dirname(path), os.readlink(root + path)) |
142 |
|
143 |
- return normpath((root + path) if prefixed else path) |
144 |
+ return normpath((root + path) if prefixed else path) |
145 |
|
146 |
|
147 |
def makedirs(path): |
148 |
- """Like os.makedirs(), but ignore EEXIST errors""" |
149 |
- try: |
150 |
- os.makedirs(path) |
151 |
- except OSError as e: |
152 |
- if e.errno != errno.EEXIST: |
153 |
- raise |
154 |
+ """Like os.makedirs(), but ignore EEXIST errors""" |
155 |
+ try: |
156 |
+ os.makedirs(path) |
157 |
+ except OSError as e: |
158 |
+ if e.errno != errno.EEXIST: |
159 |
+ raise |
160 |
|
161 |
|
162 |
def dedupe(items): |
163 |
- """Remove all duplicates from |items| (keeping order)""" |
164 |
- seen = {} |
165 |
- return [seen.setdefault(x, x) for x in items if x not in seen] |
166 |
+ """Remove all duplicates from |items| (keeping order)""" |
167 |
+ seen = {} |
168 |
+ return [seen.setdefault(x, x) for x in items if x not in seen] |
169 |
|
170 |
|
171 |
def GenerateLdsoWrapper(root, path, interp, libpaths=()): |
172 |
- """Generate a shell script wrapper which uses local ldso to run the ELF |
173 |
- |
174 |
- Since we cannot rely on the host glibc (or other libraries), we need to |
175 |
- execute the local packaged ldso directly and tell it where to find our |
176 |
- copies of libraries. |
177 |
- |
178 |
- Args: |
179 |
- root: The root tree to generate scripts inside of |
180 |
- path: The full path (inside |root|) to the program to wrap |
181 |
- interp: The ldso interpreter that we need to execute |
182 |
- libpaths: Extra lib paths to search for libraries |
183 |
- """ |
184 |
- basedir = os.path.dirname(path) |
185 |
- interp_dir, interp_name = os.path.split(interp) |
186 |
- # Add ldso interpreter dir to end of libpaths as a fallback library path. |
187 |
- libpaths = dedupe(list(libpaths) + [interp_dir]) |
188 |
- replacements = { |
189 |
- 'interp': os.path.join(os.path.relpath(interp_dir, basedir), |
190 |
- interp_name), |
191 |
- 'libpaths': ':'.join(['${basedir}/' + os.path.relpath(p, basedir) |
192 |
- for p in libpaths]), |
193 |
- } |
194 |
- wrapper = """#!/bin/sh |
195 |
+ """Generate a shell script wrapper which uses local ldso to run the ELF |
196 |
+ |
197 |
+ Since we cannot rely on the host glibc (or other libraries), we need to |
198 |
+ execute the local packaged ldso directly and tell it where to find our |
199 |
+ copies of libraries. |
200 |
+ |
201 |
+ Args: |
202 |
+ root: The root tree to generate scripts inside of |
203 |
+ path: The full path (inside |root|) to the program to wrap |
204 |
+ interp: The ldso interpreter that we need to execute |
205 |
+ libpaths: Extra lib paths to search for libraries |
206 |
+ """ |
207 |
+ basedir = os.path.dirname(path) |
208 |
+ interp_dir, interp_name = os.path.split(interp) |
209 |
+ # Add ldso interpreter dir to end of libpaths as a fallback library path. |
210 |
+ libpaths = dedupe(list(libpaths) + [interp_dir]) |
211 |
+ replacements = { |
212 |
+ 'interp': os.path.join(os.path.relpath(interp_dir, basedir), |
213 |
+ interp_name), |
214 |
+ 'libpaths': ':'.join(['${basedir}/' + os.path.relpath(p, basedir) |
215 |
+ for p in libpaths]), |
216 |
+ } |
217 |
+ wrapper = """#!/bin/sh |
218 |
if ! base=$(realpath "$0" 2>/dev/null); then |
219 |
case $0 in |
220 |
/*) base=$0;; |
221 |
@@ -171,635 +171,635 @@ exec \ |
222 |
"${base}.elf" \ |
223 |
"$@" |
224 |
""" |
225 |
- wrappath = root + path |
226 |
- os.rename(wrappath, wrappath + '.elf') |
227 |
- with open(wrappath, 'w') as f: |
228 |
- f.write(wrapper % replacements) |
229 |
- os.chmod(wrappath, 0o0755) |
230 |
+ wrappath = root + path |
231 |
+ os.rename(wrappath, wrappath + '.elf') |
232 |
+ with open(wrappath, 'w') as f: |
233 |
+ f.write(wrapper % replacements) |
234 |
+ os.chmod(wrappath, 0o0755) |
235 |
|
236 |
|
237 |
def ParseLdPaths(str_ldpaths, root='', path=None): |
238 |
- """Parse the colon-delimited list of paths and apply ldso rules to each |
239 |
- |
240 |
- Note the special handling as dictated by the ldso: |
241 |
- - Empty paths are equivalent to $PWD |
242 |
- - $ORIGIN is expanded to the path of the given file |
243 |
- - (TODO) $LIB and friends |
244 |
- |
245 |
- Args: |
246 |
- str_ldpaths: A colon-delimited string of paths |
247 |
- root: The path to prepend to all paths found |
248 |
- path: The object actively being parsed (used for $ORIGIN) |
249 |
- |
250 |
- Returns: |
251 |
- list of processed paths |
252 |
- """ |
253 |
- ldpaths = [] |
254 |
- for ldpath in str_ldpaths.split(':'): |
255 |
- if not ldpath: |
256 |
- # The ldso treats "" paths as $PWD. |
257 |
- ldpath = os.getcwd() |
258 |
- elif '$ORIGIN' in ldpath: |
259 |
- ldpath = ldpath.replace('$ORIGIN', os.path.dirname(path)) |
260 |
- else: |
261 |
- ldpath = root + ldpath |
262 |
- ldpaths.append(normpath(ldpath)) |
263 |
- return dedupe(ldpaths) |
264 |
+ """Parse the colon-delimited list of paths and apply ldso rules to each |
265 |
+ |
266 |
+ Note the special handling as dictated by the ldso: |
267 |
+ - Empty paths are equivalent to $PWD |
268 |
+ - $ORIGIN is expanded to the path of the given file |
269 |
+ - (TODO) $LIB and friends |
270 |
+ |
271 |
+ Args: |
272 |
+ str_ldpaths: A colon-delimited string of paths |
273 |
+ root: The path to prepend to all paths found |
274 |
+ path: The object actively being parsed (used for $ORIGIN) |
275 |
+ |
276 |
+ Returns: |
277 |
+ list of processed paths |
278 |
+ """ |
279 |
+ ldpaths = [] |
280 |
+ for ldpath in str_ldpaths.split(':'): |
281 |
+ if not ldpath: |
282 |
+ # The ldso treats "" paths as $PWD. |
283 |
+ ldpath = os.getcwd() |
284 |
+ elif '$ORIGIN' in ldpath: |
285 |
+ ldpath = ldpath.replace('$ORIGIN', os.path.dirname(path)) |
286 |
+ else: |
287 |
+ ldpath = root + ldpath |
288 |
+ ldpaths.append(normpath(ldpath)) |
289 |
+ return dedupe(ldpaths) |
290 |
|
291 |
|
292 |
def ParseLdSoConf(ldso_conf, root='/', debug=False, _first=True): |
293 |
- """Load all the paths from a given ldso config file |
294 |
- |
295 |
- This should handle comments, whitespace, and "include" statements. |
296 |
- |
297 |
- Args: |
298 |
- ldso_conf: The file to scan |
299 |
- root: The path to prepend to all paths found |
300 |
- debug: Enable debug output |
301 |
- _first: Recursive use only; is this the first ELF ? |
302 |
- |
303 |
- Returns: |
304 |
- list of paths found |
305 |
- """ |
306 |
- paths = [] |
307 |
- |
308 |
- dbg_pfx = '' if _first else ' ' |
309 |
- try: |
310 |
- dbg(debug, '%sParseLdSoConf(%s)' % (dbg_pfx, ldso_conf)) |
311 |
- with open(ldso_conf) as f: |
312 |
- for line in f.readlines(): |
313 |
- line = line.split('#', 1)[0].strip() |
314 |
- if not line: |
315 |
- continue |
316 |
- if line.startswith('include '): |
317 |
- line = line[8:] |
318 |
- if line[0] == '/': |
319 |
- line = root + line.lstrip('/') |
320 |
- else: |
321 |
- line = os.path.dirname(ldso_conf) + '/' + line |
322 |
- dbg(debug, '%s glob: %s' % (dbg_pfx, line)) |
323 |
- # ldconfig in glibc uses glob() which returns entries sorted according |
324 |
- # to LC_COLLATE. Further, ldconfig does not reset that but respects |
325 |
- # the active env settings (which might be a mistake). Python does not |
326 |
- # sort its results by default though, so do it ourselves. |
327 |
- for path in sorted(glob.glob(line)): |
328 |
- paths += ParseLdSoConf(path, root=root, debug=debug, _first=False) |
329 |
- else: |
330 |
- paths += [normpath(root + line)] |
331 |
- except IOError as e: |
332 |
- if e.errno != errno.ENOENT: |
333 |
- warn(e) |
334 |
+ """Load all the paths from a given ldso config file |
335 |
+ |
336 |
+ This should handle comments, whitespace, and "include" statements. |
337 |
|
338 |
- if _first: |
339 |
- # XXX: Load paths from ldso itself. |
340 |
- # Remove duplicate entries to speed things up. |
341 |
- paths = dedupe(paths) |
342 |
+ Args: |
343 |
+ ldso_conf: The file to scan |
344 |
+ root: The path to prepend to all paths found |
345 |
+ debug: Enable debug output |
346 |
+ _first: Recursive use only; is this the first ELF ? |
347 |
|
348 |
- return paths |
349 |
+ Returns: |
350 |
+ list of paths found |
351 |
+ """ |
352 |
+ paths = [] |
353 |
+ |
354 |
+ dbg_pfx = '' if _first else ' ' |
355 |
+ try: |
356 |
+ dbg(debug, '%sParseLdSoConf(%s)' % (dbg_pfx, ldso_conf)) |
357 |
+ with open(ldso_conf) as f: |
358 |
+ for line in f.readlines(): |
359 |
+ line = line.split('#', 1)[0].strip() |
360 |
+ if not line: |
361 |
+ continue |
362 |
+ if line.startswith('include '): |
363 |
+ line = line[8:] |
364 |
+ if line[0] == '/': |
365 |
+ line = root + line.lstrip('/') |
366 |
+ else: |
367 |
+ line = os.path.dirname(ldso_conf) + '/' + line |
368 |
+ dbg(debug, '%s glob: %s' % (dbg_pfx, line)) |
369 |
+ # ldconfig in glibc uses glob() which returns entries sorted according |
370 |
+ # to LC_COLLATE. Further, ldconfig does not reset that but respects |
371 |
+ # the active env settings (which might be a mistake). Python does not |
372 |
+ # sort its results by default though, so do it ourselves. |
373 |
+ for path in sorted(glob.glob(line)): |
374 |
+ paths += ParseLdSoConf(path, root=root, debug=debug, _first=False) |
375 |
+ else: |
376 |
+ paths += [normpath(root + line)] |
377 |
+ except IOError as e: |
378 |
+ if e.errno != errno.ENOENT: |
379 |
+ warn(e) |
380 |
+ |
381 |
+ if _first: |
382 |
+ # XXX: Load paths from ldso itself. |
383 |
+ # Remove duplicate entries to speed things up. |
384 |
+ paths = dedupe(paths) |
385 |
+ |
386 |
+ return paths |
387 |
|
388 |
|
389 |
def LoadLdpaths(root='/', prefix='', debug=False): |
390 |
- """Load linker paths from common locations |
391 |
- |
392 |
- This parses the ld.so.conf and LD_LIBRARY_PATH env var. |
393 |
- |
394 |
- Args: |
395 |
- root: The root tree to prepend to paths |
396 |
- prefix: The path under |root| to search |
397 |
- debug: Enable debug output |
398 |
- |
399 |
- Returns: |
400 |
- dict containing library paths to search |
401 |
- """ |
402 |
- ldpaths = { |
403 |
- 'conf': [], |
404 |
- 'env': [], |
405 |
- 'interp': [], |
406 |
- } |
407 |
- |
408 |
- # Load up $LD_LIBRARY_PATH. |
409 |
- ldpaths['env'] = [] |
410 |
- env_ldpath = os.environ.get('LD_LIBRARY_PATH') |
411 |
- if not env_ldpath is None: |
412 |
- if root != '/': |
413 |
- warn('ignoring LD_LIBRARY_PATH due to ROOT usage') |
414 |
- else: |
415 |
- # XXX: If this contains $ORIGIN, we probably have to parse this |
416 |
- # on a per-ELF basis so it can get turned into the right thing. |
417 |
- ldpaths['env'] = ParseLdPaths(env_ldpath, path='') |
418 |
+ """Load linker paths from common locations |
419 |
+ |
420 |
+ This parses the ld.so.conf and LD_LIBRARY_PATH env var. |
421 |
+ |
422 |
+ Args: |
423 |
+ root: The root tree to prepend to paths |
424 |
+ prefix: The path under |root| to search |
425 |
+ debug: Enable debug output |
426 |
+ |
427 |
+ Returns: |
428 |
+ dict containing library paths to search |
429 |
+ """ |
430 |
+ ldpaths = { |
431 |
+ 'conf': [], |
432 |
+ 'env': [], |
433 |
+ 'interp': [], |
434 |
+ } |
435 |
|
436 |
- # Load up /etc/ld.so.conf. |
437 |
- ldpaths['conf'] = ParseLdSoConf(root + prefix + '/etc/ld.so.conf', root=root, |
438 |
- debug=debug) |
439 |
+ # Load up $LD_LIBRARY_PATH. |
440 |
+ ldpaths['env'] = [] |
441 |
+ env_ldpath = os.environ.get('LD_LIBRARY_PATH') |
442 |
+ if not env_ldpath is None: |
443 |
+ if root != '/': |
444 |
+ warn('ignoring LD_LIBRARY_PATH due to ROOT usage') |
445 |
+ else: |
446 |
+ # XXX: If this contains $ORIGIN, we probably have to parse this |
447 |
+ # on a per-ELF basis so it can get turned into the right thing. |
448 |
+ ldpaths['env'] = ParseLdPaths(env_ldpath, path='') |
449 |
+ |
450 |
+ # Load up /etc/ld.so.conf. |
451 |
+ ldpaths['conf'] = ParseLdSoConf(root + prefix + '/etc/ld.so.conf', root=root, |
452 |
+ debug=debug) |
453 |
|
454 |
- return ldpaths |
455 |
+ return ldpaths |
456 |
|
457 |
|
458 |
def CompatibleELFs(elf1, elf2): |
459 |
- """See if two ELFs are compatible |
460 |
+ """See if two ELFs are compatible |
461 |
|
462 |
- This compares the aspects of the ELF to see if they're compatible: |
463 |
- bit size, endianness, machine type, and operating system. |
464 |
+ This compares the aspects of the ELF to see if they're compatible: |
465 |
+ bit size, endianness, machine type, and operating system. |
466 |
|
467 |
- Args: |
468 |
- elf1: an ELFFile object |
469 |
- elf2: an ELFFile object |
470 |
+ Args: |
471 |
+ elf1: an ELFFile object |
472 |
+ elf2: an ELFFile object |
473 |
|
474 |
- Returns: |
475 |
- True if compatible, False otherwise |
476 |
- """ |
477 |
- osabis = frozenset([e.header['e_ident']['EI_OSABI'] for e in (elf1, elf2)]) |
478 |
- compat_sets = ( |
479 |
- frozenset('ELFOSABI_%s' % x for x in ('NONE', 'SYSV', 'GNU', 'LINUX',)), |
480 |
- ) |
481 |
- return ((len(osabis) == 1 or any(osabis.issubset(x) for x in compat_sets)) and |
482 |
- elf1.elfclass == elf2.elfclass and |
483 |
- elf1.little_endian == elf2.little_endian and |
484 |
- elf1.header['e_machine'] == elf2.header['e_machine']) |
485 |
+ Returns: |
486 |
+ True if compatible, False otherwise |
487 |
+ """ |
488 |
+ osabis = frozenset([e.header['e_ident']['EI_OSABI'] for e in (elf1, elf2)]) |
489 |
+ compat_sets = ( |
490 |
+ frozenset('ELFOSABI_%s' % x for x in ('NONE', 'SYSV', 'GNU', 'LINUX',)), |
491 |
+ ) |
492 |
+ return ((len(osabis) == 1 or any(osabis.issubset(x) for x in compat_sets)) and |
493 |
+ elf1.elfclass == elf2.elfclass and |
494 |
+ elf1.little_endian == elf2.little_endian and |
495 |
+ elf1.header['e_machine'] == elf2.header['e_machine']) |
496 |
|
497 |
|
498 |
def FindLib(elf, lib, ldpaths, root='/', debug=False): |
499 |
- """Try to locate a |lib| that is compatible to |elf| in the given |ldpaths| |
500 |
- |
501 |
- Args: |
502 |
- elf: The elf which the library should be compatible with (ELF wise) |
503 |
- lib: The library (basename) to search for |
504 |
- ldpaths: A list of paths to search |
505 |
- root: The root path to resolve symlinks |
506 |
- debug: Enable debug output |
507 |
- |
508 |
- Returns: |
509 |
- Tuple of the full path to the desired library and the real path to it |
510 |
- """ |
511 |
- dbg(debug, ' FindLib(%s)' % lib) |
512 |
- |
513 |
- for ldpath in ldpaths: |
514 |
- path = os.path.join(ldpath, lib) |
515 |
- target = readlink(path, root, prefixed=True) |
516 |
- if path != target: |
517 |
- dbg(debug, ' checking: %s -> %s' % (path, target)) |
518 |
- else: |
519 |
- dbg(debug, ' checking:', path) |
520 |
+ """Try to locate a |lib| that is compatible to |elf| in the given |ldpaths| |
521 |
+ |
522 |
+ Args: |
523 |
+ elf: The elf which the library should be compatible with (ELF wise) |
524 |
+ lib: The library (basename) to search for |
525 |
+ ldpaths: A list of paths to search |
526 |
+ root: The root path to resolve symlinks |
527 |
+ debug: Enable debug output |
528 |
+ |
529 |
+ Returns: |
530 |
+ Tuple of the full path to the desired library and the real path to it |
531 |
+ """ |
532 |
+ dbg(debug, ' FindLib(%s)' % lib) |
533 |
+ |
534 |
+ for ldpath in ldpaths: |
535 |
+ path = os.path.join(ldpath, lib) |
536 |
+ target = readlink(path, root, prefixed=True) |
537 |
+ if path != target: |
538 |
+ dbg(debug, ' checking: %s -> %s' % (path, target)) |
539 |
+ else: |
540 |
+ dbg(debug, ' checking:', path) |
541 |
|
542 |
- if os.path.exists(target): |
543 |
- with open(target, 'rb') as f: |
544 |
- try: |
545 |
- libelf = ELFFile(f) |
546 |
- if CompatibleELFs(elf, libelf): |
547 |
- return (target, path) |
548 |
- except exceptions.ELFError as e: |
549 |
- warn('%s: %s' % (target, e)) |
550 |
+ if os.path.exists(target): |
551 |
+ with open(target, 'rb') as f: |
552 |
+ try: |
553 |
+ libelf = ELFFile(f) |
554 |
+ if CompatibleELFs(elf, libelf): |
555 |
+ return (target, path) |
556 |
+ except exceptions.ELFError as e: |
557 |
+ warn('%s: %s' % (target, e)) |
558 |
|
559 |
- return (None, None) |
560 |
+ return (None, None) |
561 |
|
562 |
|
563 |
# We abuse the _all_libs state. We probably shouldn't, but we do currently. |
564 |
# pylint: disable=dangerous-default-value |
565 |
def ParseELF(path, root='/', prefix='', ldpaths={'conf':[], 'env':[], 'interp':[]}, |
566 |
display=None, debug=False, _first=True, _all_libs={}): |
567 |
- """Parse the ELF dependency tree of the specified file |
568 |
+ """Parse the ELF dependency tree of the specified file |
569 |
|
570 |
- Args: |
571 |
- path: The ELF to scan |
572 |
- root: The root tree to prepend to paths; this applies to interp and rpaths |
573 |
+ Args: |
574 |
+ path: The ELF to scan |
575 |
+ root: The root tree to prepend to paths; this applies to interp and rpaths |
576 |
only as |path| and |ldpaths| are expected to be prefixed already |
577 |
- prefix: The path under |root| to search |
578 |
- ldpaths: dict containing library paths to search; should have the keys: |
579 |
- conf, env, interp |
580 |
- display: The path to show rather than |path| |
581 |
- debug: Enable debug output |
582 |
- _first: Recursive use only; is this the first ELF ? |
583 |
- _all_libs: Recursive use only; dict of all libs we've seen |
584 |
- |
585 |
- Returns: |
586 |
- a dict containing information about all the ELFs; e.g. |
587 |
- { |
588 |
- 'interp': '/lib64/ld-linux.so.2', |
589 |
- 'needed': ['libc.so.6', 'libcurl.so.4',], |
590 |
- 'libs': { |
591 |
- 'libc.so.6': { |
592 |
- 'path': '/lib64/libc.so.6', |
593 |
- 'needed': [], |
594 |
- }, |
595 |
- 'libcurl.so.4': { |
596 |
- 'path': '/usr/lib64/libcurl.so.4', |
597 |
- 'needed': ['libc.so.6', 'librt.so.1',], |
598 |
+ prefix: The path under |root| to search |
599 |
+ ldpaths: dict containing library paths to search; should have the keys: |
600 |
+ conf, env, interp |
601 |
+ display: The path to show rather than |path| |
602 |
+ debug: Enable debug output |
603 |
+ _first: Recursive use only; is this the first ELF ? |
604 |
+ _all_libs: Recursive use only; dict of all libs we've seen |
605 |
+ |
606 |
+ Returns: |
607 |
+ a dict containing information about all the ELFs; e.g. |
608 |
+ { |
609 |
+ 'interp': '/lib64/ld-linux.so.2', |
610 |
+ 'needed': ['libc.so.6', 'libcurl.so.4',], |
611 |
+ 'libs': { |
612 |
+ 'libc.so.6': { |
613 |
+ 'path': '/lib64/libc.so.6', |
614 |
+ 'needed': [], |
615 |
+ }, |
616 |
+ 'libcurl.so.4': { |
617 |
+ 'path': '/usr/lib64/libcurl.so.4', |
618 |
+ 'needed': ['libc.so.6', 'librt.so.1',], |
619 |
+ }, |
620 |
}, |
621 |
- }, |
622 |
- } |
623 |
- """ |
624 |
- if _first: |
625 |
- _all_libs = {} |
626 |
- ldpaths = ldpaths.copy() |
627 |
- ret = { |
628 |
- 'interp': None, |
629 |
- 'path': path if display is None else display, |
630 |
- 'realpath': path, |
631 |
- 'needed': [], |
632 |
- 'rpath': [], |
633 |
- 'runpath': [], |
634 |
- 'libs': _all_libs, |
635 |
- } |
636 |
- |
637 |
- dbg(debug, 'ParseELF(%s)' % path) |
638 |
- |
639 |
- with open(path, 'rb') as f: |
640 |
- elf = ELFFile(f) |
641 |
- |
642 |
- # If this is the first ELF, extract the interpreter. |
643 |
+ } |
644 |
+ """ |
645 |
if _first: |
646 |
- for segment in elf.iter_segments(): |
647 |
- if segment.header.p_type != 'PT_INTERP': |
648 |
- continue |
649 |
- |
650 |
- interp = bstr(segment.get_interp_name()) |
651 |
- dbg(debug, ' interp =', interp) |
652 |
- ret['interp'] = normpath(root + interp) |
653 |
- real_interp = readlink(ret['interp'], root, prefixed=True) |
654 |
- ret['libs'][os.path.basename(interp)] = { |
655 |
- 'path': ret['interp'], |
656 |
- 'realpath': real_interp, |
657 |
- 'needed': [], |
658 |
- } |
659 |
- # XXX: Could read it and scan for /lib paths. |
660 |
- # If the interp is a symlink, lets follow it on the assumption that it |
661 |
- # is in this path purely for ABI reasons, and the distro is using a |
662 |
- # different (probably more correct) path. This can come up in some |
663 |
- # multilib situations like s390x where /lib64/ contains all the native |
664 |
- # libraries, but /lib/ld64.so.1 is the interp hardcoded in gcc, so the |
665 |
- # ld64.so.1 is really a symlink to ../lib64/ld64.so.1. In the multiarch |
666 |
- # setup, it'll be /lib/ld64.so.1 -> /lib/s390x-linux-gnu/ld64.so.1. |
667 |
- # That is why we use |real_interp| here instead of |interp|. |
668 |
- ldpaths['interp'] = [ |
669 |
- os.path.dirname(real_interp), |
670 |
- normpath(root + prefix + '/usr/' + os.path.dirname( |
671 |
- real_interp)[len(root) + len(prefix):]), |
672 |
- ] |
673 |
- dbg(debug, ' ldpaths[interp] =', ldpaths['interp']) |
674 |
- break |
675 |
- |
676 |
- # Parse the ELF's dynamic tags. |
677 |
- libs = [] |
678 |
- rpaths = [] |
679 |
- runpaths = [] |
680 |
- for segment in elf.iter_segments(): |
681 |
- if segment.header.p_type != 'PT_DYNAMIC': |
682 |
- continue |
683 |
- |
684 |
- for t in segment.iter_tags(): |
685 |
- if t.entry.d_tag == 'DT_RPATH': |
686 |
- rpaths = ParseLdPaths(bstr(t.rpath), root=root, path=path) |
687 |
- elif t.entry.d_tag == 'DT_RUNPATH': |
688 |
- runpaths = ParseLdPaths(bstr(t.runpath), root=root, path=path) |
689 |
- elif t.entry.d_tag == 'DT_NEEDED': |
690 |
- libs.append(bstr(t.needed)) |
691 |
- if runpaths: |
692 |
- # If both RPATH and RUNPATH are set, only the latter is used. |
693 |
- rpaths = [] |
694 |
+ _all_libs = {} |
695 |
+ ldpaths = ldpaths.copy() |
696 |
+ ret = { |
697 |
+ 'interp': None, |
698 |
+ 'path': path if display is None else display, |
699 |
+ 'realpath': path, |
700 |
+ 'needed': [], |
701 |
+ 'rpath': [], |
702 |
+ 'runpath': [], |
703 |
+ 'libs': _all_libs, |
704 |
+ } |
705 |
|
706 |
- # XXX: We assume there is only one PT_DYNAMIC. This is |
707 |
- # probably fine since the runtime ldso does the same. |
708 |
- break |
709 |
- if _first: |
710 |
- # Propagate the rpaths used by the main ELF since those will be |
711 |
- # used at runtime to locate things. |
712 |
- ldpaths['rpath'] = rpaths |
713 |
- ldpaths['runpath'] = runpaths |
714 |
- dbg(debug, ' ldpaths[rpath] =', rpaths) |
715 |
- dbg(debug, ' ldpaths[runpath] =', runpaths) |
716 |
- ret['rpath'] = rpaths |
717 |
- ret['runpath'] = runpaths |
718 |
- ret['needed'] = libs |
719 |
- |
720 |
- # Search for the libs this ELF uses. |
721 |
- all_ldpaths = None |
722 |
- for lib in libs: |
723 |
- if lib in _all_libs: |
724 |
- continue |
725 |
- if all_ldpaths is None: |
726 |
- all_ldpaths = rpaths + ldpaths['rpath'] + ldpaths['env'] + runpaths + ldpaths['runpath'] + ldpaths['conf'] + ldpaths['interp'] |
727 |
- realpath, fullpath = FindLib(elf, lib, all_ldpaths, root, debug=debug) |
728 |
- _all_libs[lib] = { |
729 |
- 'realpath': realpath, |
730 |
- 'path': fullpath, |
731 |
- 'needed': [], |
732 |
- } |
733 |
- if fullpath: |
734 |
- try: |
735 |
- lret = ParseELF(realpath, root, prefix, ldpaths, display=fullpath, |
736 |
- debug=debug, _first=False, _all_libs=_all_libs) |
737 |
- except exceptions.ELFError as e: |
738 |
- warn('%s: %s' % (realpath, e)) |
739 |
- _all_libs[lib]['needed'] = lret['needed'] |
740 |
+ dbg(debug, 'ParseELF(%s)' % path) |
741 |
+ |
742 |
+ with open(path, 'rb') as f: |
743 |
+ elf = ELFFile(f) |
744 |
+ |
745 |
+ # If this is the first ELF, extract the interpreter. |
746 |
+ if _first: |
747 |
+ for segment in elf.iter_segments(): |
748 |
+ if segment.header.p_type != 'PT_INTERP': |
749 |
+ continue |
750 |
+ |
751 |
+ interp = bstr(segment.get_interp_name()) |
752 |
+ dbg(debug, ' interp =', interp) |
753 |
+ ret['interp'] = normpath(root + interp) |
754 |
+ real_interp = readlink(ret['interp'], root, prefixed=True) |
755 |
+ ret['libs'][os.path.basename(interp)] = { |
756 |
+ 'path': ret['interp'], |
757 |
+ 'realpath': real_interp, |
758 |
+ 'needed': [], |
759 |
+ } |
760 |
+ # XXX: Could read it and scan for /lib paths. |
761 |
+ # If the interp is a symlink, lets follow it on the assumption that it |
762 |
+ # is in this path purely for ABI reasons, and the distro is using a |
763 |
+ # different (probably more correct) path. This can come up in some |
764 |
+ # multilib situations like s390x where /lib64/ contains all the native |
765 |
+ # libraries, but /lib/ld64.so.1 is the interp hardcoded in gcc, so the |
766 |
+ # ld64.so.1 is really a symlink to ../lib64/ld64.so.1. In the multiarch |
767 |
+ # setup, it'll be /lib/ld64.so.1 -> /lib/s390x-linux-gnu/ld64.so.1. |
768 |
+ # That is why we use |real_interp| here instead of |interp|. |
769 |
+ ldpaths['interp'] = [ |
770 |
+ os.path.dirname(real_interp), |
771 |
+ normpath(root + prefix + '/usr/' + os.path.dirname( |
772 |
+ real_interp)[len(root) + len(prefix):]), |
773 |
+ ] |
774 |
+ dbg(debug, ' ldpaths[interp] =', ldpaths['interp']) |
775 |
+ break |
776 |
+ |
777 |
+ # Parse the ELF's dynamic tags. |
778 |
+ libs = [] |
779 |
+ rpaths = [] |
780 |
+ runpaths = [] |
781 |
+ for segment in elf.iter_segments(): |
782 |
+ if segment.header.p_type != 'PT_DYNAMIC': |
783 |
+ continue |
784 |
+ |
785 |
+ for t in segment.iter_tags(): |
786 |
+ if t.entry.d_tag == 'DT_RPATH': |
787 |
+ rpaths = ParseLdPaths(bstr(t.rpath), root=root, path=path) |
788 |
+ elif t.entry.d_tag == 'DT_RUNPATH': |
789 |
+ runpaths = ParseLdPaths(bstr(t.runpath), root=root, path=path) |
790 |
+ elif t.entry.d_tag == 'DT_NEEDED': |
791 |
+ libs.append(bstr(t.needed)) |
792 |
+ if runpaths: |
793 |
+ # If both RPATH and RUNPATH are set, only the latter is used. |
794 |
+ rpaths = [] |
795 |
+ |
796 |
+ # XXX: We assume there is only one PT_DYNAMIC. This is |
797 |
+ # probably fine since the runtime ldso does the same. |
798 |
+ break |
799 |
+ if _first: |
800 |
+ # Propagate the rpaths used by the main ELF since those will be |
801 |
+ # used at runtime to locate things. |
802 |
+ ldpaths['rpath'] = rpaths |
803 |
+ ldpaths['runpath'] = runpaths |
804 |
+ dbg(debug, ' ldpaths[rpath] =', rpaths) |
805 |
+ dbg(debug, ' ldpaths[runpath] =', runpaths) |
806 |
+ ret['rpath'] = rpaths |
807 |
+ ret['runpath'] = runpaths |
808 |
+ ret['needed'] = libs |
809 |
+ |
810 |
+ # Search for the libs this ELF uses. |
811 |
+ all_ldpaths = None |
812 |
+ for lib in libs: |
813 |
+ if lib in _all_libs: |
814 |
+ continue |
815 |
+ if all_ldpaths is None: |
816 |
+ all_ldpaths = rpaths + ldpaths['rpath'] + ldpaths['env'] + runpaths + ldpaths['runpath'] + ldpaths['conf'] + ldpaths['interp'] |
817 |
+ realpath, fullpath = FindLib(elf, lib, all_ldpaths, root, debug=debug) |
818 |
+ _all_libs[lib] = { |
819 |
+ 'realpath': realpath, |
820 |
+ 'path': fullpath, |
821 |
+ 'needed': [], |
822 |
+ } |
823 |
+ if fullpath: |
824 |
+ try: |
825 |
+ lret = ParseELF(realpath, root, prefix, ldpaths, display=fullpath, |
826 |
+ debug=debug, _first=False, _all_libs=_all_libs) |
827 |
+ except exceptions.ELFError as e: |
828 |
+ warn('%s: %s' % (realpath, e)) |
829 |
+ _all_libs[lib]['needed'] = lret['needed'] |
830 |
|
831 |
- del elf |
832 |
+ del elf |
833 |
|
834 |
- return ret |
835 |
+ return ret |
836 |
# pylint: enable=dangerous-default-value |
837 |
|
838 |
|
839 |
class _NormalizePathAction(argparse.Action): |
840 |
- def __call__(self, parser, namespace, values, option_string=None): |
841 |
- setattr(namespace, self.dest, normpath(values)) |
842 |
+ def __call__(self, parser, namespace, values, option_string=None): |
843 |
+ setattr(namespace, self.dest, normpath(values)) |
844 |
|
845 |
|
846 |
def _ActionShow(options, elf): |
847 |
- """Show the dependency tree for this ELF""" |
848 |
- def _show(lib, depth): |
849 |
- chain_libs.append(lib) |
850 |
- fullpath = elf['libs'][lib]['path'] |
851 |
+ """Show the dependency tree for this ELF""" |
852 |
+ def _show(lib, depth): |
853 |
+ chain_libs.append(lib) |
854 |
+ fullpath = elf['libs'][lib]['path'] |
855 |
+ if options.list: |
856 |
+ print(fullpath or lib) |
857 |
+ else: |
858 |
+ print('%s%s => %s' % (' ' * depth, lib, fullpath)) |
859 |
+ |
860 |
+ new_libs = [] |
861 |
+ for lib in elf['libs'][lib]['needed']: |
862 |
+ if lib in chain_libs: |
863 |
+ if not options.list: |
864 |
+ print('%s%s => !!! circular loop !!!' % (' ' * depth, lib)) |
865 |
+ continue |
866 |
+ if options.all or not lib in shown_libs: |
867 |
+ shown_libs.add(lib) |
868 |
+ new_libs.append(lib) |
869 |
+ |
870 |
+ for lib in new_libs: |
871 |
+ _show(lib, depth + 1) |
872 |
+ chain_libs.pop() |
873 |
+ |
874 |
+ shown_libs = set(elf['needed']) |
875 |
+ new_libs = elf['needed'][:] |
876 |
+ chain_libs = [] |
877 |
+ interp = elf['interp'] |
878 |
+ if interp: |
879 |
+ lib = os.path.basename(interp) |
880 |
+ shown_libs.add(lib) |
881 |
+ # If we are in non-list mode, then we want to show the "duplicate" interp |
882 |
+ # lines -- first the header (interp=>xxx), and then the DT_NEEDED line to |
883 |
+ # show that the ELF is directly linked against the interp. |
884 |
+ # If we're in list mode though, we only want to show the interp once. |
885 |
+ # Unless of course we have the --all flag active, then we show everything. |
886 |
+ if not options.all and options.list and lib in new_libs: |
887 |
+ new_libs.remove(lib) |
888 |
if options.list: |
889 |
- print(fullpath or lib) |
890 |
+ print(elf['path']) |
891 |
+ if not interp is None: |
892 |
+ print(interp) |
893 |
else: |
894 |
- print('%s%s => %s' % (' ' * depth, lib, fullpath)) |
895 |
- |
896 |
- new_libs = [] |
897 |
- for lib in elf['libs'][lib]['needed']: |
898 |
- if lib in chain_libs: |
899 |
- if not options.list: |
900 |
- print('%s%s => !!! circular loop !!!' % (' ' * depth, lib)) |
901 |
- continue |
902 |
- if options.all or not lib in shown_libs: |
903 |
- shown_libs.add(lib) |
904 |
- new_libs.append(lib) |
905 |
- |
906 |
+ print('%s (interpreter => %s)' % (elf['path'], interp)) |
907 |
for lib in new_libs: |
908 |
- _show(lib, depth + 1) |
909 |
- chain_libs.pop() |
910 |
- |
911 |
- shown_libs = set(elf['needed']) |
912 |
- new_libs = elf['needed'][:] |
913 |
- chain_libs = [] |
914 |
- interp = elf['interp'] |
915 |
- if interp: |
916 |
- lib = os.path.basename(interp) |
917 |
- shown_libs.add(lib) |
918 |
- # If we are in non-list mode, then we want to show the "duplicate" interp |
919 |
- # lines -- first the header (interp=>xxx), and then the DT_NEEDED line to |
920 |
- # show that the ELF is directly linked against the interp. |
921 |
- # If we're in list mode though, we only want to show the interp once. |
922 |
- # Unless of course we have the --all flag active, then we show everything. |
923 |
- if not options.all and options.list and lib in new_libs: |
924 |
- new_libs.remove(lib) |
925 |
- if options.list: |
926 |
- print(elf['path']) |
927 |
- if not interp is None: |
928 |
- print(interp) |
929 |
- else: |
930 |
- print('%s (interpreter => %s)' % (elf['path'], interp)) |
931 |
- for lib in new_libs: |
932 |
- _show(lib, 1) |
933 |
+ _show(lib, 1) |
934 |
|
935 |
|
936 |
def _ActionCopy(options, elf): |
937 |
- """Copy the ELF and its dependencies to a destination tree""" |
938 |
- def _StripRoot(path): |
939 |
- return path[len(options.root) - 1:] |
940 |
- |
941 |
- def _copy(realsrc, src, striproot=True, wrapit=False, libpaths=(), |
942 |
- outdir=None): |
943 |
- if realsrc is None: |
944 |
- return |
945 |
+ """Copy the ELF and its dependencies to a destination tree""" |
946 |
+ def _StripRoot(path): |
947 |
+ return path[len(options.root) - 1:] |
948 |
|
949 |
- if wrapit: |
950 |
- # Static ELFs don't need to be wrapped. |
951 |
- if not elf['interp']: |
952 |
- wrapit = False |
953 |
+ def _copy(realsrc, src, striproot=True, wrapit=False, libpaths=(), |
954 |
+ outdir=None): |
955 |
+ if realsrc is None: |
956 |
+ return |
957 |
|
958 |
- striproot = _StripRoot if striproot else lambda x: x |
959 |
+ if wrapit: |
960 |
+ # Static ELFs don't need to be wrapped. |
961 |
+ if not elf['interp']: |
962 |
+ wrapit = False |
963 |
|
964 |
- if outdir: |
965 |
- subdst = os.path.join(outdir, os.path.basename(src)) |
966 |
- else: |
967 |
- subdst = striproot(src) |
968 |
- dst = options.dest + subdst |
969 |
+ striproot = _StripRoot if striproot else lambda x: x |
970 |
|
971 |
- try: |
972 |
- # See if they're the same file. |
973 |
- nstat = os.stat(dst + ('.elf' if wrapit else '')) |
974 |
- ostat = os.stat(realsrc) |
975 |
- for field in ('mode', 'mtime', 'size'): |
976 |
- if getattr(ostat, 'st_' + field) != \ |
977 |
- getattr(nstat, 'st_' + field): |
978 |
- break |
979 |
- else: |
980 |
- return |
981 |
- except OSError as e: |
982 |
- if e.errno != errno.ENOENT: |
983 |
- raise |
984 |
+ if outdir: |
985 |
+ subdst = os.path.join(outdir, os.path.basename(src)) |
986 |
+ else: |
987 |
+ subdst = striproot(src) |
988 |
+ dst = options.dest + subdst |
989 |
|
990 |
- if options.verbose: |
991 |
- print('%s -> %s' % (src, dst)) |
992 |
+ try: |
993 |
+ # See if they're the same file. |
994 |
+ nstat = os.stat(dst + ('.elf' if wrapit else '')) |
995 |
+ ostat = os.stat(realsrc) |
996 |
+ for field in ('mode', 'mtime', 'size'): |
997 |
+ if getattr(ostat, 'st_' + field) != \ |
998 |
+ getattr(nstat, 'st_' + field): |
999 |
+ break |
1000 |
+ else: |
1001 |
+ return |
1002 |
+ except OSError as e: |
1003 |
+ if e.errno != errno.ENOENT: |
1004 |
+ raise |
1005 |
+ |
1006 |
+ if options.verbose: |
1007 |
+ print('%s -> %s' % (src, dst)) |
1008 |
+ |
1009 |
+ makedirs(os.path.dirname(dst)) |
1010 |
+ try: |
1011 |
+ shutil.copy2(realsrc, dst) |
1012 |
+ except IOError: |
1013 |
+ os.unlink(dst) |
1014 |
+ shutil.copy2(realsrc, dst) |
1015 |
+ |
1016 |
+ if wrapit: |
1017 |
+ if options.verbose: |
1018 |
+ print('generate wrapper %s' % (dst,)) |
1019 |
+ |
1020 |
+ if options.libdir: |
1021 |
+ interp = os.path.join(options.libdir, os.path.basename(elf['interp'])) |
1022 |
+ else: |
1023 |
+ interp = _StripRoot(elf['interp']) |
1024 |
+ GenerateLdsoWrapper(options.dest, subdst, interp, libpaths) |
1025 |
+ |
1026 |
+ # XXX: We should automatically import libgcc_s.so whenever libpthread.so |
1027 |
+ # is copied over (since we know it can be dlopen-ed by NPTL at runtime). |
1028 |
+ # Similarly, we should provide an option for automatically copying over |
1029 |
+ # the libnsl.so and libnss_*.so libraries, as well as an open ended list |
1030 |
+ # for known libs that get loaded (e.g. curl will dlopen(libresolv)). |
1031 |
+ uniq_libpaths = set() |
1032 |
+ for lib in elf['libs']: |
1033 |
+ libdata = elf['libs'][lib] |
1034 |
+ path = libdata['realpath'] |
1035 |
+ if path is None: |
1036 |
+ warn('could not locate library: %s' % lib) |
1037 |
+ continue |
1038 |
+ if not options.libdir: |
1039 |
+ uniq_libpaths.add(_StripRoot(os.path.dirname(path))) |
1040 |
+ _copy(path, libdata['path'], outdir=options.libdir) |
1041 |
|
1042 |
- makedirs(os.path.dirname(dst)) |
1043 |
- try: |
1044 |
- shutil.copy2(realsrc, dst) |
1045 |
- except IOError: |
1046 |
- os.unlink(dst) |
1047 |
- shutil.copy2(realsrc, dst) |
1048 |
- |
1049 |
- if wrapit: |
1050 |
- if options.verbose: |
1051 |
- print('generate wrapper %s' % (dst,)) |
1052 |
- |
1053 |
- if options.libdir: |
1054 |
- interp = os.path.join(options.libdir, os.path.basename(elf['interp'])) |
1055 |
- else: |
1056 |
- interp = _StripRoot(elf['interp']) |
1057 |
- GenerateLdsoWrapper(options.dest, subdst, interp, libpaths) |
1058 |
- |
1059 |
- # XXX: We should automatically import libgcc_s.so whenever libpthread.so |
1060 |
- # is copied over (since we know it can be dlopen-ed by NPTL at runtime). |
1061 |
- # Similarly, we should provide an option for automatically copying over |
1062 |
- # the libnsl.so and libnss_*.so libraries, as well as an open ended list |
1063 |
- # for known libs that get loaded (e.g. curl will dlopen(libresolv)). |
1064 |
- uniq_libpaths = set() |
1065 |
- for lib in elf['libs']: |
1066 |
- libdata = elf['libs'][lib] |
1067 |
- path = libdata['realpath'] |
1068 |
- if path is None: |
1069 |
- warn('could not locate library: %s' % lib) |
1070 |
- continue |
1071 |
if not options.libdir: |
1072 |
- uniq_libpaths.add(_StripRoot(os.path.dirname(path))) |
1073 |
- _copy(path, libdata['path'], outdir=options.libdir) |
1074 |
- |
1075 |
- if not options.libdir: |
1076 |
- libpaths = list(uniq_libpaths) |
1077 |
- if elf['runpath']: |
1078 |
- libpaths = elf['runpath'] + libpaths |
1079 |
+ libpaths = list(uniq_libpaths) |
1080 |
+ if elf['runpath']: |
1081 |
+ libpaths = elf['runpath'] + libpaths |
1082 |
+ else: |
1083 |
+ libpaths = elf['rpath'] + libpaths |
1084 |
else: |
1085 |
- libpaths = elf['rpath'] + libpaths |
1086 |
- else: |
1087 |
- uniq_libpaths.add(options.libdir) |
1088 |
- libpaths = list(uniq_libpaths) |
1089 |
+ uniq_libpaths.add(options.libdir) |
1090 |
+ libpaths = list(uniq_libpaths) |
1091 |
|
1092 |
- # We don't bother to copy this as ParseElf adds the interp to the 'libs', |
1093 |
- # so it was already copied in the libs loop above. |
1094 |
- #_copy(elf['interp'], outdir=options.libdir) |
1095 |
- _copy(elf['realpath'], elf['path'], striproot=options.auto_root, |
1096 |
- wrapit=options.generate_wrappers, libpaths=libpaths, |
1097 |
- outdir=options.bindir) |
1098 |
+ # We don't bother to copy this as ParseElf adds the interp to the 'libs', |
1099 |
+ # so it was already copied in the libs loop above. |
1100 |
+ #_copy(elf['interp'], outdir=options.libdir) |
1101 |
+ _copy(elf['realpath'], elf['path'], striproot=options.auto_root, |
1102 |
+ wrapit=options.generate_wrappers, libpaths=libpaths, |
1103 |
+ outdir=options.bindir) |
1104 |
|
1105 |
|
1106 |
def GetParser(): |
1107 |
- """Get a CLI parser.""" |
1108 |
- parser = argparse.ArgumentParser( |
1109 |
- description=__doc__, |
1110 |
- formatter_class=argparse.RawDescriptionHelpFormatter) |
1111 |
- parser.add_argument('-a', '--all', |
1112 |
- action='store_true', default=False, |
1113 |
- help='Show all duplicated dependencies') |
1114 |
- parser.add_argument('-R', '--root', |
1115 |
- default=os.environ.get('ROOT', ''), type=str, |
1116 |
- action=_NormalizePathAction, |
1117 |
- help='Search for all files/dependencies in ROOT') |
1118 |
- parser.add_argument('-P', '--prefix', |
1119 |
- default=os.environ.get( |
1120 |
- 'EPREFIX', '@GENTOO_PORTAGE_EPREFIX@'), type=str, |
1121 |
- action=_NormalizePathAction, |
1122 |
- help='Specify EPREFIX for binaries (for Gentoo Prefix)') |
1123 |
- parser.add_argument('--no-auto-root', |
1124 |
- dest='auto_root', action='store_false', default=True, |
1125 |
- help='Do not automatically prefix input ELFs with ROOT') |
1126 |
- parser.add_argument('-l', '--list', |
1127 |
- action='store_true', default=False, |
1128 |
- help='Display output in a simple list (easy for copying)') |
1129 |
- parser.add_argument('-x', '--debug', |
1130 |
- action='store_true', default=False, |
1131 |
- help='Run with debugging') |
1132 |
- parser.add_argument('-v', '--verbose', |
1133 |
- action='store_true', default=False, |
1134 |
- help='Be verbose') |
1135 |
- parser.add_argument('--skip-non-elfs', |
1136 |
- action='store_true', default=False, |
1137 |
- help='Skip plain (non-ELF) files instead of warning') |
1138 |
- parser.add_argument('-V', '--version', |
1139 |
- action='version', |
1140 |
- version='lddtree by Mike Frysinger <vapier@g.o>', |
1141 |
- help='Show version information') |
1142 |
- parser.add_argument('path', nargs='+') |
1143 |
- |
1144 |
- group = parser.add_argument_group('Copying options') |
1145 |
- group.add_argument('--copy-to-tree', |
1146 |
- dest='dest', default=None, type=str, |
1147 |
- action=_NormalizePathAction, |
1148 |
- help='Copy all files to the specified tree') |
1149 |
- group.add_argument('--bindir', |
1150 |
- default=None, type=str, |
1151 |
- action=_NormalizePathAction, |
1152 |
- help='Dir to store all ELFs specified on the command line') |
1153 |
- group.add_argument('--libdir', |
1154 |
- default=None, type=str, |
1155 |
- action=_NormalizePathAction, |
1156 |
- help='Dir to store all ELF libs') |
1157 |
- group.add_argument('--generate-wrappers', |
1158 |
- action='store_true', default=False, |
1159 |
- help='Wrap executable ELFs with scripts for local ldso') |
1160 |
- group.add_argument('--copy-non-elfs', |
1161 |
- action='store_true', default=False, |
1162 |
- help='Copy over plain (non-ELF) files instead of warn+ignore') |
1163 |
- |
1164 |
- return parser |
1165 |
+ """Get a CLI parser.""" |
1166 |
+ parser = argparse.ArgumentParser( |
1167 |
+ description=__doc__, |
1168 |
+ formatter_class=argparse.RawDescriptionHelpFormatter) |
1169 |
+ parser.add_argument('-a', '--all', |
1170 |
+ action='store_true', default=False, |
1171 |
+ help='Show all duplicated dependencies') |
1172 |
+ parser.add_argument('-R', '--root', |
1173 |
+ default=os.environ.get('ROOT', ''), type=str, |
1174 |
+ action=_NormalizePathAction, |
1175 |
+ help='Search for all files/dependencies in ROOT') |
1176 |
+ parser.add_argument('-P', '--prefix', |
1177 |
+ default=os.environ.get( |
1178 |
+ 'EPREFIX', '@GENTOO_PORTAGE_EPREFIX@'), type=str, |
1179 |
+ action=_NormalizePathAction, |
1180 |
+ help='Specify EPREFIX for binaries (for Gentoo Prefix)') |
1181 |
+ parser.add_argument('--no-auto-root', |
1182 |
+ dest='auto_root', action='store_false', default=True, |
1183 |
+ help='Do not automatically prefix input ELFs with ROOT') |
1184 |
+ parser.add_argument('-l', '--list', |
1185 |
+ action='store_true', default=False, |
1186 |
+ help='Display output in a simple list (easy for copying)') |
1187 |
+ parser.add_argument('-x', '--debug', |
1188 |
+ action='store_true', default=False, |
1189 |
+ help='Run with debugging') |
1190 |
+ parser.add_argument('-v', '--verbose', |
1191 |
+ action='store_true', default=False, |
1192 |
+ help='Be verbose') |
1193 |
+ parser.add_argument('--skip-non-elfs', |
1194 |
+ action='store_true', default=False, |
1195 |
+ help='Skip plain (non-ELF) files instead of warning') |
1196 |
+ parser.add_argument('-V', '--version', |
1197 |
+ action='version', |
1198 |
+ version='lddtree by Mike Frysinger <vapier@g.o>', |
1199 |
+ help='Show version information') |
1200 |
+ parser.add_argument('path', nargs='+') |
1201 |
+ |
1202 |
+ group = parser.add_argument_group('Copying options') |
1203 |
+ group.add_argument('--copy-to-tree', |
1204 |
+ dest='dest', default=None, type=str, |
1205 |
+ action=_NormalizePathAction, |
1206 |
+ help='Copy all files to the specified tree') |
1207 |
+ group.add_argument('--bindir', |
1208 |
+ default=None, type=str, |
1209 |
+ action=_NormalizePathAction, |
1210 |
+ help='Dir to store all ELFs specified on the command line') |
1211 |
+ group.add_argument('--libdir', |
1212 |
+ default=None, type=str, |
1213 |
+ action=_NormalizePathAction, |
1214 |
+ help='Dir to store all ELF libs') |
1215 |
+ group.add_argument('--generate-wrappers', |
1216 |
+ action='store_true', default=False, |
1217 |
+ help='Wrap executable ELFs with scripts for local ldso') |
1218 |
+ group.add_argument('--copy-non-elfs', |
1219 |
+ action='store_true', default=False, |
1220 |
+ help='Copy over plain (non-ELF) files instead of warn+ignore') |
1221 |
+ |
1222 |
+ return parser |
1223 |
|
1224 |
|
1225 |
def main(argv): |
1226 |
- """The main entry point!""" |
1227 |
- parser = GetParser() |
1228 |
- options = parser.parse_args(argv) |
1229 |
- paths = options.path |
1230 |
- |
1231 |
- if options.root != '/': |
1232 |
- options.root += '/' |
1233 |
- if options.prefix == '@''GENTOO_PORTAGE_EPREFIX''@': |
1234 |
- options.prefix = '' |
1235 |
- |
1236 |
- if options.bindir and options.bindir[0] != '/': |
1237 |
- parser.error('--bindir accepts absolute paths only') |
1238 |
- if options.libdir and options.libdir[0] != '/': |
1239 |
- parser.error('--libdir accepts absolute paths only') |
1240 |
- |
1241 |
- if options.skip_non_elfs and options.copy_non_elfs: |
1242 |
- parser.error('pick one handler for non-ELFs: skip or copy') |
1243 |
- |
1244 |
- dbg(options.debug, 'root =', options.root) |
1245 |
- if options.dest: |
1246 |
- dbg(options.debug, 'dest =', options.dest) |
1247 |
- if not paths: |
1248 |
- err('missing ELF files to scan') |
1249 |
- |
1250 |
- ldpaths = LoadLdpaths(options.root, options.prefix, debug=options.debug) |
1251 |
- dbg(options.debug, 'ldpaths[conf] =', ldpaths['conf']) |
1252 |
- dbg(options.debug, 'ldpaths[env] =', ldpaths['env']) |
1253 |
- |
1254 |
- # Process all the files specified. |
1255 |
- ret = 0 |
1256 |
- for path in paths: |
1257 |
- dbg(options.debug, 'argv[x] =', path) |
1258 |
- # Only auto-prefix the path if the ELF is absolute. |
1259 |
- # If it's a relative path, the user most likely wants |
1260 |
- # the local path. |
1261 |
- if options.auto_root and path.startswith('/'): |
1262 |
- path = options.root + path.lstrip('/') |
1263 |
- dbg(options.debug, ' +auto-root =', path) |
1264 |
- |
1265 |
- matched = False |
1266 |
- for p in glob.iglob(path): |
1267 |
- # Once we've processed the globs, resolve the symlink. This way you can |
1268 |
- # operate on a path that is an absolute symlink itself. e.g.: |
1269 |
- # $ ln -sf /bin/bash $PWD/root/bin/sh |
1270 |
- # $ lddtree --root $PWD/root /bin/sh |
1271 |
- # First we'd turn /bin/sh into $PWD/root/bin/sh, then we want to resolve |
1272 |
- # the symlink to $PWD/root/bin/bash rather than a plain /bin/bash. |
1273 |
- dbg(options.debug, ' globbed =', p) |
1274 |
- if not path.startswith('/'): |
1275 |
- realpath = os.path.realpath(path) |
1276 |
- elif options.auto_root: |
1277 |
- realpath = readlink(p, options.root, prefixed=True) |
1278 |
- else: |
1279 |
- realpath = path |
1280 |
- if path != realpath: |
1281 |
- dbg(options.debug, ' resolved =', realpath) |
1282 |
- |
1283 |
- matched = True |
1284 |
- try: |
1285 |
- elf = ParseELF(realpath, options.root, options.prefix, ldpaths, |
1286 |
- display=p, debug=options.debug) |
1287 |
- except exceptions.ELFError as e: |
1288 |
- if options.skip_non_elfs: |
1289 |
- continue |
1290 |
- # XXX: Ugly. Should unify with _Action* somehow. |
1291 |
- if options.dest is not None and options.copy_non_elfs: |
1292 |
- if os.path.exists(p): |
1293 |
- elf = { |
1294 |
- 'interp': None, |
1295 |
- 'libs': [], |
1296 |
- 'runpath': [], |
1297 |
- 'rpath': [], |
1298 |
- 'path': p, |
1299 |
- 'realpath': realpath, |
1300 |
- } |
1301 |
- _ActionCopy(options, elf) |
1302 |
- continue |
1303 |
- ret = 1 |
1304 |
- warn('%s: %s' % (p, e)) |
1305 |
- continue |
1306 |
- except IOError as e: |
1307 |
- ret = 1 |
1308 |
- warn('%s: %s' % (p, e)) |
1309 |
- continue |
1310 |
- |
1311 |
- if options.dest is None: |
1312 |
- _ActionShow(options, elf) |
1313 |
- else: |
1314 |
- _ActionCopy(options, elf) |
1315 |
- |
1316 |
- if not matched: |
1317 |
- ret = 1 |
1318 |
- warn('%s: did not match any paths' % (path,)) |
1319 |
- |
1320 |
- return ret |
1321 |
+ """The main entry point!""" |
1322 |
+ parser = GetParser() |
1323 |
+ options = parser.parse_args(argv) |
1324 |
+ paths = options.path |
1325 |
+ |
1326 |
+ if options.root != '/': |
1327 |
+ options.root += '/' |
1328 |
+ if options.prefix == '@''GENTOO_PORTAGE_EPREFIX''@': |
1329 |
+ options.prefix = '' |
1330 |
+ |
1331 |
+ if options.bindir and options.bindir[0] != '/': |
1332 |
+ parser.error('--bindir accepts absolute paths only') |
1333 |
+ if options.libdir and options.libdir[0] != '/': |
1334 |
+ parser.error('--libdir accepts absolute paths only') |
1335 |
+ |
1336 |
+ if options.skip_non_elfs and options.copy_non_elfs: |
1337 |
+ parser.error('pick one handler for non-ELFs: skip or copy') |
1338 |
+ |
1339 |
+ dbg(options.debug, 'root =', options.root) |
1340 |
+ if options.dest: |
1341 |
+ dbg(options.debug, 'dest =', options.dest) |
1342 |
+ if not paths: |
1343 |
+ err('missing ELF files to scan') |
1344 |
+ |
1345 |
+ ldpaths = LoadLdpaths(options.root, options.prefix, debug=options.debug) |
1346 |
+ dbg(options.debug, 'ldpaths[conf] =', ldpaths['conf']) |
1347 |
+ dbg(options.debug, 'ldpaths[env] =', ldpaths['env']) |
1348 |
+ |
1349 |
+ # Process all the files specified. |
1350 |
+ ret = 0 |
1351 |
+ for path in paths: |
1352 |
+ dbg(options.debug, 'argv[x] =', path) |
1353 |
+ # Only auto-prefix the path if the ELF is absolute. |
1354 |
+ # If it's a relative path, the user most likely wants |
1355 |
+ # the local path. |
1356 |
+ if options.auto_root and path.startswith('/'): |
1357 |
+ path = options.root + path.lstrip('/') |
1358 |
+ dbg(options.debug, ' +auto-root =', path) |
1359 |
+ |
1360 |
+ matched = False |
1361 |
+ for p in glob.iglob(path): |
1362 |
+ # Once we've processed the globs, resolve the symlink. This way you can |
1363 |
+ # operate on a path that is an absolute symlink itself. e.g.: |
1364 |
+ # $ ln -sf /bin/bash $PWD/root/bin/sh |
1365 |
+ # $ lddtree --root $PWD/root /bin/sh |
1366 |
+ # First we'd turn /bin/sh into $PWD/root/bin/sh, then we want to resolve |
1367 |
+ # the symlink to $PWD/root/bin/bash rather than a plain /bin/bash. |
1368 |
+ dbg(options.debug, ' globbed =', p) |
1369 |
+ if not path.startswith('/'): |
1370 |
+ realpath = os.path.realpath(path) |
1371 |
+ elif options.auto_root: |
1372 |
+ realpath = readlink(p, options.root, prefixed=True) |
1373 |
+ else: |
1374 |
+ realpath = path |
1375 |
+ if path != realpath: |
1376 |
+ dbg(options.debug, ' resolved =', realpath) |
1377 |
+ |
1378 |
+ matched = True |
1379 |
+ try: |
1380 |
+ elf = ParseELF(realpath, options.root, options.prefix, ldpaths, |
1381 |
+ display=p, debug=options.debug) |
1382 |
+ except exceptions.ELFError as e: |
1383 |
+ if options.skip_non_elfs: |
1384 |
+ continue |
1385 |
+ # XXX: Ugly. Should unify with _Action* somehow. |
1386 |
+ if options.dest is not None and options.copy_non_elfs: |
1387 |
+ if os.path.exists(p): |
1388 |
+ elf = { |
1389 |
+ 'interp': None, |
1390 |
+ 'libs': [], |
1391 |
+ 'runpath': [], |
1392 |
+ 'rpath': [], |
1393 |
+ 'path': p, |
1394 |
+ 'realpath': realpath, |
1395 |
+ } |
1396 |
+ _ActionCopy(options, elf) |
1397 |
+ continue |
1398 |
+ ret = 1 |
1399 |
+ warn('%s: %s' % (p, e)) |
1400 |
+ continue |
1401 |
+ except IOError as e: |
1402 |
+ ret = 1 |
1403 |
+ warn('%s: %s' % (p, e)) |
1404 |
+ continue |
1405 |
+ |
1406 |
+ if options.dest is None: |
1407 |
+ _ActionShow(options, elf) |
1408 |
+ else: |
1409 |
+ _ActionCopy(options, elf) |
1410 |
+ |
1411 |
+ if not matched: |
1412 |
+ ret = 1 |
1413 |
+ warn('%s: did not match any paths' % (path,)) |
1414 |
+ |
1415 |
+ return ret |
1416 |
|
1417 |
|
1418 |
if __name__ == '__main__': |
1419 |
- sys.exit(main(sys.argv[1:])) |
1420 |
+ sys.exit(main(sys.argv[1:])) |
1421 |
|
1422 |
diff --git a/pylint b/pylint |
1423 |
index 965537d..38d77a2 100755 |
1424 |
--- a/pylint |
1425 |
+++ b/pylint |
1426 |
@@ -12,38 +12,38 @@ import sys |
1427 |
|
1428 |
|
1429 |
def find_all_modules(source_root): |
1430 |
- """Locate all python modules in the tree for scanning""" |
1431 |
- ret = [] |
1432 |
+ """Locate all python modules in the tree for scanning""" |
1433 |
+ ret = [] |
1434 |
|
1435 |
- for root, _dirs, files in os.walk(source_root, topdown=False): |
1436 |
- # Add all of the .py modules in the tree. |
1437 |
- ret += [os.path.join(root, x) for x in files if x.endswith('.py')] |
1438 |
+ for root, _dirs, files in os.walk(source_root, topdown=False): |
1439 |
+ # Add all of the .py modules in the tree. |
1440 |
+ ret += [os.path.join(root, x) for x in files if x.endswith('.py')] |
1441 |
|
1442 |
- # Add the main scripts that don't end in .py. |
1443 |
- ret += [os.path.join(source_root, x) for x in ('pylint',)] |
1444 |
+ # Add the main scripts that don't end in .py. |
1445 |
+ ret += [os.path.join(source_root, x) for x in ('pylint',)] |
1446 |
|
1447 |
- return ret |
1448 |
+ return ret |
1449 |
|
1450 |
|
1451 |
def main(argv): |
1452 |
- """The main entry point""" |
1453 |
- source_root = os.path.dirname(os.path.realpath(__file__)) |
1454 |
+ """The main entry point""" |
1455 |
+ source_root = os.path.dirname(os.path.realpath(__file__)) |
1456 |
|
1457 |
- if not argv: |
1458 |
- argv = find_all_modules(source_root) |
1459 |
+ if not argv: |
1460 |
+ argv = find_all_modules(source_root) |
1461 |
|
1462 |
- pympath = source_root |
1463 |
- pythonpath = os.environ.get('PYTHONPATH') |
1464 |
- if pythonpath is None: |
1465 |
- pythonpath = pympath |
1466 |
- else: |
1467 |
- pythonpath = pympath + ':' + pythonpath |
1468 |
- os.environ['PYTHONPATH'] = pythonpath |
1469 |
+ pympath = source_root |
1470 |
+ pythonpath = os.environ.get('PYTHONPATH') |
1471 |
+ if pythonpath is None: |
1472 |
+ pythonpath = pympath |
1473 |
+ else: |
1474 |
+ pythonpath = pympath + ':' + pythonpath |
1475 |
+ os.environ['PYTHONPATH'] = pythonpath |
1476 |
|
1477 |
- pylintrc = os.path.join(source_root, '.pylintrc') |
1478 |
- cmd = ['pylint', '--rcfile', pylintrc] |
1479 |
- os.execvp(cmd[0], cmd + argv) |
1480 |
+ pylintrc = os.path.join(source_root, '.pylintrc') |
1481 |
+ cmd = ['pylint', '--rcfile', pylintrc] |
1482 |
+ os.execvp(cmd[0], cmd + argv) |
1483 |
|
1484 |
|
1485 |
if __name__ == '__main__': |
1486 |
- sys.exit(main(sys.argv[1:])) |
1487 |
+ sys.exit(main(sys.argv[1:])) |