| """Emit the LLDB-side imports of a script interpreter plugin's objects. |
| |
| The result is fed into liblldb's exports file so the dynamic loader can |
| satisfy the plugin's references at runtime without growing liblldb's |
| re-export surface beyond what the plugin actually consumes. |
| """ |
| |
| import argparse |
| import subprocess |
| import sys |
| |
| |
| # Mangled-name prefixes liblldb owns. Symbols outside these namespaces |
| # are resolved through other libraries on the loader path. |
| EXPORT_PREFIXES = ( |
| "_ZN12lldb_private", |
| "_ZNK12lldb_private", |
| "_ZTVN12lldb_private", |
| "_ZTSN12lldb_private", |
| "_ZTIN12lldb_private", |
| "_ZN4lldb", |
| "_ZNK4lldb", |
| "_ZTVN4lldb", |
| "_ZTSN4lldb", |
| "_ZTIN4lldb", |
| "_ZN4llvm", |
| "_ZNK4llvm", |
| "_ZTVN4llvm", |
| "_ZTSN4llvm", |
| "_ZTIN4llvm", |
| ) |
| |
| |
| def normalize(sym, mach_o): |
| # Mach-O reports an extra leading underscore that the .exports file |
| # format does not carry. |
| if mach_o and sym.startswith("_"): |
| return sym[1:] |
| return sym |
| |
| |
| def collect(nm, obj, mach_o): |
| """Return (undefined, defined) symbol-name sets for `obj`. |
| |
| Both halves are needed so cross-object references that the plugin's |
| own objects satisfy can be subtracted before emitting. |
| """ |
| undefined = set() |
| defined = set() |
| |
| out = subprocess.check_output( |
| [nm, "--undefined-only", "--just-symbol-name", obj], text=True |
| ) |
| for line in out.splitlines(): |
| sym = line.strip() |
| if sym: |
| undefined.add(normalize(sym, mach_o)) |
| |
| out = subprocess.check_output( |
| [nm, "--defined-only", "--extern-only", "--just-symbol-name", obj], |
| text=True, |
| ) |
| for line in out.splitlines(): |
| sym = line.strip() |
| if sym: |
| defined.add(normalize(sym, mach_o)) |
| |
| return undefined, defined |
| |
| |
| def main(): |
| parser = argparse.ArgumentParser(description=__doc__) |
| parser.add_argument("--nm", required=True, help="Path to llvm-nm.") |
| parser.add_argument("-o", "--output", required=True, help="Output exports file.") |
| parser.add_argument( |
| "--mach-o", |
| action="store_true", |
| help="Strip the leading underscore Mach-O prepends to every symbol.", |
| ) |
| parser.add_argument( |
| "objects", nargs="+", help="Object files (or archives) to scan." |
| ) |
| args = parser.parse_args() |
| |
| undefined = set() |
| defined = set() |
| for obj in args.objects: |
| u, d = collect(args.nm, obj, args.mach_o) |
| undefined.update(u) |
| defined.update(d) |
| |
| syms = {s for s in (undefined - defined) if s.startswith(EXPORT_PREFIXES)} |
| |
| with open(args.output, "w", newline="\n") as f: |
| for s in sorted(syms): |
| f.write(s + "\n") |
| |
| |
| if __name__ == "__main__": |
| sys.exit(main()) |