1342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch#!/usr/bin/env python
2342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch# Copyright 2015 The Chromium Authors. All rights reserved.
3342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch# Use of this source code is governed by a BSD-style license that can be
4342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch# found in the LICENSE file.
5342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
6342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch"""Runs 'ld -shared' and generates a .TOC file that's untouched when unchanged.
7342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
8342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen MurdochThis script exists to avoid using complex shell commands in
9342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdochgcc_toolchain.gni's tool("solink"), in case the host running the compiler
10342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdochdoes not have a POSIX-like shell (e.g. Windows).
11342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch"""
12342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
13342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdochimport argparse
14342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdochimport os
15342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdochimport re
16342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdochimport subprocess
17342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdochimport sys
18342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
19342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
20342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch# When running on a Windows host and using a toolchain whose tools are
21342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch# actually wrapper scripts (i.e. .bat files on Windows) rather than binary
22342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch# executables, the "command" to run has to be prefixed with this magic.
23342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch# The GN toolchain definitions take care of that for when GN/Ninja is
24342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch# running the tool directly.  When that command is passed in to this
25342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch# script, it appears as a unitary string but needs to be split up so that
26342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch# just 'cmd' is the actual command given to Python's subprocess module.
27342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen MurdochBAT_PREFIX = 'cmd /c call '
28342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
29342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdochdef CommandToRun(command):
30342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  if command[0].startswith(BAT_PREFIX):
31342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    command = command[0].split(None, 3) + command[1:]
32342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  return command
33342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
34342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
35342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdochdef CollectSONAME(args):
36342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  """Replaces: readelf -d $sofile | grep SONAME"""
37342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  toc = ''
38342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  readelf = subprocess.Popen(CommandToRun([args.readelf, '-d', args.sofile]),
39342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch                             stdout=subprocess.PIPE, bufsize=-1)
40342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  for line in readelf.stdout:
41342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    if 'SONAME' in line:
42342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch      toc += line
43342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  return readelf.wait(), toc
44342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
45342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
46342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdochdef CollectDynSym(args):
47342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  """Replaces: nm --format=posix -g -D $sofile | cut -f1-2 -d' '"""
48342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  toc = ''
49342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  nm = subprocess.Popen(CommandToRun([
50342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch      args.nm, '--format=posix', '-g', '-D', args.sofile]),
51342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch                        stdout=subprocess.PIPE, bufsize=-1)
52342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  for line in nm.stdout:
53342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    toc += ' '.join(line.split(' ', 2)[:2]) + '\n'
54342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  return nm.wait(), toc
55342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
56342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
57342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdochdef CollectTOC(args):
58342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  result, toc = CollectSONAME(args)
59342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  if result == 0:
60342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    result, dynsym = CollectDynSym(args)
61342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    toc += dynsym
62342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  return result, toc
63342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
64342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
65342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdochdef UpdateTOC(tocfile, toc):
66342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  if os.path.exists(tocfile):
67342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    old_toc = open(tocfile, 'r').read()
68342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  else:
69342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    old_toc = None
70342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  if toc != old_toc:
71342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    open(tocfile, 'w').write(toc)
72342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
73342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
74342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdochdef main():
75342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  parser = argparse.ArgumentParser(description=__doc__)
76342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  parser.add_argument('--readelf',
77342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch                      required=True,
78342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch                      help='The readelf binary to run',
79342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch                      metavar='PATH')
80342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  parser.add_argument('--nm',
81342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch                      required=True,
82342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch                      help='The nm binary to run',
83342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch                      metavar='PATH')
84342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  parser.add_argument('--strip',
85342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch                      help='The strip binary to run',
86342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch                      metavar='PATH')
87342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  parser.add_argument('--sofile',
88342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch                      required=True,
89342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch                      help='Shared object file produced by linking command',
90342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch                      metavar='FILE')
91342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  parser.add_argument('--tocfile',
92342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch                      required=True,
93342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch                      help='Output table-of-contents file',
94342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch                      metavar='FILE')
95342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  parser.add_argument('--output',
96342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch                      required=True,
97342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch                      help='Final output shared object file',
98342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch                      metavar='FILE')
99342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  parser.add_argument('command', nargs='+',
100342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch                      help='Linking command')
101342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  args = parser.parse_args()
102342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
103342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  # First, run the actual link.
104342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  result = subprocess.call(CommandToRun(args.command))
105342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  if result != 0:
106342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    return result
107342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
108342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  # Next, generate the contents of the TOC file.
109342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  result, toc = CollectTOC(args)
110342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  if result != 0:
111342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    return result
112342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
113342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  # If there is an existing TOC file with identical contents, leave it alone.
114342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  # Otherwise, write out the TOC file.
115342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  UpdateTOC(args.tocfile, toc)
116342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
117342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  # Finally, strip the linked shared object file (if desired).
118342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  if args.strip:
119342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch    result = subprocess.call(CommandToRun([args.strip, '--strip-unneeded',
120342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch                                           '-o', args.output, args.sofile]))
121342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
122342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  return result
123342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
124342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch
125342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdochif __name__ == "__main__":
126342c50ce1624b485728b9a4fc41d8bbf37eb46cfBen Murdoch  sys.exit(main())
127