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