diff options
author | scottmg@chromium.org <scottmg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-20 19:21:41 +0000 |
---|---|---|
committer | scottmg@chromium.org <scottmg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-20 19:21:41 +0000 |
commit | b03477f172bb933acc6ab05af452e2999431d817 (patch) | |
tree | 3fa8c1cefd16217ac2114d5086c0b923137e64df /tools/win | |
parent | 762190afe1ba4257d3f5b39fd24b4e43f5ba9f3e (diff) | |
download | chromium_src-b03477f172bb933acc6ab05af452e2999431d817.zip chromium_src-b03477f172bb933acc6ab05af452e2999431d817.tar.gz chromium_src-b03477f172bb933acc6ab05af452e2999431d817.tar.bz2 |
Identify data exports during split_link
Generates proper .def files that include "DATA" so that non-functions are
correctly exported.
However, for split_link because the "other" side won't import them properly
(__declspec(dllimport) is required) split_link notes them, and errors out.
This also adds support for linking individual objs out of libs so that some
_constants.cc and _switches.cc files can easily be linked into both DLLs.
R=cpu@chromium.org
BUG=237249
Review URL: https://codereview.chromium.org/15310002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@201119 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools/win')
-rw-r--r-- | tools/win/split_link/split_link.py | 112 |
1 files changed, 110 insertions, 2 deletions
diff --git a/tools/win/split_link/split_link.py b/tools/win/split_link/split_link.py index 2b035c5..72b6c47 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) |