lib_external_deps.py (1651B)
1 #!/usr/bin/env python3 2 """Print external symbols that an archive (.a) depends on. 3 4 Runs `nm` over the archive, then reports symbols that are undefined in some 5 member but not defined by any other member of the same archive. 6 """ 7 8 import argparse 9 import subprocess 10 import sys 11 12 13 def external_deps(archive: str, nm: str = "nm") -> list[str]: 14 out = subprocess.run( 15 [nm, archive], check=True, capture_output=True, text=True 16 ).stdout 17 18 defined: set[str] = set() 19 undefined: set[str] = set() 20 21 for line in out.splitlines(): 22 line = line.rstrip() 23 if not line or line.endswith(":"): 24 # Blank line or "<member>.o:" header. 25 continue 26 parts = line.split(None, 2) 27 if len(parts) < 2: 28 continue 29 if len(parts) == 2: 30 # Undefined: "<type> <name>" (no address column). 31 type_, name = parts 32 else: 33 # Defined: "<addr> <type> <name>". 34 _, type_, name = parts 35 36 if type_ == "U": 37 undefined.add(name) 38 elif type_.isupper(): 39 # Global definition (T, D, B, R, S, A, C, ...). 40 defined.add(name) 41 # Lowercase = local (file-scope); irrelevant for archive linkage. 42 43 return sorted(undefined - defined) 44 45 46 def main() -> int: 47 ap = argparse.ArgumentParser(description=__doc__) 48 ap.add_argument("archive", help="Path to a static archive (.a)") 49 ap.add_argument("--nm", default="nm", help="nm binary to use") 50 args = ap.parse_args() 51 52 for sym in external_deps(args.archive, args.nm): 53 print(sym) 54 return 0 55 56 57 if __name__ == "__main__": 58 sys.exit(main())