summaryrefslogtreecommitdiffstats
path: root/tools/win
diff options
context:
space:
mode:
authorscottmg@chromium.org <scottmg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-20 19:21:41 +0000
committerscottmg@chromium.org <scottmg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-20 19:21:41 +0000
commitb03477f172bb933acc6ab05af452e2999431d817 (patch)
tree3fa8c1cefd16217ac2114d5086c0b923137e64df /tools/win
parent762190afe1ba4257d3f5b39fd24b4e43f5ba9f3e (diff)
downloadchromium_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.py112
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)