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
101 lines
2.7 KiB
Python
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())
|