1 |
commit: 1ddedd87363c65d6b910fe32da0f1764ba1329a9 |
2 |
Author: Mike Frysinger <vapier <AT> chromium <DOT> org> |
3 |
AuthorDate: Wed Sep 28 07:39:56 2022 +0000 |
4 |
Commit: Mike Frysinger <vapier <AT> gentoo <DOT> org> |
5 |
CommitDate: Wed Sep 28 07:42:17 2022 +0000 |
6 |
URL: https://gitweb.gentoo.org/proj/pax-utils.git/commit/?id=1ddedd87 |
7 |
|
8 |
lddtree: reformat with black |
9 |
|
10 |
Largely this is just single quotes -> double quotes. |
11 |
|
12 |
Signed-off-by: Mike Frysinger <vapier <AT> gentoo.org> |
13 |
|
14 |
lddtree.py | 503 ++++++++++++++++++++++++++++++++++++------------------------- |
15 |
1 file changed, 294 insertions(+), 209 deletions(-) |
16 |
|
17 |
diff --git a/lddtree.py b/lddtree.py |
18 |
index d894505..e851ac1 100755 |
19 |
--- a/lddtree.py |
20 |
+++ b/lddtree.py |
21 |
@@ -50,7 +50,7 @@ import shutil |
22 |
import sys |
23 |
from typing import Any, Iterable, Optional, Union |
24 |
|
25 |
-assert sys.version_info >= (3, 6), f'Python 3.6+ required, but found {sys.version}' |
26 |
+assert sys.version_info >= (3, 6), f"Python 3.6+ required, but found {sys.version}" |
27 |
|
28 |
try: |
29 |
import argcomplete |
30 |
@@ -63,12 +63,12 @@ from elftools.elf.elffile import ELFFile |
31 |
|
32 |
def warn(msg: Any, prefix: Optional[str] = "warning") -> None: |
33 |
"""Write |msg| to stderr with a |prefix| before it""" |
34 |
- print('%s: %s: %s' % (os.path.basename(sys.argv[0]), prefix, msg), file=sys.stderr) |
35 |
+ print("%s: %s: %s" % (os.path.basename(sys.argv[0]), prefix, msg), file=sys.stderr) |
36 |
|
37 |
|
38 |
def err(msg: Any, status: Optional[int] = 1) -> None: |
39 |
"""Write |msg| to stderr and exit with |status|""" |
40 |
- warn(msg, prefix='error') |
41 |
+ warn(msg, prefix="error") |
42 |
sys.exit(status) |
43 |
|
44 |
|
45 |
@@ -82,7 +82,7 @@ def bstr(buf: Union[bytes, str]) -> str: |
46 |
"""Decode the byte string into a string""" |
47 |
if isinstance(buf, str): |
48 |
return buf |
49 |
- return buf.decode('utf-8') |
50 |
+ return buf.decode("utf-8") |
51 |
|
52 |
|
53 |
def normpath(path: str) -> str: |
54 |
@@ -93,7 +93,7 @@ def normpath(path: str) -> str: |
55 |
//..// -> // |
56 |
//..//..// -> /// |
57 |
""" |
58 |
- return os.path.normpath(path).replace('//', '/') |
59 |
+ return os.path.normpath(path).replace("//", "/") |
60 |
|
61 |
|
62 |
@functools.lru_cache(maxsize=None) |
63 |
@@ -115,9 +115,9 @@ def readlink(path: str, root: str, prefixed: Optional[bool] = False) -> str: |
64 |
Returns: |
65 |
A fully resolved symlink path |
66 |
""" |
67 |
- root = root.rstrip('/') |
68 |
+ root = root.rstrip("/") |
69 |
if prefixed: |
70 |
- path = path[len(root):] |
71 |
+ path = path[len(root) :] |
72 |
|
73 |
while os.path.islink(root + path): |
74 |
path = os.path.join(os.path.dirname(path), os.readlink(root + path)) |
75 |
@@ -137,9 +137,9 @@ def interp_supports_argv0(interp: str) -> bool: |
76 |
|
77 |
Starting with glibc-2.33, the ldso supports --argv0 to override argv[0]. |
78 |
""" |
79 |
- with open(interp, 'rb') as fp: |
80 |
+ with open(interp, "rb") as fp: |
81 |
with mmap.mmap(fp.fileno(), 0, prot=mmap.PROT_READ) as mm: |
82 |
- return mm.find(b'--argv0') >= 0 |
83 |
+ return mm.find(b"--argv0") >= 0 |
84 |
|
85 |
|
86 |
def GenerateLdsoWrapper( |
87 |
@@ -165,12 +165,11 @@ def GenerateLdsoWrapper( |
88 |
# Add ldso interpreter dir to end of libpaths as a fallback library path. |
89 |
libpaths = dedupe(list(libpaths) + [interp_dir]) |
90 |
replacements = { |
91 |
- 'interp': os.path.join(os.path.relpath(interp_dir, basedir), |
92 |
- interp_name), |
93 |
+ "interp": os.path.join(os.path.relpath(interp_dir, basedir), interp_name), |
94 |
"libpaths": ":".join( |
95 |
"${basedir}/" + os.path.relpath(p, basedir) for p in libpaths |
96 |
), |
97 |
- 'argv0_arg': '--argv0 "$0"' if interp_supports_argv0(root + interp) else '', |
98 |
+ "argv0_arg": '--argv0 "$0"' if interp_supports_argv0(root + interp) else "", |
99 |
} |
100 |
wrapper = """#!/bin/sh |
101 |
if ! base=$(realpath "$0" 2>/dev/null); then |
102 |
@@ -190,8 +189,8 @@ exec \\ |
103 |
"$@" |
104 |
""" |
105 |
wrappath = root + path |
106 |
- os.rename(wrappath, wrappath + '.elf') |
107 |
- with open(wrappath, 'w', encoding='utf-8') as f: |
108 |
+ os.rename(wrappath, wrappath + ".elf") |
109 |
+ with open(wrappath, "w", encoding="utf-8") as f: |
110 |
f.write(wrapper % replacements) |
111 |
os.chmod(wrappath, 0o0755) |
112 |
|
113 |
@@ -223,17 +222,17 @@ def ParseLdPaths( |
114 |
cwd = os.getcwd() |
115 |
|
116 |
ldpaths = [] |
117 |
- for ldpath in str_ldpaths.split(':'): |
118 |
+ for ldpath in str_ldpaths.split(":"): |
119 |
# Expand placeholders first. |
120 |
- if '$ORIGIN' in ldpath: |
121 |
- ldpath = ldpath.replace('$ORIGIN', os.path.dirname(path)) |
122 |
- elif '${ORIGIN}' in ldpath: |
123 |
- ldpath = ldpath.replace('${ORIGIN}', os.path.dirname(path)) |
124 |
+ if "$ORIGIN" in ldpath: |
125 |
+ ldpath = ldpath.replace("$ORIGIN", os.path.dirname(path)) |
126 |
+ elif "${ORIGIN}" in ldpath: |
127 |
+ ldpath = ldpath.replace("${ORIGIN}", os.path.dirname(path)) |
128 |
|
129 |
# Expand relative paths if needed. These don't make sense in general, |
130 |
# but that doesn't stop people from using them. As such, root prefix |
131 |
# doesn't make sense with it either. |
132 |
- if not ldpath.startswith('/'): |
133 |
+ if not ldpath.startswith("/"): |
134 |
# NB: The ldso treats "" paths as cwd too. |
135 |
ldpath = os.path.join(cwd, ldpath) |
136 |
else: |
137 |
@@ -265,27 +264,29 @@ def ParseLdSoConf( |
138 |
""" |
139 |
paths = [] |
140 |
|
141 |
- dbg_pfx = '' if _first else ' ' |
142 |
+ dbg_pfx = "" if _first else " " |
143 |
try: |
144 |
dbg(debug, f"{dbg_pfx}ParseLdSoConf({ldso_conf})") |
145 |
- with open(ldso_conf, encoding='utf-8') as f: |
146 |
+ with open(ldso_conf, encoding="utf-8") as f: |
147 |
for line in f.readlines(): |
148 |
- line = line.split('#', 1)[0].strip() |
149 |
+ line = line.split("#", 1)[0].strip() |
150 |
if not line: |
151 |
continue |
152 |
- if line.startswith('include '): |
153 |
+ if line.startswith("include "): |
154 |
line = line[8:] |
155 |
- if line[0] == '/': |
156 |
- line = root + line.lstrip('/') |
157 |
+ if line[0] == "/": |
158 |
+ line = root + line.lstrip("/") |
159 |
else: |
160 |
- line = os.path.dirname(ldso_conf) + '/' + line |
161 |
+ line = os.path.dirname(ldso_conf) + "/" + line |
162 |
dbg(debug, dbg_pfx, "glob:", line) |
163 |
# ldconfig in glibc uses glob() which returns entries sorted according |
164 |
# to LC_COLLATE. Further, ldconfig does not reset that but respects |
165 |
# the active env settings (which might be a mistake). Python does not |
166 |
# sort its results by default though, so do it ourselves. |
167 |
for path in sorted(glob.glob(line)): |
168 |
- paths += ParseLdSoConf(path, root=root, debug=debug, _first=False) |
169 |
+ paths += ParseLdSoConf( |
170 |
+ path, root=root, debug=debug, _first=False |
171 |
+ ) |
172 |
else: |
173 |
paths += [normpath(root + line)] |
174 |
except IOError as e: |
175 |
@@ -320,25 +321,26 @@ def LoadLdpaths( |
176 |
dict containing library paths to search |
177 |
""" |
178 |
ldpaths: dict[str, list[str]] = { |
179 |
- 'conf': [], |
180 |
- 'env': [], |
181 |
- 'interp': [], |
182 |
+ "conf": [], |
183 |
+ "env": [], |
184 |
+ "interp": [], |
185 |
} |
186 |
|
187 |
# Load up $LD_LIBRARY_PATH. |
188 |
- ldpaths['env'] = [] |
189 |
- env_ldpath = os.environ.get('LD_LIBRARY_PATH') |
190 |
+ ldpaths["env"] = [] |
191 |
+ env_ldpath = os.environ.get("LD_LIBRARY_PATH") |
192 |
if not env_ldpath is None: |
193 |
- if root != '/': |
194 |
- warn('ignoring LD_LIBRARY_PATH due to ROOT usage') |
195 |
+ if root != "/": |
196 |
+ warn("ignoring LD_LIBRARY_PATH due to ROOT usage") |
197 |
else: |
198 |
# XXX: If this contains $ORIGIN, we probably have to parse this |
199 |
# on a per-ELF basis so it can get turned into the right thing. |
200 |
- ldpaths['env'] = ParseLdPaths(env_ldpath, cwd=cwd, path='') |
201 |
+ ldpaths["env"] = ParseLdPaths(env_ldpath, cwd=cwd, path="") |
202 |
|
203 |
# Load up /etc/ld.so.conf. |
204 |
- ldpaths['conf'] = ParseLdSoConf(root + prefix + '/etc/ld.so.conf', root=root, |
205 |
- debug=debug) |
206 |
+ ldpaths["conf"] = ParseLdSoConf( |
207 |
+ root + prefix + "/etc/ld.so.conf", root=root, debug=debug |
208 |
+ ) |
209 |
|
210 |
return ldpaths |
211 |
|
212 |
@@ -356,14 +358,16 @@ def CompatibleELFs(elf1: ELFFile, elf2: ELFFile) -> bool: |
213 |
Returns: |
214 |
True if compatible, False otherwise |
215 |
""" |
216 |
- osabis = frozenset([e.header['e_ident']['EI_OSABI'] for e in (elf1, elf2)]) |
217 |
+ osabis = frozenset([e.header["e_ident"]["EI_OSABI"] for e in (elf1, elf2)]) |
218 |
compat_sets = ( |
219 |
frozenset(f"ELFOSABI_{x}" for x in ("NONE", "SYSV", "GNU", "LINUX")), |
220 |
) |
221 |
- return ((len(osabis) == 1 or any(osabis.issubset(x) for x in compat_sets)) and |
222 |
- elf1.elfclass == elf2.elfclass and |
223 |
- elf1.little_endian == elf2.little_endian and |
224 |
- elf1.header['e_machine'] == elf2.header['e_machine']) |
225 |
+ return ( |
226 |
+ (len(osabis) == 1 or any(osabis.issubset(x) for x in compat_sets)) |
227 |
+ and elf1.elfclass == elf2.elfclass |
228 |
+ and elf1.little_endian == elf2.little_endian |
229 |
+ and elf1.header["e_machine"] == elf2.header["e_machine"] |
230 |
+ ) |
231 |
|
232 |
|
233 |
def FindLib( |
234 |
@@ -393,10 +397,10 @@ def FindLib( |
235 |
if path != target: |
236 |
dbg(debug, " checking:", path, "->", target) |
237 |
else: |
238 |
- dbg(debug, ' checking:', path) |
239 |
+ dbg(debug, " checking:", path) |
240 |
|
241 |
if os.path.exists(target): |
242 |
- with open(target, 'rb') as f: |
243 |
+ with open(target, "rb") as f: |
244 |
try: |
245 |
libelf = ELFFile(f) |
246 |
if CompatibleELFs(elf, libelf): |
247 |
@@ -456,18 +460,18 @@ def ParseELF( |
248 |
_all_libs = {} |
249 |
ldpaths = ldpaths.copy() |
250 |
ret = { |
251 |
- 'interp': None, |
252 |
- 'path': path if display is None else display, |
253 |
- 'realpath': path, |
254 |
- 'needed': [], |
255 |
- 'rpath': [], |
256 |
- 'runpath': [], |
257 |
- 'libs': _all_libs, |
258 |
+ "interp": None, |
259 |
+ "path": path if display is None else display, |
260 |
+ "realpath": path, |
261 |
+ "needed": [], |
262 |
+ "rpath": [], |
263 |
+ "runpath": [], |
264 |
+ "libs": _all_libs, |
265 |
} |
266 |
|
267 |
dbg(debug, f"ParseELF({path})") |
268 |
|
269 |
- with open(path, 'rb') as f: |
270 |
+ with open(path, "rb") as f: |
271 |
try: |
272 |
elf = ELFFile(f) |
273 |
except exceptions.ELFParseError: |
274 |
@@ -477,17 +481,17 @@ def ParseELF( |
275 |
# If this is the first ELF, extract the interpreter. |
276 |
if _first: |
277 |
for segment in elf.iter_segments(): |
278 |
- if segment.header.p_type != 'PT_INTERP': |
279 |
+ if segment.header.p_type != "PT_INTERP": |
280 |
continue |
281 |
|
282 |
interp = bstr(segment.get_interp_name()) |
283 |
- dbg(debug, ' interp =', interp) |
284 |
- ret['interp'] = normpath(root + interp) |
285 |
- real_interp = readlink(ret['interp'], root, prefixed=True) |
286 |
- ret['libs'][os.path.basename(interp)] = { |
287 |
- 'path': ret['interp'], |
288 |
- 'realpath': real_interp, |
289 |
- 'needed': [], |
290 |
+ dbg(debug, " interp =", interp) |
291 |
+ ret["interp"] = normpath(root + interp) |
292 |
+ real_interp = readlink(ret["interp"], root, prefixed=True) |
293 |
+ ret["libs"][os.path.basename(interp)] = { |
294 |
+ "path": ret["interp"], |
295 |
+ "realpath": real_interp, |
296 |
+ "needed": [], |
297 |
} |
298 |
# XXX: Could read it and scan for /lib paths. |
299 |
# If the interp is a symlink, lets follow it on the assumption that it |
300 |
@@ -498,12 +502,16 @@ def ParseELF( |
301 |
# ld64.so.1 is really a symlink to ../lib64/ld64.so.1. In the multiarch |
302 |
# setup, it'll be /lib/ld64.so.1 -> /lib/s390x-linux-gnu/ld64.so.1. |
303 |
# That is why we use |real_interp| here instead of |interp|. |
304 |
- ldpaths['interp'] = [ |
305 |
+ ldpaths["interp"] = [ |
306 |
os.path.dirname(real_interp), |
307 |
- normpath(root + prefix + '/usr/' + os.path.dirname( |
308 |
- real_interp)[len(root) + len(prefix):]), |
309 |
+ normpath( |
310 |
+ root |
311 |
+ + prefix |
312 |
+ + "/usr/" |
313 |
+ + os.path.dirname(real_interp)[len(root) + len(prefix) :] |
314 |
+ ), |
315 |
] |
316 |
- dbg(debug, ' ldpaths[interp] =', ldpaths['interp']) |
317 |
+ dbg(debug, " ldpaths[interp] =", ldpaths["interp"]) |
318 |
break |
319 |
|
320 |
# Parse the ELF's dynamic tags. |
321 |
@@ -511,15 +519,17 @@ def ParseELF( |
322 |
rpaths = [] |
323 |
runpaths = [] |
324 |
for segment in elf.iter_segments(): |
325 |
- if segment.header.p_type != 'PT_DYNAMIC': |
326 |
+ if segment.header.p_type != "PT_DYNAMIC": |
327 |
continue |
328 |
|
329 |
for t in segment.iter_tags(): |
330 |
- if t.entry.d_tag == 'DT_RPATH': |
331 |
+ if t.entry.d_tag == "DT_RPATH": |
332 |
rpaths = ParseLdPaths(bstr(t.rpath), root=root, cwd=cwd, path=path) |
333 |
- elif t.entry.d_tag == 'DT_RUNPATH': |
334 |
- runpaths = ParseLdPaths(bstr(t.runpath), root=root, cwd=cwd, path=path) |
335 |
- elif t.entry.d_tag == 'DT_NEEDED': |
336 |
+ elif t.entry.d_tag == "DT_RUNPATH": |
337 |
+ runpaths = ParseLdPaths( |
338 |
+ bstr(t.runpath), root=root, cwd=cwd, path=path |
339 |
+ ) |
340 |
+ elif t.entry.d_tag == "DT_NEEDED": |
341 |
libs.append(bstr(t.needed)) |
342 |
if runpaths: |
343 |
# If both RPATH and RUNPATH are set, only the latter is used. |
344 |
@@ -531,13 +541,13 @@ def ParseELF( |
345 |
if _first: |
346 |
# Propagate the rpaths used by the main ELF since those will be |
347 |
# used at runtime to locate things. |
348 |
- ldpaths['rpath'] = rpaths |
349 |
- ldpaths['runpath'] = runpaths |
350 |
- dbg(debug, ' ldpaths[rpath] =', rpaths) |
351 |
- dbg(debug, ' ldpaths[runpath] =', runpaths) |
352 |
- ret['rpath'] = rpaths |
353 |
- ret['runpath'] = runpaths |
354 |
- ret['needed'] = libs |
355 |
+ ldpaths["rpath"] = rpaths |
356 |
+ ldpaths["runpath"] = runpaths |
357 |
+ dbg(debug, " ldpaths[rpath] =", rpaths) |
358 |
+ dbg(debug, " ldpaths[runpath] =", runpaths) |
359 |
+ ret["rpath"] = rpaths |
360 |
+ ret["runpath"] = runpaths |
361 |
+ ret["needed"] = libs |
362 |
|
363 |
# Search for the libs this ELF uses. |
364 |
all_ldpaths = None |
365 |
@@ -546,29 +556,42 @@ def ParseELF( |
366 |
continue |
367 |
if all_ldpaths is None: |
368 |
all_ldpaths = ( |
369 |
- rpaths + ldpaths['rpath'] + |
370 |
- ldpaths['env'] + |
371 |
- runpaths + ldpaths['runpath'] + |
372 |
- ldpaths['conf'] + |
373 |
- ldpaths['interp'] |
374 |
+ rpaths |
375 |
+ + ldpaths["rpath"] |
376 |
+ + ldpaths["env"] |
377 |
+ + runpaths |
378 |
+ + ldpaths["runpath"] |
379 |
+ + ldpaths["conf"] |
380 |
+ + ldpaths["interp"] |
381 |
) |
382 |
realpath, fullpath = FindLib(elf, lib, all_ldpaths, root, debug=debug) |
383 |
_all_libs[lib] = { |
384 |
- 'realpath': realpath, |
385 |
- 'path': fullpath, |
386 |
- 'needed': [], |
387 |
+ "realpath": realpath, |
388 |
+ "path": fullpath, |
389 |
+ "needed": [], |
390 |
} |
391 |
if realpath is not None: |
392 |
try: |
393 |
- lret = ParseELF(realpath, root, cwd, prefix, ldpaths, display=fullpath, |
394 |
- debug=debug, _first=False, _all_libs=_all_libs) |
395 |
+ lret = ParseELF( |
396 |
+ realpath, |
397 |
+ root, |
398 |
+ cwd, |
399 |
+ prefix, |
400 |
+ ldpaths, |
401 |
+ display=fullpath, |
402 |
+ debug=debug, |
403 |
+ _first=False, |
404 |
+ _all_libs=_all_libs, |
405 |
+ ) |
406 |
except exceptions.ELFError as e: |
407 |
warn(f"{realpath}: {e}") |
408 |
- _all_libs[lib]['needed'] = lret['needed'] |
409 |
+ _all_libs[lib]["needed"] = lret["needed"] |
410 |
|
411 |
del elf |
412 |
|
413 |
return ret |
414 |
+ |
415 |
+ |
416 |
# pylint: enable=dangerous-default-value |
417 |
|
418 |
|
419 |
@@ -579,9 +602,10 @@ class _NormalizePathAction(argparse.Action): |
420 |
|
421 |
def _ActionShow(options: argparse.Namespace, elf: dict): |
422 |
"""Show the dependency tree for this ELF""" |
423 |
+ |
424 |
def _show(lib, depth): |
425 |
chain_libs.append(lib) |
426 |
- fullpath = elf['libs'][lib]['path'] |
427 |
+ fullpath = elf["libs"][lib]["path"] |
428 |
if options.list: |
429 |
print(fullpath or lib) |
430 |
else: |
431 |
@@ -602,10 +626,10 @@ def _ActionShow(options: argparse.Namespace, elf: dict): |
432 |
_show(nlib, depth + 1) |
433 |
chain_libs.pop() |
434 |
|
435 |
- shown_libs = set(elf['needed']) |
436 |
- new_libs = elf['needed'][:] |
437 |
+ shown_libs = set(elf["needed"]) |
438 |
+ new_libs = elf["needed"][:] |
439 |
chain_libs: list[str] = [] |
440 |
- interp = elf['interp'] |
441 |
+ interp = elf["interp"] |
442 |
if interp: |
443 |
lib = os.path.basename(interp) |
444 |
shown_libs.add(lib) |
445 |
@@ -617,7 +641,7 @@ def _ActionShow(options: argparse.Namespace, elf: dict): |
446 |
if not options.all and options.list and lib in new_libs: |
447 |
new_libs.remove(lib) |
448 |
if options.list: |
449 |
- print(elf['path']) |
450 |
+ print(elf["path"]) |
451 |
if not interp is None: |
452 |
print(interp) |
453 |
else: |
454 |
@@ -628,17 +652,17 @@ def _ActionShow(options: argparse.Namespace, elf: dict): |
455 |
|
456 |
def _ActionCopy(options: argparse.Namespace, elf: dict): |
457 |
"""Copy the ELF and its dependencies to a destination tree""" |
458 |
+ |
459 |
def _StripRoot(path: str) -> str: |
460 |
- return path[len(options.root) - 1:] |
461 |
+ return path[len(options.root) - 1 :] |
462 |
|
463 |
- def _copy(realsrc, src, striproot=True, wrapit=False, libpaths=(), |
464 |
- outdir=None): |
465 |
+ def _copy(realsrc, src, striproot=True, wrapit=False, libpaths=(), outdir=None): |
466 |
if realsrc is None: |
467 |
return |
468 |
|
469 |
if wrapit: |
470 |
# Static ELFs don't need to be wrapped. |
471 |
- if not elf['interp']: |
472 |
+ if not elf["interp"]: |
473 |
wrapit = False |
474 |
|
475 |
striproot = _StripRoot if striproot else lambda x: x |
476 |
@@ -651,11 +675,10 @@ def _ActionCopy(options: argparse.Namespace, elf: dict): |
477 |
|
478 |
try: |
479 |
# See if they're the same file. |
480 |
- nstat = os.stat(dst + ('.elf' if wrapit else '')) |
481 |
+ nstat = os.stat(dst + (".elf" if wrapit else "")) |
482 |
ostat = os.stat(realsrc) |
483 |
- for field in ('mode', 'mtime', 'size'): |
484 |
- if getattr(ostat, 'st_' + field) != \ |
485 |
- getattr(nstat, 'st_' + field): |
486 |
+ for field in ("mode", "mtime", "size"): |
487 |
+ if getattr(ostat, "st_" + field) != getattr(nstat, "st_" + field): |
488 |
break |
489 |
else: |
490 |
return |
491 |
@@ -684,9 +707,9 @@ def _ActionCopy(options: argparse.Namespace, elf: dict): |
492 |
print("generate wrapper", dst) |
493 |
|
494 |
if options.libdir: |
495 |
- interp = os.path.join(options.libdir, os.path.basename(elf['interp'])) |
496 |
+ interp = os.path.join(options.libdir, os.path.basename(elf["interp"])) |
497 |
else: |
498 |
- interp = _StripRoot(elf['interp']) |
499 |
+ interp = _StripRoot(elf["interp"]) |
500 |
GenerateLdsoWrapper(options.dest, subdst, interp, libpaths) |
501 |
|
502 |
# XXX: We should automatically import libgcc_s.so whenever libpthread.so |
503 |
@@ -695,99 +718,153 @@ def _ActionCopy(options: argparse.Namespace, elf: dict): |
504 |
# the libnsl.so and libnss_*.so libraries, as well as an open ended list |
505 |
# for known libs that get loaded (e.g. curl will dlopen(libresolv)). |
506 |
uniq_libpaths = set() |
507 |
- for lib in elf['libs']: |
508 |
- libdata = elf['libs'][lib] |
509 |
- path = libdata['realpath'] |
510 |
+ for lib in elf["libs"]: |
511 |
+ libdata = elf["libs"][lib] |
512 |
+ path = libdata["realpath"] |
513 |
if path is None: |
514 |
warn("could not locate library:", lib) |
515 |
continue |
516 |
if not options.libdir: |
517 |
uniq_libpaths.add(_StripRoot(os.path.dirname(path))) |
518 |
- _copy(path, libdata['path'], outdir=options.libdir) |
519 |
+ _copy(path, libdata["path"], outdir=options.libdir) |
520 |
|
521 |
if not options.libdir: |
522 |
libpaths = list(uniq_libpaths) |
523 |
- if elf['runpath']: |
524 |
- libpaths = elf['runpath'] + libpaths |
525 |
+ if elf["runpath"]: |
526 |
+ libpaths = elf["runpath"] + libpaths |
527 |
else: |
528 |
- libpaths = elf['rpath'] + libpaths |
529 |
+ libpaths = elf["rpath"] + libpaths |
530 |
else: |
531 |
uniq_libpaths.add(options.libdir) |
532 |
libpaths = list(uniq_libpaths) |
533 |
|
534 |
# We don't bother to copy this as ParseElf adds the interp to the 'libs', |
535 |
# so it was already copied in the libs loop above. |
536 |
- #_copy(elf['interp'], outdir=options.libdir) |
537 |
- _copy(elf['realpath'], elf['path'], striproot=options.auto_root, |
538 |
- wrapit=options.generate_wrappers, libpaths=libpaths, |
539 |
- outdir=options.bindir) |
540 |
+ # _copy(elf['interp'], outdir=options.libdir) |
541 |
+ _copy( |
542 |
+ elf["realpath"], |
543 |
+ elf["path"], |
544 |
+ striproot=options.auto_root, |
545 |
+ wrapit=options.generate_wrappers, |
546 |
+ libpaths=libpaths, |
547 |
+ outdir=options.bindir, |
548 |
+ ) |
549 |
|
550 |
|
551 |
def GetParser() -> argparse.ArgumentParser: |
552 |
"""Get a CLI parser.""" |
553 |
parser = argparse.ArgumentParser( |
554 |
- description=__doc__, |
555 |
- formatter_class=argparse.RawDescriptionHelpFormatter) |
556 |
- parser.add_argument('-a', '--all', |
557 |
- action='store_true', default=False, |
558 |
- help='Show all duplicated dependencies') |
559 |
- parser.add_argument('-l', '--list', |
560 |
- action='store_true', default=False, |
561 |
- help='Display output in a simple list (easy for copying)') |
562 |
- parser.add_argument('-x', '--debug', |
563 |
- action='store_true', default=False, |
564 |
- help='Run with debugging') |
565 |
- parser.add_argument('-v', '--verbose', |
566 |
- action='store_true', default=False, |
567 |
- help='Be verbose') |
568 |
- parser.add_argument('--skip-non-elfs', |
569 |
- action='store_true', default=False, |
570 |
- help='Skip plain (non-ELF) files instead of warning') |
571 |
- parser.add_argument('--skip-missing', |
572 |
- action='store_true', default=False, |
573 |
- help='Skip missing files instead of failing') |
574 |
- parser.add_argument('-V', '--version', |
575 |
- action='version', |
576 |
- version='lddtree by Mike Frysinger <vapier@g.o>', |
577 |
- help='Show version information') |
578 |
- parser.add_argument('path', nargs='+') |
579 |
- |
580 |
- group = parser.add_argument_group('Path options') |
581 |
- group.add_argument('-R', '--root', |
582 |
- default=os.environ.get('ROOT', ''), type=str, |
583 |
- action=_NormalizePathAction, |
584 |
- help='Search for all files/dependencies in ROOT') |
585 |
- group.add_argument('--no-auto-root', |
586 |
- dest='auto_root', action='store_false', default=True, |
587 |
- help='Do not automatically prefix input ELFs with ROOT') |
588 |
- group.add_argument('-C', '--cwd', |
589 |
- default=os.getcwd(), type=str, action=_NormalizePathAction, |
590 |
- help='Path to resolve relative paths against') |
591 |
- group.add_argument('-P', '--prefix', |
592 |
- default=os.environ.get( |
593 |
- 'EPREFIX', '@GENTOO_PORTAGE_EPREFIX@'), type=str, |
594 |
- action=_NormalizePathAction, |
595 |
- help='Specify EPREFIX for binaries (for Gentoo Prefix)') |
596 |
- |
597 |
- group = parser.add_argument_group('Copying options') |
598 |
- group.add_argument('--copy-to-tree', |
599 |
- dest='dest', default=None, type=str, |
600 |
- action=_NormalizePathAction, |
601 |
- help='Copy all files to the specified tree') |
602 |
- group.add_argument('--bindir', |
603 |
- default=None, type=str, |
604 |
- action=_NormalizePathAction, |
605 |
- help='Dir to store all ELFs specified on the command line') |
606 |
- group.add_argument('--libdir', |
607 |
- default=None, type=str, |
608 |
- action=_NormalizePathAction, |
609 |
- help='Dir to store all ELF libs') |
610 |
- group.add_argument('--generate-wrappers', |
611 |
- action='store_true', default=False, |
612 |
- help='Wrap executable ELFs with scripts for local ldso') |
613 |
- group.add_argument('--copy-non-elfs', |
614 |
- action='store_true', default=False, |
615 |
- help='Copy over plain (non-ELF) files instead of warn+ignore') |
616 |
+ description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter |
617 |
+ ) |
618 |
+ parser.add_argument( |
619 |
+ "-a", |
620 |
+ "--all", |
621 |
+ action="store_true", |
622 |
+ default=False, |
623 |
+ help="Show all duplicated dependencies", |
624 |
+ ) |
625 |
+ parser.add_argument( |
626 |
+ "-l", |
627 |
+ "--list", |
628 |
+ action="store_true", |
629 |
+ default=False, |
630 |
+ help="Display output in a simple list (easy for copying)", |
631 |
+ ) |
632 |
+ parser.add_argument( |
633 |
+ "-x", "--debug", action="store_true", default=False, help="Run with debugging" |
634 |
+ ) |
635 |
+ parser.add_argument( |
636 |
+ "-v", "--verbose", action="store_true", default=False, help="Be verbose" |
637 |
+ ) |
638 |
+ parser.add_argument( |
639 |
+ "--skip-non-elfs", |
640 |
+ action="store_true", |
641 |
+ default=False, |
642 |
+ help="Skip plain (non-ELF) files instead of warning", |
643 |
+ ) |
644 |
+ parser.add_argument( |
645 |
+ "--skip-missing", |
646 |
+ action="store_true", |
647 |
+ default=False, |
648 |
+ help="Skip missing files instead of failing", |
649 |
+ ) |
650 |
+ parser.add_argument( |
651 |
+ "-V", |
652 |
+ "--version", |
653 |
+ action="version", |
654 |
+ version="lddtree by Mike Frysinger <vapier@g.o>", |
655 |
+ help="Show version information", |
656 |
+ ) |
657 |
+ parser.add_argument("path", nargs="+") |
658 |
+ |
659 |
+ group = parser.add_argument_group("Path options") |
660 |
+ group.add_argument( |
661 |
+ "-R", |
662 |
+ "--root", |
663 |
+ default=os.environ.get("ROOT", ""), |
664 |
+ type=str, |
665 |
+ action=_NormalizePathAction, |
666 |
+ help="Search for all files/dependencies in ROOT", |
667 |
+ ) |
668 |
+ group.add_argument( |
669 |
+ "--no-auto-root", |
670 |
+ dest="auto_root", |
671 |
+ action="store_false", |
672 |
+ default=True, |
673 |
+ help="Do not automatically prefix input ELFs with ROOT", |
674 |
+ ) |
675 |
+ group.add_argument( |
676 |
+ "-C", |
677 |
+ "--cwd", |
678 |
+ default=os.getcwd(), |
679 |
+ type=str, |
680 |
+ action=_NormalizePathAction, |
681 |
+ help="Path to resolve relative paths against", |
682 |
+ ) |
683 |
+ group.add_argument( |
684 |
+ "-P", |
685 |
+ "--prefix", |
686 |
+ default=os.environ.get("EPREFIX", "@GENTOO_PORTAGE_EPREFIX@"), |
687 |
+ type=str, |
688 |
+ action=_NormalizePathAction, |
689 |
+ help="Specify EPREFIX for binaries (for Gentoo Prefix)", |
690 |
+ ) |
691 |
+ |
692 |
+ group = parser.add_argument_group("Copying options") |
693 |
+ group.add_argument( |
694 |
+ "--copy-to-tree", |
695 |
+ dest="dest", |
696 |
+ default=None, |
697 |
+ type=str, |
698 |
+ action=_NormalizePathAction, |
699 |
+ help="Copy all files to the specified tree", |
700 |
+ ) |
701 |
+ group.add_argument( |
702 |
+ "--bindir", |
703 |
+ default=None, |
704 |
+ type=str, |
705 |
+ action=_NormalizePathAction, |
706 |
+ help="Dir to store all ELFs specified on the command line", |
707 |
+ ) |
708 |
+ group.add_argument( |
709 |
+ "--libdir", |
710 |
+ default=None, |
711 |
+ type=str, |
712 |
+ action=_NormalizePathAction, |
713 |
+ help="Dir to store all ELF libs", |
714 |
+ ) |
715 |
+ group.add_argument( |
716 |
+ "--generate-wrappers", |
717 |
+ action="store_true", |
718 |
+ default=False, |
719 |
+ help="Wrap executable ELFs with scripts for local ldso", |
720 |
+ ) |
721 |
+ group.add_argument( |
722 |
+ "--copy-non-elfs", |
723 |
+ action="store_true", |
724 |
+ default=False, |
725 |
+ help="Copy over plain (non-ELF) files instead of warn+ignore", |
726 |
+ ) |
727 |
|
728 |
if argcomplete is not None: |
729 |
argcomplete.autocomplete(parser) |
730 |
@@ -800,41 +877,42 @@ def main(argv: list[str]) -> Optional[int]: |
731 |
options = parser.parse_args(argv) |
732 |
paths = options.path |
733 |
|
734 |
- if options.root != '/': |
735 |
- options.root += '/' |
736 |
- if options.prefix == '@''GENTOO_PORTAGE_EPREFIX''@': |
737 |
- options.prefix = '' |
738 |
+ if options.root != "/": |
739 |
+ options.root += "/" |
740 |
+ if options.prefix == "@" "GENTOO_PORTAGE_EPREFIX" "@": |
741 |
+ options.prefix = "" |
742 |
|
743 |
- if options.bindir and options.bindir[0] != '/': |
744 |
- parser.error('--bindir accepts absolute paths only') |
745 |
- if options.libdir and options.libdir[0] != '/': |
746 |
- parser.error('--libdir accepts absolute paths only') |
747 |
+ if options.bindir and options.bindir[0] != "/": |
748 |
+ parser.error("--bindir accepts absolute paths only") |
749 |
+ if options.libdir and options.libdir[0] != "/": |
750 |
+ parser.error("--libdir accepts absolute paths only") |
751 |
|
752 |
if options.skip_non_elfs and options.copy_non_elfs: |
753 |
- parser.error('pick one handler for non-ELFs: skip or copy') |
754 |
+ parser.error("pick one handler for non-ELFs: skip or copy") |
755 |
|
756 |
- dbg(options.debug, 'root =', options.root) |
757 |
- dbg(options.debug, 'cwd =', options.cwd) |
758 |
+ dbg(options.debug, "root =", options.root) |
759 |
+ dbg(options.debug, "cwd =", options.cwd) |
760 |
if options.dest: |
761 |
- dbg(options.debug, 'dest =', options.dest) |
762 |
+ dbg(options.debug, "dest =", options.dest) |
763 |
if not paths: |
764 |
- err('missing ELF files to scan') |
765 |
+ err("missing ELF files to scan") |
766 |
|
767 |
- ldpaths = LoadLdpaths(options.root, cwd=options.cwd, prefix=options.prefix, |
768 |
- debug=options.debug) |
769 |
- dbg(options.debug, 'ldpaths[conf] =', ldpaths['conf']) |
770 |
- dbg(options.debug, 'ldpaths[env] =', ldpaths['env']) |
771 |
+ ldpaths = LoadLdpaths( |
772 |
+ options.root, cwd=options.cwd, prefix=options.prefix, debug=options.debug |
773 |
+ ) |
774 |
+ dbg(options.debug, "ldpaths[conf] =", ldpaths["conf"]) |
775 |
+ dbg(options.debug, "ldpaths[env] =", ldpaths["env"]) |
776 |
|
777 |
# Process all the files specified. |
778 |
ret = 0 |
779 |
for path in paths: |
780 |
- dbg(options.debug, 'argv[x] =', path) |
781 |
+ dbg(options.debug, "argv[x] =", path) |
782 |
# Only auto-prefix the path if the ELF is absolute. |
783 |
# If it's a relative path, the user most likely wants |
784 |
# the local path. |
785 |
- if options.auto_root and path.startswith('/'): |
786 |
- path = options.root + path.lstrip('/') |
787 |
- dbg(options.debug, ' +auto-root =', path) |
788 |
+ if options.auto_root and path.startswith("/"): |
789 |
+ path = options.root + path.lstrip("/") |
790 |
+ dbg(options.debug, " +auto-root =", path) |
791 |
|
792 |
matched = False |
793 |
for p in glob.iglob(path): |
794 |
@@ -844,20 +922,27 @@ def main(argv: list[str]) -> Optional[int]: |
795 |
# $ lddtree --root $PWD/root /bin/sh |
796 |
# First we'd turn /bin/sh into $PWD/root/bin/sh, then we want to resolve |
797 |
# the symlink to $PWD/root/bin/bash rather than a plain /bin/bash. |
798 |
- dbg(options.debug, ' globbed =', p) |
799 |
- if not path.startswith('/'): |
800 |
+ dbg(options.debug, " globbed =", p) |
801 |
+ if not path.startswith("/"): |
802 |
realpath = os.path.realpath(path) |
803 |
elif options.auto_root: |
804 |
realpath = readlink(p, options.root, prefixed=True) |
805 |
else: |
806 |
realpath = path |
807 |
if path != realpath: |
808 |
- dbg(options.debug, ' resolved =', realpath) |
809 |
+ dbg(options.debug, " resolved =", realpath) |
810 |
|
811 |
matched = True |
812 |
try: |
813 |
- elf = ParseELF(realpath, options.root, options.cwd, options.prefix, ldpaths, |
814 |
- display=p, debug=options.debug) |
815 |
+ elf = ParseELF( |
816 |
+ realpath, |
817 |
+ options.root, |
818 |
+ options.cwd, |
819 |
+ options.prefix, |
820 |
+ ldpaths, |
821 |
+ display=p, |
822 |
+ debug=options.debug, |
823 |
+ ) |
824 |
except exceptions.ELFError as e: |
825 |
if options.skip_non_elfs: |
826 |
continue |
827 |
@@ -865,12 +950,12 @@ def main(argv: list[str]) -> Optional[int]: |
828 |
if options.dest is not None and options.copy_non_elfs: |
829 |
if os.path.exists(p): |
830 |
elf = { |
831 |
- 'interp': None, |
832 |
- 'libs': [], |
833 |
- 'runpath': [], |
834 |
- 'rpath': [], |
835 |
- 'path': p, |
836 |
- 'realpath': realpath, |
837 |
+ "interp": None, |
838 |
+ "libs": [], |
839 |
+ "runpath": [], |
840 |
+ "rpath": [], |
841 |
+ "path": p, |
842 |
+ "realpath": realpath, |
843 |
} |
844 |
_ActionCopy(options, elf) |
845 |
continue |
846 |
@@ -895,5 +980,5 @@ def main(argv: list[str]) -> Optional[int]: |
847 |
return ret |
848 |
|
849 |
|
850 |
-if __name__ == '__main__': |
851 |
+if __name__ == "__main__": |
852 |
sys.exit(main(sys.argv[1:])) |