1*6777b538SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*6777b538SAndroid Build Coastguard Worker# Copyright 2015 The Chromium Authors 3*6777b538SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 4*6777b538SAndroid Build Coastguard Worker# found in the LICENSE file. 5*6777b538SAndroid Build Coastguard Worker 6*6777b538SAndroid Build Coastguard Worker"""Runs 'ld -shared' and generates a .TOC file that's untouched when unchanged. 7*6777b538SAndroid Build Coastguard Worker 8*6777b538SAndroid Build Coastguard WorkerThis script exists to avoid using complex shell commands in 9*6777b538SAndroid Build Coastguard Workergcc_toolchain.gni's tool("solink"), in case the host running the compiler 10*6777b538SAndroid Build Coastguard Workerdoes not have a POSIX-like shell (e.g. Windows). 11*6777b538SAndroid Build Coastguard Worker""" 12*6777b538SAndroid Build Coastguard Worker 13*6777b538SAndroid Build Coastguard Workerimport argparse 14*6777b538SAndroid Build Coastguard Workerimport os 15*6777b538SAndroid Build Coastguard Workerimport shlex 16*6777b538SAndroid Build Coastguard Workerimport subprocess 17*6777b538SAndroid Build Coastguard Workerimport sys 18*6777b538SAndroid Build Coastguard Worker 19*6777b538SAndroid Build Coastguard Workerimport wrapper_utils 20*6777b538SAndroid Build Coastguard Worker 21*6777b538SAndroid Build Coastguard Worker 22*6777b538SAndroid Build Coastguard Workerdef CollectSONAME(args): 23*6777b538SAndroid Build Coastguard Worker """Replaces: readelf -d $sofile | grep SONAME""" 24*6777b538SAndroid Build Coastguard Worker # TODO(crbug.com/1259067): Come up with a way to get this info without having 25*6777b538SAndroid Build Coastguard Worker # to bundle readelf in the toolchain package. 26*6777b538SAndroid Build Coastguard Worker toc = '' 27*6777b538SAndroid Build Coastguard Worker readelf = subprocess.Popen(wrapper_utils.CommandToRun( 28*6777b538SAndroid Build Coastguard Worker [args.readelf, '-d', args.sofile]), 29*6777b538SAndroid Build Coastguard Worker stdout=subprocess.PIPE, 30*6777b538SAndroid Build Coastguard Worker bufsize=-1, 31*6777b538SAndroid Build Coastguard Worker universal_newlines=True) 32*6777b538SAndroid Build Coastguard Worker for line in readelf.stdout: 33*6777b538SAndroid Build Coastguard Worker if 'SONAME' in line: 34*6777b538SAndroid Build Coastguard Worker toc += line 35*6777b538SAndroid Build Coastguard Worker return readelf.wait(), toc 36*6777b538SAndroid Build Coastguard Worker 37*6777b538SAndroid Build Coastguard Worker 38*6777b538SAndroid Build Coastguard Workerdef CollectDynSym(args): 39*6777b538SAndroid Build Coastguard Worker """Replaces: nm --format=posix -g -D -p $sofile | cut -f1-2 -d' '""" 40*6777b538SAndroid Build Coastguard Worker toc = '' 41*6777b538SAndroid Build Coastguard Worker nm = subprocess.Popen(wrapper_utils.CommandToRun( 42*6777b538SAndroid Build Coastguard Worker [args.nm, '--format=posix', '-g', '-D', '-p', args.sofile]), 43*6777b538SAndroid Build Coastguard Worker stdout=subprocess.PIPE, 44*6777b538SAndroid Build Coastguard Worker bufsize=-1, 45*6777b538SAndroid Build Coastguard Worker universal_newlines=True) 46*6777b538SAndroid Build Coastguard Worker for line in nm.stdout: 47*6777b538SAndroid Build Coastguard Worker toc += ' '.join(line.split(' ', 2)[:2]) + '\n' 48*6777b538SAndroid Build Coastguard Worker return nm.wait(), toc 49*6777b538SAndroid Build Coastguard Worker 50*6777b538SAndroid Build Coastguard Worker 51*6777b538SAndroid Build Coastguard Workerdef CollectTOC(args): 52*6777b538SAndroid Build Coastguard Worker result, toc = CollectSONAME(args) 53*6777b538SAndroid Build Coastguard Worker if result == 0: 54*6777b538SAndroid Build Coastguard Worker result, dynsym = CollectDynSym(args) 55*6777b538SAndroid Build Coastguard Worker toc += dynsym 56*6777b538SAndroid Build Coastguard Worker return result, toc 57*6777b538SAndroid Build Coastguard Worker 58*6777b538SAndroid Build Coastguard Worker 59*6777b538SAndroid Build Coastguard Workerdef UpdateTOC(tocfile, toc): 60*6777b538SAndroid Build Coastguard Worker if os.path.exists(tocfile): 61*6777b538SAndroid Build Coastguard Worker old_toc = open(tocfile, 'r').read() 62*6777b538SAndroid Build Coastguard Worker else: 63*6777b538SAndroid Build Coastguard Worker old_toc = None 64*6777b538SAndroid Build Coastguard Worker if toc != old_toc: 65*6777b538SAndroid Build Coastguard Worker open(tocfile, 'w').write(toc) 66*6777b538SAndroid Build Coastguard Worker 67*6777b538SAndroid Build Coastguard Worker 68*6777b538SAndroid Build Coastguard Workerdef CollectInputs(out, args): 69*6777b538SAndroid Build Coastguard Worker for x in args: 70*6777b538SAndroid Build Coastguard Worker if x.startswith('@'): 71*6777b538SAndroid Build Coastguard Worker with open(x[1:]) as rsp: 72*6777b538SAndroid Build Coastguard Worker CollectInputs(out, shlex.split(rsp.read())) 73*6777b538SAndroid Build Coastguard Worker elif not x.startswith('-') and (x.endswith('.o') or x.endswith('.a')): 74*6777b538SAndroid Build Coastguard Worker out.write(x) 75*6777b538SAndroid Build Coastguard Worker out.write('\n') 76*6777b538SAndroid Build Coastguard Worker 77*6777b538SAndroid Build Coastguard Worker 78*6777b538SAndroid Build Coastguard Workerdef InterceptFlag(flag, command): 79*6777b538SAndroid Build Coastguard Worker ret = flag in command 80*6777b538SAndroid Build Coastguard Worker if ret: 81*6777b538SAndroid Build Coastguard Worker command.remove(flag) 82*6777b538SAndroid Build Coastguard Worker return ret 83*6777b538SAndroid Build Coastguard Worker 84*6777b538SAndroid Build Coastguard Worker 85*6777b538SAndroid Build Coastguard Workerdef SafeDelete(path): 86*6777b538SAndroid Build Coastguard Worker try: 87*6777b538SAndroid Build Coastguard Worker os.unlink(path) 88*6777b538SAndroid Build Coastguard Worker except OSError: 89*6777b538SAndroid Build Coastguard Worker pass 90*6777b538SAndroid Build Coastguard Worker 91*6777b538SAndroid Build Coastguard Worker 92*6777b538SAndroid Build Coastguard Workerdef main(): 93*6777b538SAndroid Build Coastguard Worker parser = argparse.ArgumentParser(description=__doc__) 94*6777b538SAndroid Build Coastguard Worker parser.add_argument('--readelf', 95*6777b538SAndroid Build Coastguard Worker required=True, 96*6777b538SAndroid Build Coastguard Worker help='The readelf binary to run', 97*6777b538SAndroid Build Coastguard Worker metavar='PATH') 98*6777b538SAndroid Build Coastguard Worker parser.add_argument('--nm', 99*6777b538SAndroid Build Coastguard Worker required=True, 100*6777b538SAndroid Build Coastguard Worker help='The nm binary to run', 101*6777b538SAndroid Build Coastguard Worker metavar='PATH') 102*6777b538SAndroid Build Coastguard Worker parser.add_argument('--strip', 103*6777b538SAndroid Build Coastguard Worker help='The strip binary to run', 104*6777b538SAndroid Build Coastguard Worker metavar='PATH') 105*6777b538SAndroid Build Coastguard Worker parser.add_argument('--dwp', help='The dwp binary to run', metavar='PATH') 106*6777b538SAndroid Build Coastguard Worker parser.add_argument('--sofile', 107*6777b538SAndroid Build Coastguard Worker required=True, 108*6777b538SAndroid Build Coastguard Worker help='Shared object file produced by linking command', 109*6777b538SAndroid Build Coastguard Worker metavar='FILE') 110*6777b538SAndroid Build Coastguard Worker parser.add_argument('--tocfile', 111*6777b538SAndroid Build Coastguard Worker required=True, 112*6777b538SAndroid Build Coastguard Worker help='Output table-of-contents file', 113*6777b538SAndroid Build Coastguard Worker metavar='FILE') 114*6777b538SAndroid Build Coastguard Worker parser.add_argument('--map-file', 115*6777b538SAndroid Build Coastguard Worker help=('Use --Wl,-Map to generate a map file. Will be ' 116*6777b538SAndroid Build Coastguard Worker 'gzipped if extension ends with .gz'), 117*6777b538SAndroid Build Coastguard Worker metavar='FILE') 118*6777b538SAndroid Build Coastguard Worker parser.add_argument('--output', 119*6777b538SAndroid Build Coastguard Worker required=True, 120*6777b538SAndroid Build Coastguard Worker help='Final output shared object file', 121*6777b538SAndroid Build Coastguard Worker metavar='FILE') 122*6777b538SAndroid Build Coastguard Worker parser.add_argument('command', nargs='+', 123*6777b538SAndroid Build Coastguard Worker help='Linking command') 124*6777b538SAndroid Build Coastguard Worker args = parser.parse_args() 125*6777b538SAndroid Build Coastguard Worker 126*6777b538SAndroid Build Coastguard Worker # Work-around for gold being slow-by-default. http://crbug.com/632230 127*6777b538SAndroid Build Coastguard Worker fast_env = dict(os.environ) 128*6777b538SAndroid Build Coastguard Worker fast_env['LC_ALL'] = 'C' 129*6777b538SAndroid Build Coastguard Worker 130*6777b538SAndroid Build Coastguard Worker # Extract flags passed through ldflags but meant for this script. 131*6777b538SAndroid Build Coastguard Worker # https://crbug.com/954311 tracks finding a better way to plumb these. 132*6777b538SAndroid Build Coastguard Worker partitioned_library = InterceptFlag('--partitioned-library', args.command) 133*6777b538SAndroid Build Coastguard Worker collect_inputs_only = InterceptFlag('--collect-inputs-only', args.command) 134*6777b538SAndroid Build Coastguard Worker 135*6777b538SAndroid Build Coastguard Worker # Partitioned .so libraries are used only for splitting apart in a subsequent 136*6777b538SAndroid Build Coastguard Worker # step. 137*6777b538SAndroid Build Coastguard Worker # 138*6777b538SAndroid Build Coastguard Worker # - The TOC file optimization isn't useful, because the partition libraries 139*6777b538SAndroid Build Coastguard Worker # must always be re-extracted if the combined library changes (and nothing 140*6777b538SAndroid Build Coastguard Worker # should be depending on the combined library's dynamic symbol table). 141*6777b538SAndroid Build Coastguard Worker # - Stripping isn't necessary, because the combined library is not used in 142*6777b538SAndroid Build Coastguard Worker # production or published. 143*6777b538SAndroid Build Coastguard Worker # 144*6777b538SAndroid Build Coastguard Worker # Both of these operations could still be done, they're needless work, and 145*6777b538SAndroid Build Coastguard Worker # tools would need to be updated to handle and/or not complain about 146*6777b538SAndroid Build Coastguard Worker # partitioned libraries. Instead, to keep Ninja happy, simply create dummy 147*6777b538SAndroid Build Coastguard Worker # files for the TOC and stripped lib. 148*6777b538SAndroid Build Coastguard Worker if collect_inputs_only or partitioned_library: 149*6777b538SAndroid Build Coastguard Worker open(args.output, 'w').close() 150*6777b538SAndroid Build Coastguard Worker open(args.tocfile, 'w').close() 151*6777b538SAndroid Build Coastguard Worker 152*6777b538SAndroid Build Coastguard Worker # Instead of linking, records all inputs to a file. This is used by 153*6777b538SAndroid Build Coastguard Worker # enable_resource_allowlist_generation in order to avoid needing to 154*6777b538SAndroid Build Coastguard Worker # link (which is slow) to build the resources allowlist. 155*6777b538SAndroid Build Coastguard Worker if collect_inputs_only: 156*6777b538SAndroid Build Coastguard Worker if args.map_file: 157*6777b538SAndroid Build Coastguard Worker open(args.map_file, 'w').close() 158*6777b538SAndroid Build Coastguard Worker if args.dwp: 159*6777b538SAndroid Build Coastguard Worker open(args.sofile + '.dwp', 'w').close() 160*6777b538SAndroid Build Coastguard Worker 161*6777b538SAndroid Build Coastguard Worker with open(args.sofile, 'w') as f: 162*6777b538SAndroid Build Coastguard Worker CollectInputs(f, args.command) 163*6777b538SAndroid Build Coastguard Worker return 0 164*6777b538SAndroid Build Coastguard Worker 165*6777b538SAndroid Build Coastguard Worker # First, run the actual link. 166*6777b538SAndroid Build Coastguard Worker command = wrapper_utils.CommandToRun(args.command) 167*6777b538SAndroid Build Coastguard Worker result = wrapper_utils.RunLinkWithOptionalMapFile(command, 168*6777b538SAndroid Build Coastguard Worker env=fast_env, 169*6777b538SAndroid Build Coastguard Worker map_file=args.map_file) 170*6777b538SAndroid Build Coastguard Worker 171*6777b538SAndroid Build Coastguard Worker if result != 0: 172*6777b538SAndroid Build Coastguard Worker return result 173*6777b538SAndroid Build Coastguard Worker 174*6777b538SAndroid Build Coastguard Worker # If dwp is set, then package debug info for this SO. 175*6777b538SAndroid Build Coastguard Worker dwp_proc = None 176*6777b538SAndroid Build Coastguard Worker if args.dwp: 177*6777b538SAndroid Build Coastguard Worker # Explicit delete to account for symlinks (when toggling between 178*6777b538SAndroid Build Coastguard Worker # debug/release). 179*6777b538SAndroid Build Coastguard Worker SafeDelete(args.sofile + '.dwp') 180*6777b538SAndroid Build Coastguard Worker # Suppress warnings about duplicate CU entries (https://crbug.com/1264130) 181*6777b538SAndroid Build Coastguard Worker dwp_proc = subprocess.Popen(wrapper_utils.CommandToRun( 182*6777b538SAndroid Build Coastguard Worker [args.dwp, '-e', args.sofile, '-o', args.sofile + '.dwp']), 183*6777b538SAndroid Build Coastguard Worker stderr=subprocess.DEVNULL) 184*6777b538SAndroid Build Coastguard Worker 185*6777b538SAndroid Build Coastguard Worker if not partitioned_library: 186*6777b538SAndroid Build Coastguard Worker # Next, generate the contents of the TOC file. 187*6777b538SAndroid Build Coastguard Worker result, toc = CollectTOC(args) 188*6777b538SAndroid Build Coastguard Worker if result != 0: 189*6777b538SAndroid Build Coastguard Worker return result 190*6777b538SAndroid Build Coastguard Worker 191*6777b538SAndroid Build Coastguard Worker # If there is an existing TOC file with identical contents, leave it alone. 192*6777b538SAndroid Build Coastguard Worker # Otherwise, write out the TOC file. 193*6777b538SAndroid Build Coastguard Worker UpdateTOC(args.tocfile, toc) 194*6777b538SAndroid Build Coastguard Worker 195*6777b538SAndroid Build Coastguard Worker # Finally, strip the linked shared object file (if desired). 196*6777b538SAndroid Build Coastguard Worker if args.strip: 197*6777b538SAndroid Build Coastguard Worker result = subprocess.call( 198*6777b538SAndroid Build Coastguard Worker wrapper_utils.CommandToRun( 199*6777b538SAndroid Build Coastguard Worker [args.strip, '-o', args.output, args.sofile])) 200*6777b538SAndroid Build Coastguard Worker 201*6777b538SAndroid Build Coastguard Worker if dwp_proc: 202*6777b538SAndroid Build Coastguard Worker dwp_result = dwp_proc.wait() 203*6777b538SAndroid Build Coastguard Worker if dwp_result != 0: 204*6777b538SAndroid Build Coastguard Worker sys.stderr.write('dwp failed with error code {}\n'.format(dwp_result)) 205*6777b538SAndroid Build Coastguard Worker return dwp_result 206*6777b538SAndroid Build Coastguard Worker 207*6777b538SAndroid Build Coastguard Worker return result 208*6777b538SAndroid Build Coastguard Worker 209*6777b538SAndroid Build Coastguard Worker 210*6777b538SAndroid Build Coastguard Workerif __name__ == "__main__": 211*6777b538SAndroid Build Coastguard Worker sys.exit(main()) 212