llvm.org GIT mirror llvm / f653fb8
Add auto-exporting of symbols from tools so that plugins work on Windows The problem with plugins on Windows is that when building a plugin DLL it needs to explicitly link against something (an exe or DLL) if it uses symbols from that thing, and that thing must explicitly export those symbols. Also there's a limit of 65535 symbols that can be exported. This means that currently plugins only work on Windows when using BUILD_SHARED_LIBS, and that doesn't work with MSVC. This patch adds an LLVM_EXPORT_SYMBOLS_FOR_PLUGINS option, which when enabled automatically exports from all LLVM tools the symbols that a plugin could want to use so that a plugin can link against a tool directly. Plugins can specify what tool they link against by using PLUGIN_TOOL argument to llvm_add_library. The option can also be enabled on Linux, though there all it should do is restrict the set of symbols that are exported as by default all symbols are exported. This option is currently OFF by default, as while I've verified that it works with MSVC, linux gcc, and cygwin gcc, I haven't tried mingw gcc and I have no idea what will happen on OSX. Also unfortunately we can't turn on LLVM_ENABLE_PLUGINS when the option is ON as bugpoint-passes needs to be loaded by both bugpoint.exe and opt.exe which is incompatible with this approach. Also currently clang plugins don't work with this approach, which will be fixed in future patches. Differential Revision: http://reviews.llvm.org/D18826 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@270839 91177308-0d34-0410-b5e6-96231b3b80d8 John Brawn 4 years ago
5 changed file(s) with 577 addition(s) and 13 deletion(s). Raw diff Collapse all Expand all
327327 # May specify header files for IDE generators.
328328 # SONAME
329329 # Should set SONAME link flags and create symlinks
330 # PLUGIN_TOOL
331 # The tool (i.e. cmake target) that this plugin will link against
330332 # )
331333 function(llvm_add_library name)
332334 cmake_parse_arguments(ARG
333335 "MODULE;SHARED;STATIC;OBJECT;DISABLE_LLVM_LINK_LLVM_DYLIB;SONAME"
334 "OUTPUT_NAME"
336 "OUTPUT_NAME;PLUGIN_TOOL"
335337 "ADDITIONAL_HEADERS;DEPENDS;LINK_COMPONENTS;LINK_LIBS;OBJLIBS"
336338 ${ARGN})
337339 list(APPEND LLVM_COMMON_DEPENDS ${ARG_DEPENDS})
349351 if(ARG_SHARED OR ARG_STATIC)
350352 message(WARNING "MODULE with SHARED|STATIC doesn't make sense.")
351353 endif()
352 if(NOT LLVM_ENABLE_PLUGINS)
354 # Plugins that link against a tool are allowed even when plugins in general are not
355 if(NOT LLVM_ENABLE_PLUGINS AND NOT (ARG_PLUGIN_TOOL AND LLVM_EXPORT_SYMBOLS_FOR_PLUGINS))
353356 message(STATUS "${name} ignored -- Loadable modules not supported on this platform.")
354357 return()
355358 endif()
356359 else()
360 if(ARG_PLUGIN_TOOL)
361 message(WARNING "PLUGIN_TOOL without MODULE doesn't make sense.")
362 endif()
357363 if(BUILD_SHARED_LIBS AND NOT ARG_STATIC)
358364 set(ARG_SHARED TRUE)
359365 endif()
467473 endif()
468474 endif()
469475
470 if (DEFINED LLVM_LINK_COMPONENTS OR DEFINED ARG_LINK_COMPONENTS)
476 if(ARG_MODULE AND LLVM_EXPORT_SYMBOLS_FOR_PLUGINS AND ARG_PLUGIN_TOOL AND (WIN32 OR CYGWIN))
477 # On DLL platforms symbols are imported from the tool by linking against it.
478 set(llvm_libs ${ARG_PLUGIN_TOOL})
479 elseif (DEFINED LLVM_LINK_COMPONENTS OR DEFINED ARG_LINK_COMPONENTS)
471480 if (LLVM_LINK_LLVM_DYLIB AND NOT ARG_DISABLE_LLVM_LINK_LLVM_DYLIB)
472481 set(llvm_libs LLVM)
473482 else()
672681 endmacro(add_llvm_executable name)
673682
674683 function(export_executable_symbols target)
675 if (NOT MSVC) # MSVC's linker doesn't support exporting all symbols.
684 if (LLVM_EXPORTED_SYMBOL_FILE)
685 # The symbol file should contain the symbols we want the executable to
686 # export
687 set_target_properties(${target} PROPERTIES ENABLE_EXPORTS 1)
688 elseif (LLVM_EXPORT_SYMBOLS_FOR_PLUGINS)
689 # Extract the symbols to export from the static libraries that the
690 # executable links against.
691 set_target_properties(${target} PROPERTIES ENABLE_EXPORTS 1)
692 set(exported_symbol_file ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${target}.symbols)
693 # We need to consider not just the direct link dependencies, but also the
694 # transitive link dependencies. Do this by starting with the set of direct
695 # dependencies, then the dependencies of those dependencies, and so on.
696 get_target_property(new_libs ${target} LINK_LIBRARIES)
697 set(link_libs ${new_libs})
698 while(NOT "${new_libs}" STREQUAL "")
699 foreach(lib ${new_libs})
700 get_target_property(lib_type ${lib} TYPE)
701 if("${lib_type}" STREQUAL "STATIC_LIBRARY")
702 list(APPEND static_libs ${lib})
703 else()
704 list(APPEND other_libs ${lib})
705 endif()
706 get_target_property(transitive_libs ${lib} INTERFACE_LINK_LIBRARIES)
707 foreach(transitive_lib ${transitive_libs})
708 list(FIND link_libs ${transitive_lib} idx)
709 if(TARGET ${transitive_lib} AND idx EQUAL -1)
710 list(APPEND newer_libs ${transitive_lib})
711 list(APPEND link_libs ${transitive_lib})
712 endif()
713 endforeach(transitive_lib)
714 endforeach(lib)
715 set(new_libs ${newer_libs})
716 set(newer_libs "")
717 endwhile()
718 if (MSVC)
719 set(mangling microsoft)
720 else()
721 set(mangling itanium)
722 endif()
723 add_custom_command(OUTPUT ${exported_symbol_file}
724 COMMAND ${PYTHON_EXECUTABLE} ${LLVM_MAIN_SRC_DIR}/utils/extract_symbols.py --mangling=${mangling} ${static_libs} -o ${exported_symbol_file}
725 WORKING_DIRECTORY ${LLVM_LIBRARY_OUTPUT_INTDIR}
726 DEPENDS ${LLVM_MAIN_SRC_DIR}/utils/extract_symbols.py ${static_libs}
727 VERBATIM
728 COMMENT "Generating export list for ${target}")
729 add_llvm_symbol_exports( ${target} ${exported_symbol_file} )
730 # If something links against this executable then we want a
731 # transitive link against only the libraries whose symbols
732 # we aren't exporting.
733 set_target_properties(${target} PROPERTIES INTERFACE_LINK_LIBRARIES "${other_libs}")
734 # The default import library suffix that cmake uses for cygwin/mingw is
735 # ".dll.a", but for clang.exe that causes a collision with libclang.dll,
736 # where the import libraries of both get named libclang.dll.a. Use a suffix
737 # of ".exe.a" to avoid this.
738 if(CYGWIN OR MINGW)
739 set_target_properties(${target} PROPERTIES IMPORT_SUFFIX ".exe.a")
740 endif()
741 elseif(NOT (WIN32 OR CYGWIN))
742 # On Windows auto-exporting everything doesn't work because of the limit on
743 # the size of the exported symbol table, but on other platforms we can do
744 # it without any trouble.
676745 set_target_properties(${target} PROPERTIES ENABLE_EXPORTS 1)
677746 if (APPLE)
678747 set_property(TARGET ${target} APPEND_STRING PROPERTY
593593 endif()
594594 endif()
595595
596 if(CYGWIN OR MINGW)
597 # Prune --out-implib from executables. It doesn't make sense even
598 # with --export-all-symbols.
599 string(REGEX REPLACE "-Wl,--out-implib,[^ ]+ " " "
600 CMAKE_C_LINK_EXECUTABLE "${CMAKE_C_LINK_EXECUTABLE}")
601 string(REGEX REPLACE "-Wl,--out-implib,[^ ]+ " " "
602 CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE}")
603 endif()
604
605596 if(MSVC)
606597 # Remove flags here, for exceptions and RTTI.
607598 # Each target property or source property should be responsible to control
639630 CMAKE_EXE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS)
640631 endif()
641632
633 # This option makes utils/extract_symbols.py be used to determine the list of
634 # symbols to export from LLVM tools. This is necessary when using MSVC if you
635 # want to allow plugins, though note that the plugin has to explicitly link
636 # against (exactly one) tool so we can't unilaterally turn on
637 # LLVM_ENABLE_PLUGINS when it's enabled.
638 option(LLVM_EXPORT_SYMBOLS_FOR_PLUGINS "Export symbols from LLVM tools so that plugins can import them" OFF)
639 if(BUILD_SHARED_LIBS AND LLVM_EXPORT_SYMBOLS_FOR_PLUGINS)
640 message(FATAL_ERROR "BUILD_SHARED_LIBS not compatible with LLVM_EXPORT_SYMBOLS_FOR_PLUGINS")
641 endif()
642 if(LLVM_LINK_LLVM_DYLIB AND LLVM_EXPORT_SYMBOLS_FOR_PLUGINS)
643 message(FATAL_ERROR "LLVM_LINK_LLVM_DYLIB not compatible with LLVM_EXPORT_SYMBOLS_FOR_PLUGINS")
644 endif()
645
642646 # Plugin support
643647 # FIXME: Make this configurable.
644648 if(WIN32 OR CYGWIN)
3939 set(LLVM_ENABLE_PIC @LLVM_ENABLE_PIC@)
4040
4141 set(LLVM_ENABLE_PLUGINS @LLVM_ENABLE_PLUGINS@)
42 set(LLVM_EXPORT_SYMBOLS_FOR_PLUGINS @LLVM_EXPORT_SYMBOLS_FOR_PLUGINS@)
4243 set(LLVM_PLUGIN_EXT @LLVM_PLUGIN_EXT@)
4344
4445 set(LLVM_ON_UNIX @LLVM_ON_UNIX@)
1414
1515 DEPENDS
1616 intrinsics_gen
17 PLUGIN_TOOL
18 opt
1719 )
0 #!/usr/bin/env python
1
2 """A tool for extracting a list of symbols to export
3
4 When exporting symbols from a dll or exe we either need to mark the symbols in
5 the source code as __declspec(dllexport) or supply a list of symbols to the
6 linker. This program automates the latter by inspecting the symbol tables of a
7 list of link inputs and deciding which of those symbols need to be exported.
8
9 We can't just export all the defined symbols, as there's a limit of 65535
10 exported symbols and in clang we go way over that, particularly in a debug
11 build. Therefore a large part of the work is pruning symbols either which can't
12 be imported, or which we think are things that have definitions in public header
13 files (i.e. template instantiations) and we would get defined in the thing
14 importing these symbols anyway.
15 """
16
17 import sys
18 import re
19 import os
20 import subprocess
21 import multiprocessing
22 import argparse
23
24 # Define functions which extract a list of symbols from a library using several
25 # different tools. We use subprocess.Popen and yield a symbol at a time instead
26 # of using subprocess.check_output and returning a list as, especially on
27 # Windows, waiting for the entire output to be ready can take a significant
28 # amount of time.
29
30 def dumpbin_get_symbols(lib):
31 process = subprocess.Popen(['dumpbin','/symbols',lib], bufsize=1,
32 stdout=subprocess.PIPE, stdin=subprocess.PIPE)
33 process.stdin.close()
34 for line in process.stdout:
35 # Look for external symbols that are defined in some section
36 match = re.match("^.+SECT.+External\s+\|\s+(\S+).*$", line)
37 if match:
38 yield match.group(1)
39 process.wait()
40
41 def nm_get_symbols(lib):
42 process = subprocess.Popen(['nm',lib], bufsize=1,
43 stdout=subprocess.PIPE, stdin=subprocess.PIPE)
44 process.stdin.close()
45 for line in process.stdout:
46 # Look for external symbols that are defined in some section
47 match = re.match("^\S+\s+[BDGRSTVW]\s+(\S+)$", line)
48 if match:
49 yield match.group(1)
50 process.wait()
51
52 def readobj_get_symbols(lib):
53 process = subprocess.Popen(['llvm-readobj','-symbols',lib], bufsize=1,
54 stdout=subprocess.PIPE, stdin=subprocess.PIPE)
55 process.stdin.close()
56 for line in process.stdout:
57 # When looking through the output of llvm-readobj we expect to see Name,
58 # Section, then StorageClass, so record Name and Section when we see
59 # them and decide if this is a defined external symbol when we see
60 # StorageClass.
61 match = re.search('Name: (\S+)', line)
62 if match:
63 name = match.group(1)
64 match = re.search('Section: (\S+)', line)
65 if match:
66 section = match.group(1)
67 match = re.search('StorageClass: (\S+)', line)
68 if match:
69 storageclass = match.group(1)
70 if section != 'IMAGE_SYM_ABSOLUTE' and \
71 section != 'IMAGE_SYM_UNDEFINED' and \
72 storageclass == 'External':
73 yield name
74 process.wait()
75
76 # Define functions which determine if the target is 32-bit Windows (as that's
77 # where calling convention name decoration happens).
78
79 def dumpbin_is_32bit_windows(lib):
80 # dumpbin /headers can output a huge amount of data (>100MB in a debug
81 # build) so we read only up to the 'machine' line then close the output.
82 process = subprocess.Popen(['dumpbin','/headers',lib], bufsize=1,
83 stdout=subprocess.PIPE, stdin=subprocess.PIPE)
84 process.stdin.close()
85 retval = False
86 for line in process.stdout:
87 match = re.match('.+machine \((\S+)\)', line)
88 if match:
89 retval = (match.group(1) == 'x86')
90 break
91 process.stdout.close()
92 process.wait()
93 return retval
94
95 def objdump_is_32bit_windows(lib):
96 output = subprocess.check_output(['objdump','-f',lib])
97 for line in output:
98 match = re.match('.+file format (\S+)', line)
99 if match:
100 return (match.group(1) == 'pe-i386')
101 return False
102
103 def readobj_is_32bit_windows(lib):
104 output = subprocess.check_output(['llvm-readobj','-file-headers',lib])
105 for line in output:
106 match = re.match('Format: (\S+)', line)
107 if match:
108 return (match.group(1) == 'COFF-i386')
109 return False
110
111 # MSVC mangles names to ?@. By examining the
112 # identifier/type mangling we can decide which symbols could possibly be
113 # required and which we can discard.
114 def should_keep_microsoft_symbol(symbol, calling_convention_decoration):
115 # Keep unmangled (i.e. extern "C") names
116 if not '?' in symbol:
117 if calling_convention_decoration:
118 # Remove calling convention decoration from names
119 match = re.match('[_@]([^@]+)', symbol)
120 if match:
121 return match.group(1)
122 return symbol
123 # Function template instantiations start with ?$, discard them as it's
124 # assumed that the definition is public
125 elif symbol.startswith('??$'):
126 return None
127 # Deleting destructors start with ?_G or ?_E and can be discarded because
128 # link.exe gives you a warning telling you they can't be exported if you
129 # don't
130 elif symbol.startswith('??_G') or symbol.startswith('??_E'):
131 return None
132 # Constructors (?0) and destructors (?1) of templates (?$) are assumed to be
133 # defined in headers and not required to be kept
134 elif symbol.startswith('??0?$') or symbol.startswith('??1?$'):
135 return None
136 # An anonymous namespace is mangled as ?A(maybe hex number)@. Any symbol
137 # that mentions an anonymous namespace can be discarded, as the anonymous
138 # namespace doesn't exist outside of that translation unit.
139 elif re.search('\?A(0x\w+)?@', symbol):
140 return None
141 # Keep mangled llvm:: and clang:: function symbols. How we detect these is a
142 # bit of a mess and imprecise, but that avoids having to completely demangle
143 # the symbol name. The outermost namespace is at the end of the identifier
144 # mangling, and the identifier mangling is followed by the type mangling, so
145 # we look for (llvm|clang)@@ followed by something that looks like a
146 # function type mangling. To spot a function type we use (this is derived
147 # from clang/lib/AST/MicrosoftMangle.cpp):
148 # ::=
149 #
150 #
151 # ::= [A-Z]
152 # ::= [A-Z0-9_]*
153 # ::= [A-JQ]
154 # ::= .+
155 # ::= X (void)
156 # ::= .+@ (list of types)
157 # ::= .*Z (list of types, varargs)
158 # ::= exceptions are not allowed
159 elif re.search('(llvm|clang)@@[A-Z][A-Z0-9_]*[A-JQ].+(X|.+@|.*Z)$', symbol):
160 return symbol
161 return None
162
163 # Itanium manglings are of the form _Z. We
164 # demangle the identifier mangling to identify symbols that can be safely
165 # discarded.
166 def should_keep_itanium_symbol(symbol, calling_convention_decoration):
167 # Start by removing any calling convention decoration (which we expect to
168 # see on all symbols, even mangled C++ symbols)
169 if calling_convention_decoration and symbol.startswith('_'):
170 symbol = symbol[1:]
171 # Keep unmangled names
172 if not symbol.startswith('_') and not symbol.startswith('.'):
173 return symbol
174 # Discard manglings that aren't nested names
175 match = re.match('_Z(T[VTIS])?(N.+)', symbol)
176 if not match:
177 return None
178 # Demangle the name. If the name is too complex then we don't need to keep
179 # it, but it the demangling fails then keep the symbol just in case.
180 try:
181 names, _ = parse_itanium_nested_name(match.group(2))
182 except TooComplexName:
183 return None
184 if not names:
185 return symbol
186 # Constructors and destructors of templates classes are assumed to be
187 # defined in headers and not required to be kept
188 if re.match('[CD][123]', names[-1][0]) and names[-2][1]:
189 return None
190 # Discard function template instantiations as it's assumed that the
191 # definition is public
192 elif names[-1][1]:
193 return None
194 # Keep llvm:: and clang:: names
195 elif names[0][0] == '4llvm' or names[0][0] == '5clang':
196 return symbol
197 # Discard everything else
198 else:
199 return None
200
201 # Certain kinds of complex manglings we assume cannot be part of a public
202 # interface, and we handle them by raising an exception.
203 class TooComplexName(Exception):
204 pass
205
206 # Parse an itanium mangled name from the start of a string and return a
207 # (name, rest of string) pair.
208 def parse_itanium_name(arg):
209 # Check for a normal name
210 match = re.match('(\d+)(.+)', arg)
211 if match:
212 n = int(match.group(1))
213 name = match.group(1)+match.group(2)[:n]
214 rest = match.group(2)[n:]
215 return name, rest
216 # Check for constructor/destructor names
217 match = re.match('([CD][123])(.+)', arg)
218 if match:
219 return match.group(1), match.group(2)
220 # Assume that a sequence of characters that doesn't end a nesting is an
221 # operator (this is very imprecise, but appears to be good enough)
222 match = re.match('([^E]+)(.+)', arg)
223 if match:
224 return match.group(1), match.group(2)
225 # Anything else: we can't handle it
226 return None, arg
227
228 # Parse an itanium mangled template argument list from the start of a string
229 # and throw it away, returning the rest of the string.
230 def skip_itanium_template(arg):
231 # A template argument list starts with I
232 assert arg.startswith('I'), arg
233 tmp = arg[1:]
234 while tmp:
235 # Check for names
236 match = re.match('(\d+)(.+)', tmp)
237 if match:
238 n = int(match.group(1))
239 tmp = match.group(2)[n:]
240 continue
241 # Check for substitutions
242 match = re.match('S[A-Z0-9]*_(.+)', tmp)
243 if match:
244 tmp = match.group(1)
245 # Start of a template
246 elif tmp.startswith('I'):
247 tmp = skip_itanium_template(tmp)
248 # Start of a nested name
249 elif tmp.startswith('N'):
250 _, tmp = parse_itanium_nested_name(tmp)
251 # Start of an expression: assume that it's too complicated
252 elif tmp.startswith('L') or tmp.startswith('X'):
253 raise TooComplexName
254 # End of the template
255 elif tmp.startswith('E'):
256 return tmp[1:]
257 # Something else: probably a type, skip it
258 else:
259 tmp = tmp[1:]
260 return None
261
262 # Parse an itanium mangled nested name and transform it into a list of pairs of
263 # (name, is_template), returning (list, rest of string).
264 def parse_itanium_nested_name(arg):
265 # A nested name starts with N
266 assert arg.startswith('N'), arg
267 ret = []
268
269 # Skip past the N, and possibly a substitution
270 match = re.match('NS[A-Z0-9]*_(.+)', arg)
271 if match:
272 tmp = match.group(1)
273 else:
274 tmp = arg[1:]
275
276 # Skip past CV-qualifiers and ref qualifiers
277 match = re.match('[rVKRO]*(.+)', tmp);
278 if match:
279 tmp = match.group(1)
280
281 # Repeatedly parse names from the string until we reach the end of the
282 # nested name
283 while tmp:
284 # An E ends the nested name
285 if tmp.startswith('E'):
286 return ret, tmp[1:]
287 # Parse a name
288 name_part, tmp = parse_itanium_name(tmp)
289 if not name_part:
290 # If we failed then we don't know how to demangle this
291 return None, None
292 is_template = False
293 # If this name is a template record that, then skip the template
294 # arguments
295 if tmp.startswith('I'):
296 tmp = skip_itanium_template(tmp)
297 is_template = True
298 # Add the name to the list
299 ret.append((name_part, is_template))
300
301 # If we get here then something went wrong
302 return None, None
303
304 def extract_symbols(arg):
305 get_symbols, should_keep_symbol, calling_convention_decoration, lib = arg
306 symbols = dict()
307 for symbol in get_symbols(lib):
308 symbol = should_keep_symbol(symbol, calling_convention_decoration)
309 if symbol:
310 symbols[symbol] = 1 + symbols.setdefault(symbol,0)
311 return symbols
312
313 if __name__ == '__main__':
314 tool_exes = ['dumpbin','nm','objdump','llvm-readobj']
315 parser = argparse.ArgumentParser(
316 description='Extract symbols to export from libraries')
317 parser.add_argument('--mangling', choices=['itanium','microsoft'],
318 required=True, help='expected symbol mangling scheme')
319 parser.add_argument('--tools', choices=tool_exes, nargs='*',
320 help='tools to use to extract symbols and determine the'
321 ' target')
322 parser.add_argument('libs', metavar='lib', type=str, nargs='+',
323 help='libraries to extract symbols from')
324 parser.add_argument('-o', metavar='file', type=str, help='output to file')
325 args = parser.parse_args()
326
327 # Determine the function to use to get the list of symbols from the inputs,
328 # and the function to use to determine if the target is 32-bit windows.
329 tools = { 'dumpbin' : (dumpbin_get_symbols, dumpbin_is_32bit_windows),
330 'nm' : (nm_get_symbols, None),
331 'objdump' : (None, objdump_is_32bit_windows),
332 'llvm-readobj' : (readobj_get_symbols, readobj_is_32bit_windows) }
333 get_symbols = None
334 is_32bit_windows = None
335 # If we have a tools argument then use that for the list of tools to check
336 if args.tools:
337 tool_exes = args.tools
338 # Find a tool to use by trying each in turn until we find one that exists
339 # (subprocess.call will throw OSError when the program does not exist)
340 get_symbols = None
341 for exe in tool_exes:
342 try:
343 # Close std streams as we don't want any output and we don't
344 # want the process to wait for something on stdin.
345 p = subprocess.Popen([exe], stdout=subprocess.PIPE,
346 stderr=subprocess.PIPE,
347 stdin=subprocess.PIPE)
348 p.stdout.close()
349 p.stderr.close()
350 p.stdin.close()
351 p.wait()
352 # Keep going until we have a tool to use for both get_symbols and
353 # is_32bit_windows
354 if not get_symbols:
355 get_symbols = tools[exe][0]
356 if not is_32bit_windows:
357 is_32bit_windows = tools[exe][1]
358 if get_symbols and is_32bit_windows:
359 break
360 except OSError:
361 continue
362 if not get_symbols:
363 print >>sys.stderr, "Couldn't find a program to read symbols with"
364 exit(1)
365 if not is_32bit_windows:
366 print >>sys.stderr, "Couldn't find a program to determing the target"
367 exit(1)
368
369 # How we determine which symbols to keep and which to discard depends on
370 # the mangling scheme
371 if args.mangling == 'microsoft':
372 should_keep_symbol = should_keep_microsoft_symbol
373 else:
374 should_keep_symbol = should_keep_itanium_symbol
375
376 # Get the list of libraries to extract symbols from
377 libs = list()
378 for lib in args.libs:
379 # When invoked by cmake the arguments are the cmake target names of the
380 # libraries, so we need to add .lib/.a to the end and maybe lib to the
381 # start to get the filename. Also allow objects.
382 suffixes = ['.lib','.a','.obj','.o']
383 if not any([lib.endswith(s) for s in suffixes]):
384 for s in suffixes:
385 if os.path.exists(lib+s):
386 lib = lib+s
387 break
388 if os.path.exists('lib'+lib+s):
389 lib = 'lib'+lib+s
390 break
391 if not any([lib.endswith(s) for s in suffixes]):
392 print >>sys.stderr, "Don't know what to do with argument "+lib
393 exit(1)
394 libs.append(lib)
395
396 # Check if calling convention decoration is used by inspecting the first
397 # library in the list
398 calling_convention_decoration = is_32bit_windows(libs[0])
399
400 # Extract symbols from libraries in parallel. This is a huge time saver when
401 # doing a debug build, as there are hundreds of thousands of symbols in each
402 # library.
403 pool = multiprocessing.Pool()
404 try:
405 # Only one argument can be passed to the mapping function, and we can't
406 # use a lambda or local function definition as that doesn't work on
407 # windows, so create a list of tuples which duplicates the arguments
408 # that are the same in all calls.
409 vals = [(get_symbols, should_keep_symbol, calling_convention_decoration, x) for x in libs]
410 # Do an async map then wait for the result to make sure that
411 # KeyboardInterrupt gets caught correctly (see
412 # http://bugs.python.org/issue8296)
413 result = pool.map_async(extract_symbols, vals)
414 pool.close()
415 libs_symbols = result.get(3600)
416 except KeyboardInterrupt:
417 # On Ctrl-C terminate everything and exit
418 pool.terminate()
419 pool.join()
420 exit(1)
421
422 # Merge everything into a single dict
423 symbols = dict()
424 for this_lib_symbols in libs_symbols:
425 for k,v in this_lib_symbols.items():
426 symbols[k] = v + symbols.setdefault(k,0)
427
428 # Count instances of member functions of template classes, and map the
429 # symbol name to the function+class. We do this under the assumption that if
430 # a member function of a template class is instantiated many times it's
431 # probably declared in a public header file.
432 template_function_count = dict()
433 template_function_mapping = dict()
434 template_function_count[""] = 0
435 for k in symbols:
436 name = None
437 if args.mangling == 'microsoft':
438 # Member functions of templates start with
439 # ?@?$@, so we map to @?$.
440 # As manglings go from the innermost scope to the outermost scope
441 # this means:
442 # * When we have a function member of a subclass of a template
443 # class then will actually contain the mangling of
444 # both the subclass and the function member. This is fine.
445 # * When we have a function member of a template subclass of a
446 # (possibly template) class then it's the innermost template
447 # subclass that becomes . This should be OK so long
448 # as we don't have multiple classes with a template subclass of
449 # the same name.
450 match = re.search("^\?(\??\w+\@\?\$\w+)\@", k)
451 if match:
452 name = match.group(1)
453 else:
454 # Find member functions of templates by demangling the name and
455 # checking if the second-to-last name in the list is a template.
456 match = re.match('_Z(T[VTIS])?(N.+)', k)
457 if match:
458 try:
459 names, _ = parse_itanium_nested_name(match.group(2))
460 if names and names[-2][1]:
461 name = ''.join([x for x,_ in names])
462 except TooComplexName:
463 # Manglings that are too complex should already have been
464 # filtered out, but if we happen to somehow see one here
465 # just leave it as-is.
466 pass
467 if name:
468 old_count = template_function_count.setdefault(name,0)
469 template_function_count[name] = old_count + 1
470 template_function_mapping[k] = name
471 else:
472 template_function_mapping[k] = ""
473
474 # Print symbols which both:
475 # * Appear in exactly one input, as symbols defined in multiple
476 # objects/libraries are assumed to have public definitions.
477 # * Aren't instances of member functions of templates which have been
478 # instantiated 100 times or more, which are assumed to have public
479 # definitions. (100 is an arbitrary guess here.)
480 if args.o:
481 outfile = open(args.o,'w')
482 else:
483 outfile = sys.stdout
484 for k,v in symbols.items():
485 template_count = template_function_count[template_function_mapping[k]]
486 if v == 1 and template_count < 100:
487 print >>outfile, k