27
27
from elftools .elf .sections import NoteSection
28
28
29
29
from .architecture import Architecture
30
- from .libc import Libc , get_libc
30
+ from .error import InvalidLibc
31
+ from .libc import Libc
31
32
32
33
log = logging .getLogger (__name__ )
33
34
__all__ = ["DynamicExecutable" , "DynamicLibrary" , "ldd" ]
@@ -80,6 +81,7 @@ class DynamicLibrary:
80
81
@dataclass (frozen = True )
81
82
class DynamicExecutable :
82
83
interpreter : str | None
84
+ libc : Libc | None
83
85
path : str
84
86
realpath : Path
85
87
platform : Platform
@@ -295,7 +297,9 @@ def parse_ld_so_conf(ldso_conf: str, root: str = "/", _first: bool = True) -> li
295
297
296
298
297
299
@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 ]]:
299
303
"""Load linker paths from common locations
300
304
301
305
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]]:
323
327
# on a per-ELF basis so it can get turned into the right thing.
324
328
ldpaths ["env" ] = parse_ld_paths (env_ldpath , path = "" )
325
329
326
- libc = get_libc ()
327
330
if libc == Libc .MUSL :
328
331
# from https://git.musl-libc.org/cgit/musl/tree/ldso
329
332
# /dynlink.c?id=3f701faace7addc75d16dea8a6cd769fa5b3f260#n1063
@@ -436,31 +439,43 @@ def ldd(
436
439
},
437
440
}
438
441
"""
439
- if not ldpaths :
440
- ldpaths = load_ld_paths ().copy ()
441
-
442
442
_first = _all_libs is None
443
443
if _all_libs is None :
444
444
_all_libs = {}
445
445
446
446
log .debug ("ldd(%s)" , path )
447
447
448
448
interpreter : str | None = None
449
+ libc : Libc | None = None
449
450
needed : set [str ] = set ()
450
451
rpaths : list [str ] = []
451
452
runpaths : list [str ] = []
452
453
453
454
with open (path , "rb" ) as f :
454
455
elf = ELFFile (f )
456
+
457
+ # get the platform
458
+ platform = _get_platform (elf )
459
+
455
460
# If this is the first ELF, extract the interpreter.
456
461
if _first :
457
462
for segment in elf .iter_segments ():
458
463
if segment .header .p_type != "PT_INTERP" :
459
464
continue
460
-
461
465
interp = segment .get_interp_name ()
462
466
log .debug (" interp = %s" , interp )
463
467
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 ()
464
479
# XXX: Should read it and scan for /lib paths.
465
480
ldpaths ["interp" ] = [
466
481
normpath (root + os .path .dirname (interp )),
@@ -471,14 +486,10 @@ def ldd(
471
486
log .debug (" ldpaths[interp] = %s" , ldpaths ["interp" ])
472
487
break
473
488
474
- # get the platform
475
- platform = _get_platform (elf )
476
-
477
489
# Parse the ELF's dynamic tags.
478
490
for segment in elf .iter_segments ():
479
491
if segment .header .p_type != "PT_DYNAMIC" :
480
492
continue
481
-
482
493
for t in segment .iter_tags ():
483
494
if t .entry .d_tag == "DT_RPATH" :
484
495
rpaths = parse_ld_paths (t .rpath , path = str (path ), root = root )
@@ -497,14 +508,31 @@ def ldd(
497
508
del elf
498
509
499
510
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 ()
500
527
# Propagate the rpaths used by the main ELF since those will be
501
528
# used at runtime to locate things.
502
529
ldpaths ["rpath" ] = rpaths
503
530
ldpaths ["runpath" ] = runpaths
504
531
log .debug (" ldpaths[rpath] = %s" , rpaths )
505
532
log .debug (" ldpaths[runpath] = %s" , runpaths )
506
533
507
- # Search for the libs this ELF uses.
534
+ assert ldpaths is not None
535
+
508
536
all_ldpaths = (
509
537
ldpaths ["rpath" ]
510
538
+ rpaths
@@ -541,17 +569,9 @@ def ldd(
541
569
dependency .needed ,
542
570
)
543
571
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
-
553
572
return DynamicExecutable (
554
573
interpreter ,
574
+ libc ,
555
575
str (path ) if display is None else display ,
556
576
path ,
557
577
platform ,
0 commit comments