Files
llvm-tms34010/lldb/scripts/extract-dynamic-script-interpreter-exports.py
Jonas Devlieghere 119e576302 [lldb] Build liblldb exports from script-interpreter imports (#201392)
With LLDB_ENABLE_DYNAMIC_SCRIPTINTERPRETERS, the script interpreter
plugins are built as standalone shared libraries that resolve their
lldb_private/llvm references through liblldb's re-exports. liblldb was
falling back to liblldb-private.exports, which allowlists the
lldb_private/llvm namespaces wholesale.

Generate the export list per build instead. For each plugin, run llvm-nm
on its compiled objects, keep undefined references in namespaces liblldb
owns, subtract the plugin's own definitions, and concatenate the
per-plugin lists with liblldb.exports.

The plugin's SHARED target LINK_LIBS liblldb, which makes the plugin's
compile order depend on liblldb. Wiring the exports file in via
add_llvm_symbol_exports' built-in target dependency would form a cycle
through that order-only edge. Add NO_TARGET_DEPENDENCY to
add_llvm_symbol_exports so we can apply the file via LINK_DEPENDS alone;
that still triggers a relink when the symbol set changes, without
participating in compile-order tracking.

Windows is unchanged: msvc_extract_private_symbols.py was already doing
per-symbol extraction at namespace granularity.

Assisted-by: Claude
2026-06-04 16:59:18 +00:00

101 lines
2.7 KiB
Python

"""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())