blob: b3da994b2bef1fb2b8f2e73314076bbf2b7a59b7 [file] [edit]
"""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())