| Index: tools/win/split_link/split_link.py
|
| diff --git a/tools/win/split_link/split_link.py b/tools/win/split_link/split_link.py
|
| index 2b035c5418b2a70389ee903fb30820d3a1c4d469..72b6c47290d77c88f004f0db687a4f28229b9e96 100644
|
| --- a/tools/win/split_link/split_link.py
|
| +++ b/tools/win/split_link/split_link.py
|
| @@ -13,6 +13,7 @@ import os
|
| import re
|
| import subprocess
|
| import sys
|
| +import tempfile
|
|
|
|
|
| BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
| @@ -179,6 +180,50 @@ def RunLinker(flags, index, inputs, phase):
|
| return stdout, popen.returncode, output_name
|
|
|
|
|
| +def GetLibObjList(lib):
|
| + """Gets the list of object files contained in a .lib."""
|
| + link_exe = GetOriginalLinkerPath()
|
| + popen = subprocess.Popen(
|
| + [link_exe, '/lib', '/nologo', '/list', lib], stdout=subprocess.PIPE)
|
| + stdout, _ = popen.communicate()
|
| + return stdout.splitlines()
|
| +
|
| +
|
| +def ExtractObjFromLib(lib, obj):
|
| + """Extracts a .obj file contained in a .lib file. Returns the absolute path
|
| + a temp file."""
|
| + link_exe = GetOriginalLinkerPath()
|
| + temp = tempfile.NamedTemporaryFile(
|
| + prefix='split_link_', suffix='.obj', delete=False)
|
| + temp.close()
|
| + subprocess.check_call([
|
| + link_exe, '/lib', '/nologo', '/extract:' + obj, lib, '/out:' + temp.name])
|
| + return temp.name
|
| +
|
| +
|
| +def Unmangle(export):
|
| + "Returns the human-presentable name of a mangled symbol."""
|
| + # Use dbghelp.dll to demangle the name.
|
| + # TODO(scottmg): Perhaps a simple cache? Seems pretty fast though.
|
| + UnDecorateSymbolName = ctypes.windll.dbghelp.UnDecorateSymbolName
|
| + buffer_size = 2048
|
| + output_string = ctypes.create_string_buffer(buffer_size)
|
| + if not UnDecorateSymbolName(
|
| + export, ctypes.byref(output_string), buffer_size, 0):
|
| + raise ctypes.WinError()
|
| + return output_string.value
|
| +
|
| +
|
| +def IsDataDefinition(export):
|
| + """Determines if a given name is data rather than a function. Always returns
|
| + False for C-style (as opposed to C++-style names)."""
|
| + if export[0] != '?':
|
| + return False
|
| +
|
| + # If it contains a '(' we assume it's a function.
|
| + return '(' not in Unmangle(export)
|
| +
|
| +
|
| def GenerateDefFiles(unresolved_by_part):
|
| """Given a list of unresolved externals, generates a .def file that will
|
| cause all those symbols to be exported."""
|
| @@ -192,7 +237,10 @@ def GenerateDefFiles(unresolved_by_part):
|
| for j, part in enumerate(unresolved_by_part):
|
| if i == j:
|
| continue
|
| - print >> f, '\n'.join(' ' + export for export in part)
|
| + is_data = [' DATA' if IsDataDefinition(export) else ''
|
| + for export in part]
|
| + print >> f, '\n'.join(' ' + export + data
|
| + for export, data in zip(part, is_data))
|
| deffiles.append(deffile)
|
| return deffiles
|
|
|
| @@ -238,6 +286,40 @@ def AttemptLink(flags, inputs_by_part, unresolved_by_part, deffiles,
|
| return all_succeeded, dlls, combined_externals
|
|
|
|
|
| +def ExtractSubObjsTargetedAtAll(
|
| + inputs,
|
| + num_parts,
|
| + description_parts,
|
| + description_all,
|
| + description_all_from_libs):
|
| + """For (lib, obj) tuples in the all_from_libs section, extract the obj out of
|
| + the lib and added it to inputs. Returns a list of lists for which part the
|
| + extracted obj belongs in (which is whichever the .lib isn't in)."""
|
| + by_parts = [[] for _ in range(num_parts)]
|
| + for lib_spec, obj_spec in description_all_from_libs:
|
| + for input_file in inputs:
|
| + if re.search(lib_spec, input_file):
|
| + objs = GetLibObjList(input_file)
|
| + match_count = 0
|
| + for obj in objs:
|
| + if re.search(obj_spec, obj, re.I):
|
| + extracted_obj = ExtractObjFromLib(input_file, obj)
|
| + #Log('extracted %s (%s %s)' % (extracted_obj, input_file, obj))
|
| + i = PartFor(input_file, description_parts, description_all)
|
| + if i == -1:
|
| + raise SystemExit(
|
| + '%s is already in all parts, but matched '
|
| + '%s in all_from_libs' % (input_file, obj))
|
| + # See note in main().
|
| + assert num_parts == 2, "Can't handle > 2 dlls currently"
|
| + by_parts[1 - i].append(obj)
|
| + match_count += 1
|
| + if match_count == 0:
|
| + raise SystemExit(
|
| + '%s, %s matched a lib, but no objs' % (lib_spec, obj_spec))
|
| + return by_parts
|
| +
|
| +
|
| def main():
|
| flags, inputs = GetFlagsAndInputs(sys.argv[1:])
|
| partition_file = os.path.normpath(
|
| @@ -246,13 +328,20 @@ def main():
|
| description = eval(partition.read())
|
| inputs_by_part = []
|
| description_parts = description['parts']
|
| - # We currently assume that if a symbols isn't in dll 0, then it's in dll 1
|
| + # We currently assume that if a symbol isn't in dll 0, then it's in dll 1
|
| # when generating def files. Otherwise, we'd need to do more complex things
|
| # to figure out where each symbol actually is to assign it to the correct
|
| # .def file.
|
| num_parts = len(description_parts)
|
| assert num_parts == 2, "Can't handle > 2 dlls currently"
|
| description_parts.reverse()
|
| + objs_from_libs = ExtractSubObjsTargetedAtAll(
|
| + inputs,
|
| + num_parts,
|
| + description_parts,
|
| + description['all'],
|
| + description['all_from_libs'])
|
| + objs_from_libs.reverse()
|
| inputs_by_part = [[] for _ in range(num_parts)]
|
| for input_file in inputs:
|
| i = PartFor(input_file, description_parts, description['all'])
|
| @@ -263,21 +352,40 @@ def main():
|
| inputs_by_part[i].append(input_file)
|
| inputs_by_part.reverse()
|
|
|
| + # Put the subobjs on to the main list.
|
| + for i, part in enumerate(objs_from_libs):
|
| + Log('%d sub .objs added to part %d' % (len(part), i))
|
| + inputs_by_part[i].extend(part)
|
| +
|
| unresolved_by_part = [[] for _ in range(num_parts)]
|
| import_libs = [None] * num_parts
|
| deffiles = [None] * num_parts
|
|
|
| + data_exports = 0
|
| for i in range(5):
|
| Log('--- starting pass %d' % i)
|
| ok, dlls, unresolved_by_part = AttemptLink(
|
| flags, inputs_by_part, unresolved_by_part, deffiles, import_libs)
|
| if ok:
|
| break
|
| + data_exports = 0
|
| + for i, part in enumerate(unresolved_by_part):
|
| + for export in part:
|
| + if IsDataDefinition(export):
|
| + print 'part %d contains data export: %s (aka %s)' % (
|
| + i, Unmangle(export), export)
|
| + data_exports += 1
|
| deffiles = GenerateDefFiles(unresolved_by_part)
|
| import_libs = BuildImportLibs(flags, inputs_by_part, deffiles)
|
| else:
|
| return 1
|
|
|
| + if data_exports:
|
| + print 'Data exports found, see report above.'
|
| + print('These cannot be exported, and must be either duplicated to the '
|
| + 'target DLL, or wrapped in a function.')
|
| + return 1
|
| +
|
| mt_exe = GetMtPath()
|
| for i, dll in enumerate(dlls):
|
| Log('embedding manifest in %s' % dll)
|
|
|