summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Frysinger <vapier@chromium.org>2022-09-28 13:24:56 +0545
committerMike Frysinger <vapier@gentoo.org>2022-09-28 13:27:17 +0545
commit1ddedd87363c65d6b910fe32da0f1764ba1329a9 (patch)
tree90467dac0e0c2ba556578eee339e6dcbb751792f
parentlddtree: avoid shadowing function args (diff)
downloadpax-utils-1ddedd87363c65d6b910fe32da0f1764ba1329a9.tar.gz
pax-utils-1ddedd87363c65d6b910fe32da0f1764ba1329a9.tar.bz2
pax-utils-1ddedd87363c65d6b910fe32da0f1764ba1329a9.zip
lddtree: reformat with black
Largely this is just single quotes -> double quotes. Signed-off-by: Mike Frysinger <vapier@gentoo.org>
-rwxr-xr-xlddtree.py503
1 files changed, 294 insertions, 209 deletions
diff --git a/lddtree.py b/lddtree.py
index d894505..e851ac1 100755
--- a/lddtree.py
+++ b/lddtree.py
@@ -50,7 +50,7 @@ import shutil
import sys
from typing import Any, Iterable, Optional, Union
-assert sys.version_info >= (3, 6), f'Python 3.6+ required, but found {sys.version}'
+assert sys.version_info >= (3, 6), f"Python 3.6+ required, but found {sys.version}"
try:
import argcomplete
@@ -63,12 +63,12 @@ from elftools.elf.elffile import ELFFile
def warn(msg: Any, prefix: Optional[str] = "warning") -> None:
"""Write |msg| to stderr with a |prefix| before it"""
- print('%s: %s: %s' % (os.path.basename(sys.argv[0]), prefix, msg), file=sys.stderr)
+ print("%s: %s: %s" % (os.path.basename(sys.argv[0]), prefix, msg), file=sys.stderr)
def err(msg: Any, status: Optional[int] = 1) -> None:
"""Write |msg| to stderr and exit with |status|"""
- warn(msg, prefix='error')
+ warn(msg, prefix="error")
sys.exit(status)
@@ -82,7 +82,7 @@ def bstr(buf: Union[bytes, str]) -> str:
"""Decode the byte string into a string"""
if isinstance(buf, str):
return buf
- return buf.decode('utf-8')
+ return buf.decode("utf-8")
def normpath(path: str) -> str:
@@ -93,7 +93,7 @@ def normpath(path: str) -> str:
//..// -> //
//..//..// -> ///
"""
- return os.path.normpath(path).replace('//', '/')
+ return os.path.normpath(path).replace("//", "/")
@functools.lru_cache(maxsize=None)
@@ -115,9 +115,9 @@ def readlink(path: str, root: str, prefixed: Optional[bool] = False) -> str:
Returns:
A fully resolved symlink path
"""
- root = root.rstrip('/')
+ root = root.rstrip("/")
if prefixed:
- path = path[len(root):]
+ path = path[len(root) :]
while os.path.islink(root + path):
path = os.path.join(os.path.dirname(path), os.readlink(root + path))
@@ -137,9 +137,9 @@ def interp_supports_argv0(interp: str) -> bool:
Starting with glibc-2.33, the ldso supports --argv0 to override argv[0].
"""
- with open(interp, 'rb') as fp:
+ with open(interp, "rb") as fp:
with mmap.mmap(fp.fileno(), 0, prot=mmap.PROT_READ) as mm:
- return mm.find(b'--argv0') >= 0
+ return mm.find(b"--argv0") >= 0
def GenerateLdsoWrapper(
@@ -165,12 +165,11 @@ def GenerateLdsoWrapper(
# Add ldso interpreter dir to end of libpaths as a fallback library path.
libpaths = dedupe(list(libpaths) + [interp_dir])
replacements = {
- 'interp': os.path.join(os.path.relpath(interp_dir, basedir),
- interp_name),
+ "interp": os.path.join(os.path.relpath(interp_dir, basedir), interp_name),
"libpaths": ":".join(
"${basedir}/" + os.path.relpath(p, basedir) for p in libpaths
),
- 'argv0_arg': '--argv0 "$0"' if interp_supports_argv0(root + interp) else '',
+ "argv0_arg": '--argv0 "$0"' if interp_supports_argv0(root + interp) else "",
}
wrapper = """#!/bin/sh
if ! base=$(realpath "$0" 2>/dev/null); then
@@ -190,8 +189,8 @@ exec \\
"$@"
"""
wrappath = root + path
- os.rename(wrappath, wrappath + '.elf')
- with open(wrappath, 'w', encoding='utf-8') as f:
+ os.rename(wrappath, wrappath + ".elf")
+ with open(wrappath, "w", encoding="utf-8") as f:
f.write(wrapper % replacements)
os.chmod(wrappath, 0o0755)
@@ -223,17 +222,17 @@ def ParseLdPaths(
cwd = os.getcwd()
ldpaths = []
- for ldpath in str_ldpaths.split(':'):
+ for ldpath in str_ldpaths.split(":"):
# Expand placeholders first.
- if '$ORIGIN' in ldpath:
- ldpath = ldpath.replace('$ORIGIN', os.path.dirname(path))
- elif '${ORIGIN}' in ldpath:
- ldpath = ldpath.replace('${ORIGIN}', os.path.dirname(path))
+ if "$ORIGIN" in ldpath:
+ ldpath = ldpath.replace("$ORIGIN", os.path.dirname(path))
+ elif "${ORIGIN}" in ldpath:
+ ldpath = ldpath.replace("${ORIGIN}", os.path.dirname(path))
# Expand relative paths if needed. These don't make sense in general,
# but that doesn't stop people from using them. As such, root prefix
# doesn't make sense with it either.
- if not ldpath.startswith('/'):
+ if not ldpath.startswith("/"):
# NB: The ldso treats "" paths as cwd too.
ldpath = os.path.join(cwd, ldpath)
else:
@@ -265,27 +264,29 @@ def ParseLdSoConf(
"""
paths = []
- dbg_pfx = '' if _first else ' '
+ dbg_pfx = "" if _first else " "
try:
dbg(debug, f"{dbg_pfx}ParseLdSoConf({ldso_conf})")
- with open(ldso_conf, encoding='utf-8') as f:
+ with open(ldso_conf, encoding="utf-8") as f:
for line in f.readlines():
- line = line.split('#', 1)[0].strip()
+ line = line.split("#", 1)[0].strip()
if not line:
continue
- if line.startswith('include '):
+ if line.startswith("include "):
line = line[8:]
- if line[0] == '/':
- line = root + line.lstrip('/')
+ if line[0] == "/":
+ line = root + line.lstrip("/")
else:
- line = os.path.dirname(ldso_conf) + '/' + line
+ line = os.path.dirname(ldso_conf) + "/" + line
dbg(debug, dbg_pfx, "glob:", line)
# ldconfig in glibc uses glob() which returns entries sorted according
# to LC_COLLATE. Further, ldconfig does not reset that but respects
# the active env settings (which might be a mistake). Python does not
# sort its results by default though, so do it ourselves.
for path in sorted(glob.glob(line)):
- paths += ParseLdSoConf(path, root=root, debug=debug, _first=False)
+ paths += ParseLdSoConf(
+ path, root=root, debug=debug, _first=False
+ )
else:
paths += [normpath(root + line)]
except IOError as e:
@@ -320,25 +321,26 @@ def LoadLdpaths(
dict containing library paths to search
"""
ldpaths: dict[str, list[str]] = {
- 'conf': [],
- 'env': [],
- 'interp': [],
+ "conf": [],
+ "env": [],
+ "interp": [],
}
# Load up $LD_LIBRARY_PATH.
- ldpaths['env'] = []
- env_ldpath = os.environ.get('LD_LIBRARY_PATH')
+ ldpaths["env"] = []
+ env_ldpath = os.environ.get("LD_LIBRARY_PATH")
if not env_ldpath is None:
- if root != '/':
- warn('ignoring LD_LIBRARY_PATH due to ROOT usage')
+ if root != "/":
+ warn("ignoring LD_LIBRARY_PATH due to ROOT usage")
else:
# XXX: If this contains $ORIGIN, we probably have to parse this
# on a per-ELF basis so it can get turned into the right thing.
- ldpaths['env'] = ParseLdPaths(env_ldpath, cwd=cwd, path='')
+ ldpaths["env"] = ParseLdPaths(env_ldpath, cwd=cwd, path="")
# Load up /etc/ld.so.conf.
- ldpaths['conf'] = ParseLdSoConf(root + prefix + '/etc/ld.so.conf', root=root,
- debug=debug)
+ ldpaths["conf"] = ParseLdSoConf(
+ root + prefix + "/etc/ld.so.conf", root=root, debug=debug
+ )
return ldpaths
@@ -356,14 +358,16 @@ def CompatibleELFs(elf1: ELFFile, elf2: ELFFile) -> bool:
Returns:
True if compatible, False otherwise
"""
- osabis = frozenset([e.header['e_ident']['EI_OSABI'] for e in (elf1, elf2)])
+ osabis = frozenset([e.header["e_ident"]["EI_OSABI"] for e in (elf1, elf2)])
compat_sets = (
frozenset(f"ELFOSABI_{x}" for x in ("NONE", "SYSV", "GNU", "LINUX")),
)
- return ((len(osabis) == 1 or any(osabis.issubset(x) for x in compat_sets)) and
- elf1.elfclass == elf2.elfclass and
- elf1.little_endian == elf2.little_endian and
- elf1.header['e_machine'] == elf2.header['e_machine'])
+ return (
+ (len(osabis) == 1 or any(osabis.issubset(x) for x in compat_sets))
+ and elf1.elfclass == elf2.elfclass
+ and elf1.little_endian == elf2.little_endian
+ and elf1.header["e_machine"] == elf2.header["e_machine"]
+ )
def FindLib(
@@ -393,10 +397,10 @@ def FindLib(
if path != target:
dbg(debug, " checking:", path, "->", target)
else:
- dbg(debug, ' checking:', path)
+ dbg(debug, " checking:", path)
if os.path.exists(target):
- with open(target, 'rb') as f:
+ with open(target, "rb") as f:
try:
libelf = ELFFile(f)
if CompatibleELFs(elf, libelf):
@@ -456,18 +460,18 @@ def ParseELF(
_all_libs = {}
ldpaths = ldpaths.copy()
ret = {
- 'interp': None,
- 'path': path if display is None else display,
- 'realpath': path,
- 'needed': [],
- 'rpath': [],
- 'runpath': [],
- 'libs': _all_libs,
+ "interp": None,
+ "path": path if display is None else display,
+ "realpath": path,
+ "needed": [],
+ "rpath": [],
+ "runpath": [],
+ "libs": _all_libs,
}
dbg(debug, f"ParseELF({path})")
- with open(path, 'rb') as f:
+ with open(path, "rb") as f:
try:
elf = ELFFile(f)
except exceptions.ELFParseError:
@@ -477,17 +481,17 @@ def ParseELF(
# If this is the first ELF, extract the interpreter.
if _first:
for segment in elf.iter_segments():
- if segment.header.p_type != 'PT_INTERP':
+ if segment.header.p_type != "PT_INTERP":
continue
interp = bstr(segment.get_interp_name())
- dbg(debug, ' interp =', interp)
- ret['interp'] = normpath(root + interp)
- real_interp = readlink(ret['interp'], root, prefixed=True)
- ret['libs'][os.path.basename(interp)] = {
- 'path': ret['interp'],
- 'realpath': real_interp,
- 'needed': [],
+ dbg(debug, " interp =", interp)
+ ret["interp"] = normpath(root + interp)
+ real_interp = readlink(ret["interp"], root, prefixed=True)
+ ret["libs"][os.path.basename(interp)] = {
+ "path": ret["interp"],
+ "realpath": real_interp,
+ "needed": [],
}
# XXX: Could read it and scan for /lib paths.
# If the interp is a symlink, lets follow it on the assumption that it
@@ -498,12 +502,16 @@ def ParseELF(
# ld64.so.1 is really a symlink to ../lib64/ld64.so.1. In the multiarch
# setup, it'll be /lib/ld64.so.1 -> /lib/s390x-linux-gnu/ld64.so.1.
# That is why we use |real_interp| here instead of |interp|.
- ldpaths['interp'] = [
+ ldpaths["interp"] = [
os.path.dirname(real_interp),
- normpath(root + prefix + '/usr/' + os.path.dirname(
- real_interp)[len(root) + len(prefix):]),
+ normpath(
+ root
+ + prefix
+ + "/usr/"
+ + os.path.dirname(real_interp)[len(root) + len(prefix) :]
+ ),
]
- dbg(debug, ' ldpaths[interp] =', ldpaths['interp'])
+ dbg(debug, " ldpaths[interp] =", ldpaths["interp"])
break
# Parse the ELF's dynamic tags.
@@ -511,15 +519,17 @@ def ParseELF(
rpaths = []
runpaths = []
for segment in elf.iter_segments():
- if segment.header.p_type != 'PT_DYNAMIC':
+ if segment.header.p_type != "PT_DYNAMIC":
continue
for t in segment.iter_tags():
- if t.entry.d_tag == 'DT_RPATH':
+ if t.entry.d_tag == "DT_RPATH":
rpaths = ParseLdPaths(bstr(t.rpath), root=root, cwd=cwd, path=path)
- elif t.entry.d_tag == 'DT_RUNPATH':
- runpaths = ParseLdPaths(bstr(t.runpath), root=root, cwd=cwd, path=path)
- elif t.entry.d_tag == 'DT_NEEDED':
+ elif t.entry.d_tag == "DT_RUNPATH":
+ runpaths = ParseLdPaths(
+ bstr(t.runpath), root=root, cwd=cwd, path=path
+ )
+ elif t.entry.d_tag == "DT_NEEDED":
libs.append(bstr(t.needed))
if runpaths:
# If both RPATH and RUNPATH are set, only the latter is used.
@@ -531,13 +541,13 @@ def ParseELF(
if _first:
# Propagate the rpaths used by the main ELF since those will be
# used at runtime to locate things.
- ldpaths['rpath'] = rpaths
- ldpaths['runpath'] = runpaths
- dbg(debug, ' ldpaths[rpath] =', rpaths)
- dbg(debug, ' ldpaths[runpath] =', runpaths)
- ret['rpath'] = rpaths
- ret['runpath'] = runpaths
- ret['needed'] = libs
+ ldpaths["rpath"] = rpaths
+ ldpaths["runpath"] = runpaths
+ dbg(debug, " ldpaths[rpath] =", rpaths)
+ dbg(debug, " ldpaths[runpath] =", runpaths)
+ ret["rpath"] = rpaths
+ ret["runpath"] = runpaths
+ ret["needed"] = libs
# Search for the libs this ELF uses.
all_ldpaths = None
@@ -546,29 +556,42 @@ def ParseELF(
continue
if all_ldpaths is None:
all_ldpaths = (
- rpaths + ldpaths['rpath'] +
- ldpaths['env'] +
- runpaths + ldpaths['runpath'] +
- ldpaths['conf'] +
- ldpaths['interp']
+ rpaths
+ + ldpaths["rpath"]
+ + ldpaths["env"]
+ + runpaths
+ + ldpaths["runpath"]
+ + ldpaths["conf"]
+ + ldpaths["interp"]
)
realpath, fullpath = FindLib(elf, lib, all_ldpaths, root, debug=debug)
_all_libs[lib] = {
- 'realpath': realpath,
- 'path': fullpath,
- 'needed': [],
+ "realpath": realpath,
+ "path": fullpath,
+ "needed": [],
}
if realpath is not None:
try:
- lret = ParseELF(realpath, root, cwd, prefix, ldpaths, display=fullpath,
- debug=debug, _first=False, _all_libs=_all_libs)
+ lret = ParseELF(
+ realpath,
+ root,
+ cwd,
+ prefix,
+ ldpaths,
+ display=fullpath,
+ debug=debug,
+ _first=False,
+ _all_libs=_all_libs,
+ )
except exceptions.ELFError as e:
warn(f"{realpath}: {e}")
- _all_libs[lib]['needed'] = lret['needed']
+ _all_libs[lib]["needed"] = lret["needed"]
del elf
return ret
+
+
# pylint: enable=dangerous-default-value
@@ -579,9 +602,10 @@ class _NormalizePathAction(argparse.Action):
def _ActionShow(options: argparse.Namespace, elf: dict):
"""Show the dependency tree for this ELF"""
+
def _show(lib, depth):
chain_libs.append(lib)
- fullpath = elf['libs'][lib]['path']
+ fullpath = elf["libs"][lib]["path"]
if options.list:
print(fullpath or lib)
else:
@@ -602,10 +626,10 @@ def _ActionShow(options: argparse.Namespace, elf: dict):
_show(nlib, depth + 1)
chain_libs.pop()
- shown_libs = set(elf['needed'])
- new_libs = elf['needed'][:]
+ shown_libs = set(elf["needed"])
+ new_libs = elf["needed"][:]
chain_libs: list[str] = []
- interp = elf['interp']
+ interp = elf["interp"]
if interp:
lib = os.path.basename(interp)
shown_libs.add(lib)
@@ -617,7 +641,7 @@ def _ActionShow(options: argparse.Namespace, elf: dict):
if not options.all and options.list and lib in new_libs:
new_libs.remove(lib)
if options.list:
- print(elf['path'])
+ print(elf["path"])
if not interp is None:
print(interp)
else:
@@ -628,17 +652,17 @@ def _ActionShow(options: argparse.Namespace, elf: dict):
def _ActionCopy(options: argparse.Namespace, elf: dict):
"""Copy the ELF and its dependencies to a destination tree"""
+
def _StripRoot(path: str) -> str:
- return path[len(options.root) - 1:]
+ return path[len(options.root) - 1 :]
- def _copy(realsrc, src, striproot=True, wrapit=False, libpaths=(),
- outdir=None):
+ def _copy(realsrc, src, striproot=True, wrapit=False, libpaths=(), outdir=None):
if realsrc is None:
return
if wrapit:
# Static ELFs don't need to be wrapped.
- if not elf['interp']:
+ if not elf["interp"]:
wrapit = False
striproot = _StripRoot if striproot else lambda x: x
@@ -651,11 +675,10 @@ def _ActionCopy(options: argparse.Namespace, elf: dict):
try:
# See if they're the same file.
- nstat = os.stat(dst + ('.elf' if wrapit else ''))
+ nstat = os.stat(dst + (".elf" if wrapit else ""))
ostat = os.stat(realsrc)
- for field in ('mode', 'mtime', 'size'):
- if getattr(ostat, 'st_' + field) != \
- getattr(nstat, 'st_' + field):
+ for field in ("mode", "mtime", "size"):
+ if getattr(ostat, "st_" + field) != getattr(nstat, "st_" + field):
break
else:
return
@@ -684,9 +707,9 @@ def _ActionCopy(options: argparse.Namespace, elf: dict):
print("generate wrapper", dst)
if options.libdir:
- interp = os.path.join(options.libdir, os.path.basename(elf['interp']))
+ interp = os.path.join(options.libdir, os.path.basename(elf["interp"]))
else:
- interp = _StripRoot(elf['interp'])
+ interp = _StripRoot(elf["interp"])
GenerateLdsoWrapper(options.dest, subdst, interp, libpaths)
# XXX: We should automatically import libgcc_s.so whenever libpthread.so
@@ -695,99 +718,153 @@ def _ActionCopy(options: argparse.Namespace, elf: dict):
# the libnsl.so and libnss_*.so libraries, as well as an open ended list
# for known libs that get loaded (e.g. curl will dlopen(libresolv)).
uniq_libpaths = set()
- for lib in elf['libs']:
- libdata = elf['libs'][lib]
- path = libdata['realpath']
+ for lib in elf["libs"]:
+ libdata = elf["libs"][lib]
+ path = libdata["realpath"]
if path is None:
warn("could not locate library:", lib)
continue
if not options.libdir:
uniq_libpaths.add(_StripRoot(os.path.dirname(path)))
- _copy(path, libdata['path'], outdir=options.libdir)
+ _copy(path, libdata["path"], outdir=options.libdir)
if not options.libdir:
libpaths = list(uniq_libpaths)
- if elf['runpath']:
- libpaths = elf['runpath'] + libpaths
+ if elf["runpath"]:
+ libpaths = elf["runpath"] + libpaths
else:
- libpaths = elf['rpath'] + libpaths
+ libpaths = elf["rpath"] + libpaths
else:
uniq_libpaths.add(options.libdir)
libpaths = list(uniq_libpaths)
# We don't bother to copy this as ParseElf adds the interp to the 'libs',
# so it was already copied in the libs loop above.
- #_copy(elf['interp'], outdir=options.libdir)
- _copy(elf['realpath'], elf['path'], striproot=options.auto_root,
- wrapit=options.generate_wrappers, libpaths=libpaths,
- outdir=options.bindir)
+ # _copy(elf['interp'], outdir=options.libdir)
+ _copy(
+ elf["realpath"],
+ elf["path"],
+ striproot=options.auto_root,
+ wrapit=options.generate_wrappers,
+ libpaths=libpaths,
+ outdir=options.bindir,
+ )
def GetParser() -> argparse.ArgumentParser:
"""Get a CLI parser."""
parser = argparse.ArgumentParser(
- description=__doc__,
- formatter_class=argparse.RawDescriptionHelpFormatter)
- parser.add_argument('-a', '--all',
- action='store_true', default=False,
- help='Show all duplicated dependencies')
- parser.add_argument('-l', '--list',
- action='store_true', default=False,
- help='Display output in a simple list (easy for copying)')
- parser.add_argument('-x', '--debug',
- action='store_true', default=False,
- help='Run with debugging')
- parser.add_argument('-v', '--verbose',
- action='store_true', default=False,
- help='Be verbose')
- parser.add_argument('--skip-non-elfs',
- action='store_true', default=False,
- help='Skip plain (non-ELF) files instead of warning')
- parser.add_argument('--skip-missing',
- action='store_true', default=False,
- help='Skip missing files instead of failing')
- parser.add_argument('-V', '--version',
- action='version',
- version='lddtree by Mike Frysinger <vapier@gentoo.org>',
- help='Show version information')
- parser.add_argument('path', nargs='+')
-
- group = parser.add_argument_group('Path options')
- group.add_argument('-R', '--root',
- default=os.environ.get('ROOT', ''), type=str,
- action=_NormalizePathAction,
- help='Search for all files/dependencies in ROOT')
- group.add_argument('--no-auto-root',
- dest='auto_root', action='store_false', default=True,
- help='Do not automatically prefix input ELFs with ROOT')
- group.add_argument('-C', '--cwd',
- default=os.getcwd(), type=str, action=_NormalizePathAction,
- help='Path to resolve relative paths against')
- group.add_argument('-P', '--prefix',
- default=os.environ.get(
- 'EPREFIX', '@GENTOO_PORTAGE_EPREFIX@'), type=str,
- action=_NormalizePathAction,
- help='Specify EPREFIX for binaries (for Gentoo Prefix)')
-
- group = parser.add_argument_group('Copying options')
- group.add_argument('--copy-to-tree',
- dest='dest', default=None, type=str,
- action=_NormalizePathAction,
- help='Copy all files to the specified tree')
- group.add_argument('--bindir',
- default=None, type=str,
- action=_NormalizePathAction,
- help='Dir to store all ELFs specified on the command line')
- group.add_argument('--libdir',
- default=None, type=str,
- action=_NormalizePathAction,
- help='Dir to store all ELF libs')
- group.add_argument('--generate-wrappers',
- action='store_true', default=False,
- help='Wrap executable ELFs with scripts for local ldso')
- group.add_argument('--copy-non-elfs',
- action='store_true', default=False,
- help='Copy over plain (non-ELF) files instead of warn+ignore')
+ description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
+ )
+ parser.add_argument(
+ "-a",
+ "--all",
+ action="store_true",
+ default=False,
+ help="Show all duplicated dependencies",
+ )
+ parser.add_argument(
+ "-l",
+ "--list",
+ action="store_true",
+ default=False,
+ help="Display output in a simple list (easy for copying)",
+ )
+ parser.add_argument(
+ "-x", "--debug", action="store_true", default=False, help="Run with debugging"
+ )
+ parser.add_argument(
+ "-v", "--verbose", action="store_true", default=False, help="Be verbose"
+ )
+ parser.add_argument(
+ "--skip-non-elfs",
+ action="store_true",
+ default=False,
+ help="Skip plain (non-ELF) files instead of warning",
+ )
+ parser.add_argument(
+ "--skip-missing",
+ action="store_true",
+ default=False,
+ help="Skip missing files instead of failing",
+ )
+ parser.add_argument(
+ "-V",
+ "--version",
+ action="version",
+ version="lddtree by Mike Frysinger <vapier@gentoo.org>",
+ help="Show version information",
+ )
+ parser.add_argument("path", nargs="+")
+
+ group = parser.add_argument_group("Path options")
+ group.add_argument(
+ "-R",
+ "--root",
+ default=os.environ.get("ROOT", ""),
+ type=str,
+ action=_NormalizePathAction,
+ help="Search for all files/dependencies in ROOT",
+ )
+ group.add_argument(
+ "--no-auto-root",
+ dest="auto_root",
+ action="store_false",
+ default=True,
+ help="Do not automatically prefix input ELFs with ROOT",
+ )
+ group.add_argument(
+ "-C",
+ "--cwd",
+ default=os.getcwd(),
+ type=str,
+ action=_NormalizePathAction,
+ help="Path to resolve relative paths against",
+ )
+ group.add_argument(
+ "-P",
+ "--prefix",
+ default=os.environ.get("EPREFIX", "@GENTOO_PORTAGE_EPREFIX@"),
+ type=str,
+ action=_NormalizePathAction,
+ help="Specify EPREFIX for binaries (for Gentoo Prefix)",
+ )
+
+ group = parser.add_argument_group("Copying options")
+ group.add_argument(
+ "--copy-to-tree",
+ dest="dest",
+ default=None,
+ type=str,
+ action=_NormalizePathAction,
+ help="Copy all files to the specified tree",
+ )
+ group.add_argument(
+ "--bindir",
+ default=None,
+ type=str,
+ action=_NormalizePathAction,
+ help="Dir to store all ELFs specified on the command line",
+ )
+ group.add_argument(
+ "--libdir",
+ default=None,
+ type=str,
+ action=_NormalizePathAction,
+ help="Dir to store all ELF libs",
+ )
+ group.add_argument(
+ "--generate-wrappers",
+ action="store_true",
+ default=False,
+ help="Wrap executable ELFs with scripts for local ldso",
+ )
+ group.add_argument(
+ "--copy-non-elfs",
+ action="store_true",
+ default=False,
+ help="Copy over plain (non-ELF) files instead of warn+ignore",
+ )
if argcomplete is not None:
argcomplete.autocomplete(parser)
@@ -800,41 +877,42 @@ def main(argv: list[str]) -> Optional[int]:
options = parser.parse_args(argv)
paths = options.path
- if options.root != '/':
- options.root += '/'
- if options.prefix == '@''GENTOO_PORTAGE_EPREFIX''@':
- options.prefix = ''
+ if options.root != "/":
+ options.root += "/"
+ if options.prefix == "@" "GENTOO_PORTAGE_EPREFIX" "@":
+ options.prefix = ""
- if options.bindir and options.bindir[0] != '/':
- parser.error('--bindir accepts absolute paths only')
- if options.libdir and options.libdir[0] != '/':
- parser.error('--libdir accepts absolute paths only')
+ if options.bindir and options.bindir[0] != "/":
+ parser.error("--bindir accepts absolute paths only")
+ if options.libdir and options.libdir[0] != "/":
+ parser.error("--libdir accepts absolute paths only")
if options.skip_non_elfs and options.copy_non_elfs:
- parser.error('pick one handler for non-ELFs: skip or copy')
+ parser.error("pick one handler for non-ELFs: skip or copy")
- dbg(options.debug, 'root =', options.root)
- dbg(options.debug, 'cwd =', options.cwd)
+ dbg(options.debug, "root =", options.root)
+ dbg(options.debug, "cwd =", options.cwd)
if options.dest:
- dbg(options.debug, 'dest =', options.dest)
+ dbg(options.debug, "dest =", options.dest)
if not paths:
- err('missing ELF files to scan')
+ err("missing ELF files to scan")
- ldpaths = LoadLdpaths(options.root, cwd=options.cwd, prefix=options.prefix,
- debug=options.debug)
- dbg(options.debug, 'ldpaths[conf] =', ldpaths['conf'])
- dbg(options.debug, 'ldpaths[env] =', ldpaths['env'])
+ ldpaths = LoadLdpaths(
+ options.root, cwd=options.cwd, prefix=options.prefix, debug=options.debug
+ )
+ dbg(options.debug, "ldpaths[conf] =", ldpaths["conf"])
+ dbg(options.debug, "ldpaths[env] =", ldpaths["env"])
# Process all the files specified.
ret = 0
for path in paths:
- dbg(options.debug, 'argv[x] =', path)
+ dbg(options.debug, "argv[x] =", path)
# Only auto-prefix the path if the ELF is absolute.
# If it's a relative path, the user most likely wants
# the local path.
- if options.auto_root and path.startswith('/'):
- path = options.root + path.lstrip('/')
- dbg(options.debug, ' +auto-root =', path)
+ if options.auto_root and path.startswith("/"):
+ path = options.root + path.lstrip("/")
+ dbg(options.debug, " +auto-root =", path)
matched = False
for p in glob.iglob(path):
@@ -844,20 +922,27 @@ def main(argv: list[str]) -> Optional[int]:
# $ lddtree --root $PWD/root /bin/sh
# First we'd turn /bin/sh into $PWD/root/bin/sh, then we want to resolve
# the symlink to $PWD/root/bin/bash rather than a plain /bin/bash.
- dbg(options.debug, ' globbed =', p)
- if not path.startswith('/'):
+ dbg(options.debug, " globbed =", p)
+ if not path.startswith("/"):
realpath = os.path.realpath(path)
elif options.auto_root:
realpath = readlink(p, options.root, prefixed=True)
else:
realpath = path
if path != realpath:
- dbg(options.debug, ' resolved =', realpath)
+ dbg(options.debug, " resolved =", realpath)
matched = True
try:
- elf = ParseELF(realpath, options.root, options.cwd, options.prefix, ldpaths,
- display=p, debug=options.debug)
+ elf = ParseELF(
+ realpath,
+ options.root,
+ options.cwd,
+ options.prefix,
+ ldpaths,
+ display=p,
+ debug=options.debug,
+ )
except exceptions.ELFError as e:
if options.skip_non_elfs:
continue
@@ -865,12 +950,12 @@ def main(argv: list[str]) -> Optional[int]:
if options.dest is not None and options.copy_non_elfs:
if os.path.exists(p):
elf = {
- 'interp': None,
- 'libs': [],
- 'runpath': [],
- 'rpath': [],
- 'path': p,
- 'realpath': realpath,
+ "interp": None,
+ "libs": [],
+ "runpath": [],
+ "rpath": [],
+ "path": p,
+ "realpath": realpath,
}
_ActionCopy(options, elf)
continue
@@ -895,5 +980,5 @@ def main(argv: list[str]) -> Optional[int]:
return ret
-if __name__ == '__main__':
+if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))