Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(5)

Side by Side Diff: tools/win/split_link/split_link.py

Issue 15310002: Identify data exports during split_link (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: . Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « build/split_link_partition.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright 2013 The Chromium Authors. All rights reserved. 1 # Copyright 2013 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """Takes the same arguments as Windows link.exe, and a definition of libraries 5 """Takes the same arguments as Windows link.exe, and a definition of libraries
6 to split into subcomponents. Does multiple passes of link.exe invocation to 6 to split into subcomponents. Does multiple passes of link.exe invocation to
7 determine exports between parts and generates .def and import libraries to 7 determine exports between parts and generates .def and import libraries to
8 cause symbols to be available to other parts.""" 8 cause symbols to be available to other parts."""
9 9
10 import _winreg 10 import _winreg
11 import ctypes 11 import ctypes
12 import os 12 import os
13 import re 13 import re
14 import subprocess 14 import subprocess
15 import sys 15 import sys
16 import tempfile
16 17
17 18
18 BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 19 BASE_DIR = os.path.dirname(os.path.abspath(__file__))
19 20
20 21
21 def Log(message): 22 def Log(message):
22 print 'split_link:', message 23 print 'split_link:', message
23 24
24 25
25 def GetFlagsAndInputs(argv): 26 def GetFlagsAndInputs(argv):
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after
172 print >> f, '/OUT:' + output_name 173 print >> f, '/OUT:' + output_name
173 print >> f, '/MANIFESTFILE:' + manifest_name 174 print >> f, '/MANIFESTFILE:' + manifest_name
174 print >> f, '/PDB:' + PdbNameForIndex(index) 175 print >> f, '/PDB:' + PdbNameForIndex(index)
175 # Log('[[[\n' + open(rspfile).read() + '\n]]]') 176 # Log('[[[\n' + open(rspfile).read() + '\n]]]')
176 link_exe = GetOriginalLinkerPath() 177 link_exe = GetOriginalLinkerPath()
177 popen = subprocess.Popen([link_exe, '@' + rspfile], stdout=subprocess.PIPE) 178 popen = subprocess.Popen([link_exe, '@' + rspfile], stdout=subprocess.PIPE)
178 stdout, _ = popen.communicate() 179 stdout, _ = popen.communicate()
179 return stdout, popen.returncode, output_name 180 return stdout, popen.returncode, output_name
180 181
181 182
183 def GetLibObjList(lib):
184 """Gets the list of object files contained in a .lib."""
185 link_exe = GetOriginalLinkerPath()
186 popen = subprocess.Popen(
187 [link_exe, '/lib', '/nologo', '/list', lib], stdout=subprocess.PIPE)
188 stdout, _ = popen.communicate()
189 return stdout.splitlines()
190
191
192 def ExtractObjFromLib(lib, obj):
193 """Extracts a .obj file contained in a .lib file. Returns the absolute path
194 a temp file."""
195 link_exe = GetOriginalLinkerPath()
196 temp = tempfile.NamedTemporaryFile(
197 prefix='split_link_', suffix='.obj', delete=False)
198 temp.close()
199 subprocess.check_call([
200 link_exe, '/lib', '/nologo', '/extract:' + obj, lib, '/out:' + temp.name])
201 return temp.name
202
203
204 def Unmangle(export):
205 "Returns the human-presentable name of a mangled symbol."""
206 # Use dbghelp.dll to demangle the name.
207 # TODO(scottmg): Perhaps a simple cache? Seems pretty fast though.
208 UnDecorateSymbolName = ctypes.windll.dbghelp.UnDecorateSymbolName
209 buffer_size = 2048
210 output_string = ctypes.create_string_buffer(buffer_size)
211 if not UnDecorateSymbolName(
212 export, ctypes.byref(output_string), buffer_size, 0):
213 raise ctypes.WinError()
214 return output_string.value
215
216
217 def IsDataDefinition(export):
218 """Determines if a given name is data rather than a function. Always returns
219 False for C-style (as opposed to C++-style names)."""
220 if export[0] != '?':
221 return False
222
223 # If it contains a '(' we assume it's a function.
224 return '(' not in Unmangle(export)
225
226
182 def GenerateDefFiles(unresolved_by_part): 227 def GenerateDefFiles(unresolved_by_part):
183 """Given a list of unresolved externals, generates a .def file that will 228 """Given a list of unresolved externals, generates a .def file that will
184 cause all those symbols to be exported.""" 229 cause all those symbols to be exported."""
185 deffiles = [] 230 deffiles = []
186 Log('generating .def files') 231 Log('generating .def files')
187 for i, part in enumerate(unresolved_by_part): 232 for i, part in enumerate(unresolved_by_part):
188 deffile = 'part%d.def' % i 233 deffile = 'part%d.def' % i
189 with open(deffile, 'w') as f: 234 with open(deffile, 'w') as f:
190 print >> f, 'LIBRARY %s' % OutputNameForIndex(i) 235 print >> f, 'LIBRARY %s' % OutputNameForIndex(i)
191 print >> f, 'EXPORTS' 236 print >> f, 'EXPORTS'
192 for j, part in enumerate(unresolved_by_part): 237 for j, part in enumerate(unresolved_by_part):
193 if i == j: 238 if i == j:
194 continue 239 continue
195 print >> f, '\n'.join(' ' + export for export in part) 240 is_data = [' DATA' if IsDataDefinition(export) else ''
241 for export in part]
242 print >> f, '\n'.join(' ' + export + data
243 for export, data in zip(part, is_data))
196 deffiles.append(deffile) 244 deffiles.append(deffile)
197 return deffiles 245 return deffiles
198 246
199 247
200 def BuildImportLibs(flags, inputs_by_part, deffiles): 248 def BuildImportLibs(flags, inputs_by_part, deffiles):
201 """Runs the linker to generate an import library.""" 249 """Runs the linker to generate an import library."""
202 import_libs = [] 250 import_libs = []
203 Log('building import libs') 251 Log('building import libs')
204 for i, (inputs, deffile) in enumerate(zip(inputs_by_part, deffiles)): 252 for i, (inputs, deffile) in enumerate(zip(inputs_by_part, deffiles)):
205 libfile = 'part%d.lib' % i 253 libfile = 'part%d.lib' % i
(...skipping 25 matching lines...) Expand all
231 all_succeeded = False 279 all_succeeded = False
232 new_externals.append(ParseOutExternals(stdout)) 280 new_externals.append(ParseOutExternals(stdout))
233 else: 281 else:
234 new_externals.append([]) 282 new_externals.append([])
235 dlls.append(output) 283 dlls.append(output)
236 combined_externals = [sorted(set(prev) | set(new)) 284 combined_externals = [sorted(set(prev) | set(new))
237 for prev, new in zip(unresolved_by_part, new_externals)] 285 for prev, new in zip(unresolved_by_part, new_externals)]
238 return all_succeeded, dlls, combined_externals 286 return all_succeeded, dlls, combined_externals
239 287
240 288
289 def ExtractSubObjsTargetedAtAll(
290 inputs,
291 num_parts,
292 description_parts,
293 description_all,
294 description_all_from_libs):
295 """For (lib, obj) tuples in the all_from_libs section, extract the obj out of
296 the lib and added it to inputs. Returns a list of lists for which part the
297 extracted obj belongs in (which is whichever the .lib isn't in)."""
298 by_parts = [[] for _ in range(num_parts)]
299 for lib_spec, obj_spec in description_all_from_libs:
300 for input_file in inputs:
301 if re.search(lib_spec, input_file):
302 objs = GetLibObjList(input_file)
303 match_count = 0
304 for obj in objs:
305 if re.search(obj_spec, obj, re.I):
306 extracted_obj = ExtractObjFromLib(input_file, obj)
307 #Log('extracted %s (%s %s)' % (extracted_obj, input_file, obj))
308 i = PartFor(input_file, description_parts, description_all)
309 if i == -1:
310 raise SystemExit(
311 '%s is already in all parts, but matched '
312 '%s in all_from_libs' % (input_file, obj))
313 # See note in main().
314 assert num_parts == 2, "Can't handle > 2 dlls currently"
315 by_parts[1 - i].append(obj)
316 match_count += 1
317 if match_count == 0:
318 raise SystemExit(
319 '%s, %s matched a lib, but no objs' % (lib_spec, obj_spec))
320 return by_parts
321
322
241 def main(): 323 def main():
242 flags, inputs = GetFlagsAndInputs(sys.argv[1:]) 324 flags, inputs = GetFlagsAndInputs(sys.argv[1:])
243 partition_file = os.path.normpath( 325 partition_file = os.path.normpath(
244 os.path.join(BASE_DIR, '../../../build/split_link_partition.py')) 326 os.path.join(BASE_DIR, '../../../build/split_link_partition.py'))
245 with open(partition_file) as partition: 327 with open(partition_file) as partition:
246 description = eval(partition.read()) 328 description = eval(partition.read())
247 inputs_by_part = [] 329 inputs_by_part = []
248 description_parts = description['parts'] 330 description_parts = description['parts']
249 # We currently assume that if a symbols isn't in dll 0, then it's in dll 1 331 # We currently assume that if a symbol isn't in dll 0, then it's in dll 1
250 # when generating def files. Otherwise, we'd need to do more complex things 332 # when generating def files. Otherwise, we'd need to do more complex things
251 # to figure out where each symbol actually is to assign it to the correct 333 # to figure out where each symbol actually is to assign it to the correct
252 # .def file. 334 # .def file.
253 num_parts = len(description_parts) 335 num_parts = len(description_parts)
254 assert num_parts == 2, "Can't handle > 2 dlls currently" 336 assert num_parts == 2, "Can't handle > 2 dlls currently"
255 description_parts.reverse() 337 description_parts.reverse()
338 objs_from_libs = ExtractSubObjsTargetedAtAll(
339 inputs,
340 num_parts,
341 description_parts,
342 description['all'],
343 description['all_from_libs'])
344 objs_from_libs.reverse()
256 inputs_by_part = [[] for _ in range(num_parts)] 345 inputs_by_part = [[] for _ in range(num_parts)]
257 for input_file in inputs: 346 for input_file in inputs:
258 i = PartFor(input_file, description_parts, description['all']) 347 i = PartFor(input_file, description_parts, description['all'])
259 if i == -1: 348 if i == -1:
260 for part in inputs_by_part: 349 for part in inputs_by_part:
261 part.append(input_file) 350 part.append(input_file)
262 else: 351 else:
263 inputs_by_part[i].append(input_file) 352 inputs_by_part[i].append(input_file)
264 inputs_by_part.reverse() 353 inputs_by_part.reverse()
265 354
355 # Put the subobjs on to the main list.
356 for i, part in enumerate(objs_from_libs):
357 Log('%d sub .objs added to part %d' % (len(part), i))
358 inputs_by_part[i].extend(part)
359
266 unresolved_by_part = [[] for _ in range(num_parts)] 360 unresolved_by_part = [[] for _ in range(num_parts)]
267 import_libs = [None] * num_parts 361 import_libs = [None] * num_parts
268 deffiles = [None] * num_parts 362 deffiles = [None] * num_parts
269 363
364 data_exports = 0
270 for i in range(5): 365 for i in range(5):
271 Log('--- starting pass %d' % i) 366 Log('--- starting pass %d' % i)
272 ok, dlls, unresolved_by_part = AttemptLink( 367 ok, dlls, unresolved_by_part = AttemptLink(
273 flags, inputs_by_part, unresolved_by_part, deffiles, import_libs) 368 flags, inputs_by_part, unresolved_by_part, deffiles, import_libs)
274 if ok: 369 if ok:
275 break 370 break
371 data_exports = 0
372 for i, part in enumerate(unresolved_by_part):
373 for export in part:
374 if IsDataDefinition(export):
375 print 'part %d contains data export: %s (aka %s)' % (
376 i, Unmangle(export), export)
377 data_exports += 1
276 deffiles = GenerateDefFiles(unresolved_by_part) 378 deffiles = GenerateDefFiles(unresolved_by_part)
277 import_libs = BuildImportLibs(flags, inputs_by_part, deffiles) 379 import_libs = BuildImportLibs(flags, inputs_by_part, deffiles)
278 else: 380 else:
279 return 1 381 return 1
280 382
383 if data_exports:
384 print 'Data exports found, see report above.'
385 print('These cannot be exported, and must be either duplicated to the '
386 'target DLL, or wrapped in a function.')
387 return 1
388
281 mt_exe = GetMtPath() 389 mt_exe = GetMtPath()
282 for i, dll in enumerate(dlls): 390 for i, dll in enumerate(dlls):
283 Log('embedding manifest in %s' % dll) 391 Log('embedding manifest in %s' % dll)
284 args = [mt_exe, '-nologo', '-manifest'] 392 args = [mt_exe, '-nologo', '-manifest']
285 args.append(ManifestNameForIndex(i)) 393 args.append(ManifestNameForIndex(i))
286 args.append(description['manifest']) 394 args.append(description['manifest'])
287 args.append('-outputresource:%s;2' % dll) 395 args.append('-outputresource:%s;2' % dll)
288 subprocess.check_call(args) 396 subprocess.check_call(args)
289 397
290 Log('built %r' % dlls) 398 Log('built %r' % dlls)
291 399
292 return 0 400 return 0
293 401
294 402
295 if __name__ == '__main__': 403 if __name__ == '__main__':
296 sys.exit(main()) 404 sys.exit(main())
OLDNEW
« no previous file with comments | « build/split_link_partition.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698