OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright 2015 The Chromium Authors. All rights reserved. | 2 # Copyright 2015 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """Runs 'ld -shared' and generates a .TOC file that's untouched when unchanged. | 6 """Runs 'ld -shared' and generates a .TOC file that's untouched when unchanged. |
7 | 7 |
8 This script exists to avoid using complex shell commands in | 8 This script exists to avoid using complex shell commands in |
9 gcc_toolchain.gni's tool("solink"), in case the host running the compiler | 9 gcc_toolchain.gni's tool("solink"), in case the host running the compiler |
10 does not have a POSIX-like shell (e.g. Windows). | 10 does not have a POSIX-like shell (e.g. Windows). |
11 """ | 11 """ |
12 | 12 |
13 import argparse | 13 import argparse |
| 14 import gzip |
14 import os | 15 import os |
| 16 import shutil |
15 import subprocess | 17 import subprocess |
16 import sys | 18 import sys |
| 19 import threading |
17 | 20 |
18 import wrapper_utils | 21 import wrapper_utils |
19 | 22 |
20 | 23 |
21 def CollectSONAME(args): | 24 def CollectSONAME(args): |
22 """Replaces: readelf -d $sofile | grep SONAME""" | 25 """Replaces: readelf -d $sofile | grep SONAME""" |
23 toc = '' | 26 toc = '' |
24 readelf = subprocess.Popen(wrapper_utils.CommandToRun( | 27 readelf = subprocess.Popen(wrapper_utils.CommandToRun( |
25 [args.readelf, '-d', args.sofile]), stdout=subprocess.PIPE, bufsize=-1) | 28 [args.readelf, '-d', args.sofile]), stdout=subprocess.PIPE, bufsize=-1) |
26 for line in readelf.stdout: | 29 for line in readelf.stdout: |
(...skipping 23 matching lines...) Expand all Loading... |
50 | 53 |
51 def UpdateTOC(tocfile, toc): | 54 def UpdateTOC(tocfile, toc): |
52 if os.path.exists(tocfile): | 55 if os.path.exists(tocfile): |
53 old_toc = open(tocfile, 'r').read() | 56 old_toc = open(tocfile, 'r').read() |
54 else: | 57 else: |
55 old_toc = None | 58 old_toc = None |
56 if toc != old_toc: | 59 if toc != old_toc: |
57 open(tocfile, 'w').write(toc) | 60 open(tocfile, 'w').write(toc) |
58 | 61 |
59 | 62 |
| 63 def _GzipHelper(src_path, dest_path): |
| 64 # Results for Android map file with GCC on a z620: |
| 65 # Uncompressed: 207MB |
| 66 # gzip -9: 16.4MB, takes 8.7 seconds. |
| 67 # gzip -1: 21.8MB, takes 2.0 seconds. |
| 68 # Piping directly from the linker via -print-map (or via -Map with a fifo) |
| 69 # adds a whopping 30-45 seconds! |
| 70 with open(src_path, 'rb') as f_in, gzip.GzipFile(dest_path, 'wb', 1) as f_out: |
| 71 shutil.copyfileobj(f_in, f_out) |
| 72 os.unlink(src_path) |
| 73 |
| 74 |
60 def main(): | 75 def main(): |
61 parser = argparse.ArgumentParser(description=__doc__) | 76 parser = argparse.ArgumentParser(description=__doc__) |
62 parser.add_argument('--readelf', | 77 parser.add_argument('--readelf', |
63 required=True, | 78 required=True, |
64 help='The readelf binary to run', | 79 help='The readelf binary to run', |
65 metavar='PATH') | 80 metavar='PATH') |
66 parser.add_argument('--nm', | 81 parser.add_argument('--nm', |
67 required=True, | 82 required=True, |
68 help='The nm binary to run', | 83 help='The nm binary to run', |
69 metavar='PATH') | 84 metavar='PATH') |
70 parser.add_argument('--strip', | 85 parser.add_argument('--strip', |
71 help='The strip binary to run', | 86 help='The strip binary to run', |
72 metavar='PATH') | 87 metavar='PATH') |
73 parser.add_argument('--sofile', | 88 parser.add_argument('--sofile', |
74 required=True, | 89 required=True, |
75 help='Shared object file produced by linking command', | 90 help='Shared object file produced by linking command', |
76 metavar='FILE') | 91 metavar='FILE') |
77 parser.add_argument('--tocfile', | 92 parser.add_argument('--tocfile', |
78 required=True, | 93 required=True, |
79 help='Output table-of-contents file', | 94 help='Output table-of-contents file', |
80 metavar='FILE') | 95 metavar='FILE') |
| 96 parser.add_argument('--mapfile', |
| 97 help=('Use --Wl,-Map to generate a map file. Will be ' |
| 98 'gzipped if extension ends with .gz'), |
| 99 metavar='FILE') |
81 parser.add_argument('--output', | 100 parser.add_argument('--output', |
82 required=True, | 101 required=True, |
83 help='Final output shared object file', | 102 help='Final output shared object file', |
84 metavar='FILE') | 103 metavar='FILE') |
85 parser.add_argument('--resource-whitelist', | 104 parser.add_argument('--resource-whitelist', |
86 help='Merge all resource whitelists into a single file.', | 105 help='Merge all resource whitelists into a single file.', |
87 metavar='PATH') | 106 metavar='PATH') |
88 parser.add_argument('command', nargs='+', | 107 parser.add_argument('command', nargs='+', |
89 help='Linking command') | 108 help='Linking command') |
90 args = parser.parse_args() | 109 args = parser.parse_args() |
91 | 110 |
92 # Work-around for gold being slow-by-default. http://crbug.com/632230 | 111 # Work-around for gold being slow-by-default. http://crbug.com/632230 |
93 fast_env = dict(os.environ) | 112 fast_env = dict(os.environ) |
94 fast_env['LC_ALL'] = 'C' | 113 fast_env['LC_ALL'] = 'C' |
95 | 114 |
96 if args.resource_whitelist: | 115 if args.resource_whitelist: |
97 whitelist_candidates = wrapper_utils.ResolveRspLinks(args.command) | 116 whitelist_candidates = wrapper_utils.ResolveRspLinks(args.command) |
98 wrapper_utils.CombineResourceWhitelists( | 117 wrapper_utils.CombineResourceWhitelists( |
99 whitelist_candidates, args.resource_whitelist) | 118 whitelist_candidates, args.resource_whitelist) |
100 | 119 |
101 # First, run the actual link. | 120 # First, run the actual link. |
102 result = subprocess.call( | 121 command = wrapper_utils.CommandToRun(args.command) |
103 wrapper_utils.CommandToRun(args.command), env=fast_env) | 122 tmp_map_path = None |
| 123 if args.mapfile and args.mapfile.endswith('.gz'): |
| 124 tmp_map_path = args.mapfile + '.tmp' |
| 125 command.append('-Wl,-Map,' + tmp_map_path) |
| 126 elif args.mapfile: |
| 127 command.append('-Wl,-Map,' + args.mapfile) |
| 128 result = subprocess.call(command, env=fast_env) |
| 129 |
| 130 if tmp_map_path and result == 0: |
| 131 threading.Thread( |
| 132 target=lambda: _GzipHelper(tmp_map_path, args.mapfile)).start() |
| 133 elif tmp_map_path and os.path.exists(tmp_map_file): |
| 134 os.unlink(tmp_map_file) |
| 135 |
104 if result != 0: | 136 if result != 0: |
105 return result | 137 return result |
106 | 138 |
107 # Next, generate the contents of the TOC file. | 139 # Next, generate the contents of the TOC file. |
108 result, toc = CollectTOC(args) | 140 result, toc = CollectTOC(args) |
109 if result != 0: | 141 if result != 0: |
110 return result | 142 return result |
111 | 143 |
112 # If there is an existing TOC file with identical contents, leave it alone. | 144 # If there is an existing TOC file with identical contents, leave it alone. |
113 # Otherwise, write out the TOC file. | 145 # Otherwise, write out the TOC file. |
114 UpdateTOC(args.tocfile, toc) | 146 UpdateTOC(args.tocfile, toc) |
115 | 147 |
116 # Finally, strip the linked shared object file (if desired). | 148 # Finally, strip the linked shared object file (if desired). |
117 if args.strip: | 149 if args.strip: |
118 result = subprocess.call(wrapper_utils.CommandToRun( | 150 result = subprocess.call(wrapper_utils.CommandToRun( |
119 [args.strip, '--strip-unneeded', '-o', args.output, args.sofile])) | 151 [args.strip, '--strip-unneeded', '-o', args.output, args.sofile])) |
120 | 152 |
121 return result | 153 return result |
122 | 154 |
123 | 155 |
124 if __name__ == "__main__": | 156 if __name__ == "__main__": |
125 sys.exit(main()) | 157 sys.exit(main()) |
OLD | NEW |