2727from elftools .elf .sections import NoteSection
2828
2929from .architecture import Architecture
30- from .libc import Libc , get_libc
30+ from .error import InvalidLibc
31+ from .libc import Libc
3132
3233log = logging .getLogger (__name__ )
3334__all__ = ["DynamicExecutable" , "DynamicLibrary" , "ldd" ]
@@ -80,6 +81,7 @@ class DynamicLibrary:
8081@dataclass (frozen = True )
8182class DynamicExecutable :
8283 interpreter : str | None
84+ libc : Libc | None
8385 path : str
8486 realpath : Path
8587 platform : Platform
@@ -295,7 +297,9 @@ def parse_ld_so_conf(ldso_conf: str, root: str = "/", _first: bool = True) -> li
295297
296298
297299@functools .lru_cache
298- def load_ld_paths (root : str = "/" , prefix : str = "" ) -> dict [str , list [str ]]:
300+ def load_ld_paths (
301+ libc : Libc | None , root : str = "/" , prefix : str = ""
302+ ) -> dict [str , list [str ]]:
299303 """Load linker paths from common locations
300304
301305 This parses the ld.so.conf and LD_LIBRARY_PATH env var.
@@ -323,7 +327,6 @@ def load_ld_paths(root: str = "/", prefix: str = "") -> dict[str, list[str]]:
323327 # on a per-ELF basis so it can get turned into the right thing.
324328 ldpaths ["env" ] = parse_ld_paths (env_ldpath , path = "" )
325329
326- libc = get_libc ()
327330 if libc == Libc .MUSL :
328331 # from https://git.musl-libc.org/cgit/musl/tree/ldso
329332 # /dynlink.c?id=3f701faace7addc75d16dea8a6cd769fa5b3f260#n1063
@@ -436,31 +439,43 @@ def ldd(
436439 },
437440 }
438441 """
439- if not ldpaths :
440- ldpaths = load_ld_paths ().copy ()
441-
442442 _first = _all_libs is None
443443 if _all_libs is None :
444444 _all_libs = {}
445445
446446 log .debug ("ldd(%s)" , path )
447447
448448 interpreter : str | None = None
449+ libc : Libc | None = None
449450 needed : set [str ] = set ()
450451 rpaths : list [str ] = []
451452 runpaths : list [str ] = []
452453
453454 with open (path , "rb" ) as f :
454455 elf = ELFFile (f )
456+
457+ # get the platform
458+ platform = _get_platform (elf )
459+
455460 # If this is the first ELF, extract the interpreter.
456461 if _first :
457462 for segment in elf .iter_segments ():
458463 if segment .header .p_type != "PT_INTERP" :
459464 continue
460-
461465 interp = segment .get_interp_name ()
462466 log .debug (" interp = %s" , interp )
463467 interpreter = normpath (root + interp )
468+ soname = os .path .basename (interpreter )
469+ _all_libs [soname ] = DynamicLibrary (
470+ soname ,
471+ interpreter ,
472+ Path (readlink (interpreter , root , prefixed = True )),
473+ platform ,
474+ )
475+ # if we have an interpreter and it's not MUSL, assume GLIBC
476+ libc = Libc .MUSL if soname .startswith ("ld-musl-" ) else Libc .GLIBC
477+ if ldpaths is None :
478+ ldpaths = load_ld_paths (libc ).copy ()
464479 # XXX: Should read it and scan for /lib paths.
465480 ldpaths ["interp" ] = [
466481 normpath (root + os .path .dirname (interp )),
@@ -471,14 +486,10 @@ def ldd(
471486 log .debug (" ldpaths[interp] = %s" , ldpaths ["interp" ])
472487 break
473488
474- # get the platform
475- platform = _get_platform (elf )
476-
477489 # Parse the ELF's dynamic tags.
478490 for segment in elf .iter_segments ():
479491 if segment .header .p_type != "PT_DYNAMIC" :
480492 continue
481-
482493 for t in segment .iter_tags ():
483494 if t .entry .d_tag == "DT_RPATH" :
484495 rpaths = parse_ld_paths (t .rpath , path = str (path ), root = root )
@@ -497,14 +508,31 @@ def ldd(
497508 del elf
498509
499510 if _first :
511+ # get the libc based on dependencies
512+ for soname in needed :
513+ if soname .startswith ("libc.musl-" ):
514+ if libc is None :
515+ libc = Libc .MUSL
516+ if libc != Libc .MUSL :
517+ msg = f"found a dependency on MUSL but the libc is already set to { libc } "
518+ raise InvalidLibc (msg )
519+ elif soname == "libc.so.6" :
520+ if libc is None :
521+ libc = Libc .GLIBC
522+ if libc != Libc .GLIBC :
523+ msg = f"found a dependency on GLIBC but the libc is already set to { libc } "
524+ raise InvalidLibc (msg )
525+ if ldpaths is None :
526+ ldpaths = load_ld_paths (libc ).copy ()
500527 # Propagate the rpaths used by the main ELF since those will be
501528 # used at runtime to locate things.
502529 ldpaths ["rpath" ] = rpaths
503530 ldpaths ["runpath" ] = runpaths
504531 log .debug (" ldpaths[rpath] = %s" , rpaths )
505532 log .debug (" ldpaths[runpath] = %s" , runpaths )
506533
507- # Search for the libs this ELF uses.
534+ assert ldpaths is not None
535+
508536 all_ldpaths = (
509537 ldpaths ["rpath" ]
510538 + rpaths
@@ -541,17 +569,9 @@ def ldd(
541569 dependency .needed ,
542570 )
543571
544- if interpreter is not None :
545- soname = os .path .basename (interpreter )
546- _all_libs [soname ] = DynamicLibrary (
547- soname ,
548- interpreter ,
549- Path (readlink (interpreter , root , prefixed = True )),
550- platform ,
551- )
552-
553572 return DynamicExecutable (
554573 interpreter ,
574+ libc ,
555575 str (path ) if display is None else display ,
556576 path ,
557577 platform ,
0 commit comments