Skip to content

Commit a514c30

Browse files
authored
[lldb] Add windows support for LLDB_EXPORT_ALL_SYMBOLS (#67628)
LLDB_EXPORT_ALL_SYMBOLS is useful when building out-of-tree plugins and extensions that rely on LLDB's internal symbols. For example, this is how the Mojo language provides its REPL and debugger support. Supporting this on windows is kind of tricky because this is normally expected to be done using dllexport/dllimport, but lldb uses these with the public api. This PR takes an approach similar to what LLVM does with LLVM_EXPORT_SYMBOLS_FOR_PLUGINS, and what chromium does for [abseil](https://github.com/chromium/chromium/blob/253d14e20fdc0cab05e5516770dceca18f9bddaf/third_party/abseil-cpp/generate_def_files.py), and uses a python script to extract the necessary symbols by looking at the symbol table for the various lldb libraries.
1 parent 3661a48 commit a514c30

File tree

3 files changed

+141
-8
lines changed

3 files changed

+141
-8
lines changed

lldb/cmake/modules/LLDBConfig.cmake

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -122,14 +122,8 @@ if(APPLE AND CMAKE_GENERATOR STREQUAL Xcode)
122122
endif()
123123
endif()
124124

125-
if (NOT CMAKE_SYSTEM_NAME MATCHES "Windows")
126-
set(LLDB_EXPORT_ALL_SYMBOLS 0 CACHE BOOL
127-
"Causes lldb to export all symbols when building liblldb.")
128-
else()
129-
# Windows doesn't support toggling this, so don't bother making it a
130-
# cache variable.
131-
set(LLDB_EXPORT_ALL_SYMBOLS 0)
132-
endif()
125+
set(LLDB_EXPORT_ALL_SYMBOLS 0 CACHE BOOL
126+
"Causes lldb to export all symbols when building liblldb.")
133127

134128
if ((NOT MSVC) OR MSVC12)
135129
add_definitions( -DHAVE_ROUND )
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
"""A tool for extracting a list of private lldb symbols to export for MSVC.
2+
3+
When exporting symbols from a dll or exe we either need to mark the symbols in
4+
the source code as __declspec(dllexport) or supply a list of symbols to the
5+
linker. Private symbols in LLDB don't explicitly specific dllexport, so we
6+
automate that by examining the symbol table.
7+
"""
8+
9+
import argparse
10+
import os
11+
import re
12+
import subprocess
13+
import sys
14+
15+
16+
def 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* [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+
52+
def 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+
101+
if __name__ == "__main__":
102+
main()

lldb/source/API/CMakeLists.txt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,43 @@ if (NOT CMAKE_SYSTEM_NAME MATCHES "Windows")
184184
add_llvm_symbol_exports(liblldb ${CMAKE_CURRENT_SOURCE_DIR}/liblldb-private.exports)
185185
endif()
186186
set_target_properties(liblldb_exports PROPERTIES FOLDER "lldb misc")
187+
elseif (LLDB_EXPORT_ALL_SYMBOLS)
188+
MESSAGE("-- Symbols (liblldb): exporting all symbols from the lldb and lldb_private namespaces")
189+
190+
# Pull out the various lldb libraries linked into liblldb, these will be used
191+
# when looking for symbols to extract. We ignore plugin libraries here,
192+
# because these symbols aren't publicly exposed.
193+
get_target_property(all_liblldb_libs liblldb LINK_LIBRARIES)
194+
set(lldb_libs "")
195+
foreach(lib ${all_liblldb_libs})
196+
if(TARGET ${lib} AND ${lib} MATCHES "^lldb" AND
197+
NOT ${lib} MATCHES "^lldbPlugin")
198+
get_target_property(lib_type ${lib} TYPE)
199+
if("${lib_type}" STREQUAL "STATIC_LIBRARY")
200+
list(APPEND lldb_libs ${lib})
201+
endif()
202+
endif()
203+
endforeach(lib)
204+
list(REMOVE_DUPLICATES lldb_libs)
205+
206+
# Extract all of the private symbols and produce a single file we can use for
207+
# the exports.
208+
set(exported_symbol_file ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/liblldb_private.symbols)
209+
get_host_tool_path(llvm-nm LLVM_NM llvm_nm_exe llvm_nm_target)
210+
add_custom_command(
211+
OUTPUT ${exported_symbol_file}
212+
COMMAND "${Python3_EXECUTABLE}"
213+
${LLDB_SOURCE_DIR}/scripts/msvc_extract_private_symbols.py
214+
${lldb_libs} -o ${exported_symbol_file} --nm=${llvm_nm_exe}
215+
WORKING_DIRECTORY ${LLVM_LIBRARY_OUTPUT_INTDIR}
216+
DEPENDS ${LLDB_SOURCE_DIR}/scripts/msvc_extract_private_symbols.py
217+
${lldb_libs} ${llvm_nm_target}
218+
VERBATIM
219+
COMMENT "Generating liblldb private export list"
220+
)
221+
222+
add_llvm_symbol_exports(liblldb ${exported_symbol_file})
223+
set_target_properties(liblldb_exports PROPERTIES FOLDER "lldb misc")
187224
endif()
188225

189226
if (NOT MSVC)

0 commit comments

Comments
 (0)