1"""A tool for extracting a list of private lldb symbols to export for MSVC. 2 3When exporting symbols from a dll or exe we either need to mark the symbols in 4the source code as __declspec(dllexport) or supply a list of symbols to the 5linker. Private symbols in LLDB don't explicitly specific dllexport, so we 6automate that by examining the symbol table. 7""" 8 9import argparse 10import os 11import re 12import subprocess 13import sys 14 15 16def extract_symbols(nm_path: str, lib: str): 17 """Extract all of the private lldb symbols from the given path to llvm-nm and 18 library to extract from.""" 19 20 # Matches mangled symbols containing 'lldb_private'. 21 lldb_sym_re = r"[0-9a-zA-Z]* [BT] (?P<symbol>[?]+[^?].*lldb_private.*)" 22 23 # '-g' means we only get global symbols. 24 # '-p' do not waste time sorting the symbols. 25 process = subprocess.Popen( 26 [nm_path, "-g", "-p", lib], 27 bufsize=1, 28 stdout=subprocess.PIPE, 29 stdin=subprocess.PIPE, 30 universal_newlines=True, 31 ) 32 process.stdin.close() 33 34 lldb_symbols = set() 35 for line in process.stdout: 36 match = re.match(lldb_sym_re, line) 37 if match: 38 symbol = match.group("symbol") 39 assert ( 40 symbol.count(" ") == 0 41 ), "Regex matched too much, probably got undecorated name as well" 42 # Deleting destructors start with ?_G or ?_E and can be discarded 43 # because link.exe gives you a warning telling you they can't be 44 # exported if you don't. 45 if symbol.startswith("??_G") or symbol.startswith("??_E"): 46 continue 47 lldb_symbols.add(symbol) 48 49 return lldb_symbols 50 51 52def main(): 53 parser = argparse.ArgumentParser(description="Generate LLDB dll exports") 54 parser.add_argument( 55 "-o", metavar="file", type=str, help="The name of the resultant export file." 56 ) 57 parser.add_argument("--nm", help="Path to the llvm-nm executable.") 58 parser.add_argument( 59 "libs", 60 metavar="lib", 61 type=str, 62 nargs="+", 63 help="The libraries to extract symbols from.", 64 ) 65 args = parser.parse_args() 66 67 # Get the list of libraries to extract symbols from 68 libs = list() 69 for lib in args.libs: 70 # When invoked by cmake the arguments are the cmake target names of the 71 # libraries, so we need to add .lib/.a to the end and maybe lib to the 72 # start to get the filename. Also allow objects. 73 suffixes = [".lib", ".a", ".obj", ".o"] 74 if not any([lib.endswith(s) for s in suffixes]): 75 for suffix in suffixes: 76 if os.path.exists(lib + suffix): 77 lib = lib + suffix 78 break 79 if os.path.exists("lib" + lib + suffix): 80 lib = "lib" + lib + suffix 81 break 82 if not any([lib.endswith(s) for s in suffixes]): 83 print( 84 "Unknown extension type for library argument: " + lib, file=sys.stderr 85 ) 86 exit(1) 87 libs.append(lib) 88 89 # Extract symbols from the input libraries. 90 symbols = set() 91 for lib in libs: 92 for sym in list(extract_symbols(args.nm, lib)): 93 symbols.add(sym) 94 95 # Write out the symbols to the output file. 96 with open(args.o, "w", newline="") as f: 97 for s in sorted(symbols): 98 f.write(f"{s}\n") 99 100 101if __name__ == "__main__": 102 main() 103