xref: /aosp_15_r20/bionic/benchmarks/linker_relocation/regen/gen_bench.py (revision 8d67ca893c1523eb926b9080dbe4e2ffd2a27ba1)
1*8d67ca89SAndroid Build Coastguard Worker#!/usr/bin/env python3
2*8d67ca89SAndroid Build Coastguard Worker#
3*8d67ca89SAndroid Build Coastguard Worker# Copyright (C) 2019 The Android Open Source Project
4*8d67ca89SAndroid Build Coastguard Worker# All rights reserved.
5*8d67ca89SAndroid Build Coastguard Worker#
6*8d67ca89SAndroid Build Coastguard Worker# Redistribution and use in source and binary forms, with or without
7*8d67ca89SAndroid Build Coastguard Worker# modification, are permitted provided that the following conditions
8*8d67ca89SAndroid Build Coastguard Worker# are met:
9*8d67ca89SAndroid Build Coastguard Worker#  * Redistributions of source code must retain the above copyright
10*8d67ca89SAndroid Build Coastguard Worker#    notice, this list of conditions and the following disclaimer.
11*8d67ca89SAndroid Build Coastguard Worker#  * Redistributions in binary form must reproduce the above copyright
12*8d67ca89SAndroid Build Coastguard Worker#    notice, this list of conditions and the following disclaimer in
13*8d67ca89SAndroid Build Coastguard Worker#    the documentation and/or other materials provided with the
14*8d67ca89SAndroid Build Coastguard Worker#    distribution.
15*8d67ca89SAndroid Build Coastguard Worker#
16*8d67ca89SAndroid Build Coastguard Worker# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17*8d67ca89SAndroid Build Coastguard Worker# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18*8d67ca89SAndroid Build Coastguard Worker# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19*8d67ca89SAndroid Build Coastguard Worker# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20*8d67ca89SAndroid Build Coastguard Worker# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21*8d67ca89SAndroid Build Coastguard Worker# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22*8d67ca89SAndroid Build Coastguard Worker# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
23*8d67ca89SAndroid Build Coastguard Worker# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24*8d67ca89SAndroid Build Coastguard Worker# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25*8d67ca89SAndroid Build Coastguard Worker# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26*8d67ca89SAndroid Build Coastguard Worker# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27*8d67ca89SAndroid Build Coastguard Worker# SUCH DAMAGE.
28*8d67ca89SAndroid Build Coastguard Worker
29*8d67ca89SAndroid Build Coastguard Worker# Generate a benchmark using a JSON dump of ELF file symbols and relocations.
30*8d67ca89SAndroid Build Coastguard Worker
31*8d67ca89SAndroid Build Coastguard Workerimport argparse
32*8d67ca89SAndroid Build Coastguard Workerimport codecs
33*8d67ca89SAndroid Build Coastguard Workerimport json
34*8d67ca89SAndroid Build Coastguard Workerimport math
35*8d67ca89SAndroid Build Coastguard Workerimport os
36*8d67ca89SAndroid Build Coastguard Workerimport re
37*8d67ca89SAndroid Build Coastguard Workerimport shlex
38*8d67ca89SAndroid Build Coastguard Workerimport shutil
39*8d67ca89SAndroid Build Coastguard Workerimport subprocess
40*8d67ca89SAndroid Build Coastguard Workerimport sys
41*8d67ca89SAndroid Build Coastguard Workerimport tempfile
42*8d67ca89SAndroid Build Coastguard Workerimport textwrap
43*8d67ca89SAndroid Build Coastguard Workerimport typing
44*8d67ca89SAndroid Build Coastguard Workerfrom enum import Enum
45*8d67ca89SAndroid Build Coastguard Workerfrom typing import Dict, List, Optional, Set
46*8d67ca89SAndroid Build Coastguard Workerfrom subprocess import PIPE, DEVNULL
47*8d67ca89SAndroid Build Coastguard Workerfrom pathlib import Path
48*8d67ca89SAndroid Build Coastguard Worker
49*8d67ca89SAndroid Build Coastguard Workerfrom common_types import LoadedLibrary, SymbolRef, SymBind, SymKind, bfs_walk, json_to_elf_tree
50*8d67ca89SAndroid Build Coastguard Worker
51*8d67ca89SAndroid Build Coastguard Worker
52*8d67ca89SAndroid Build Coastguard Workerg_obfuscate = True
53*8d67ca89SAndroid Build Coastguard Workerg_benchmark_name = 'linker_reloc_bench'
54*8d67ca89SAndroid Build Coastguard Worker
55*8d67ca89SAndroid Build Coastguard Worker
56*8d67ca89SAndroid Build Coastguard WorkerkBionicSonames: Set[str] = set([
57*8d67ca89SAndroid Build Coastguard Worker    'libc.so',
58*8d67ca89SAndroid Build Coastguard Worker    'libdl.so',
59*8d67ca89SAndroid Build Coastguard Worker    'libdl_android.so',
60*8d67ca89SAndroid Build Coastguard Worker    'libm.so',
61*8d67ca89SAndroid Build Coastguard Worker    'ld-android.so',
62*8d67ca89SAndroid Build Coastguard Worker])
63*8d67ca89SAndroid Build Coastguard Worker
64*8d67ca89SAndroid Build Coastguard Worker# Skip these symbols so the benchmark runs on multiple C libraries (glibc, Bionic, musl).
65*8d67ca89SAndroid Build Coastguard WorkerkBionicIgnoredSymbols: Set[str] = set([
66*8d67ca89SAndroid Build Coastguard Worker    '__FD_ISSET_chk',
67*8d67ca89SAndroid Build Coastguard Worker    '__FD_SET_chk',
68*8d67ca89SAndroid Build Coastguard Worker    '__assert',
69*8d67ca89SAndroid Build Coastguard Worker    '__assert2',
70*8d67ca89SAndroid Build Coastguard Worker    '__b64_ntop',
71*8d67ca89SAndroid Build Coastguard Worker    '__cmsg_nxthdr',
72*8d67ca89SAndroid Build Coastguard Worker    '__cxa_thread_atexit_impl',
73*8d67ca89SAndroid Build Coastguard Worker    '__errno',
74*8d67ca89SAndroid Build Coastguard Worker    '__gnu_basename',
75*8d67ca89SAndroid Build Coastguard Worker    '__gnu_strerror_r',
76*8d67ca89SAndroid Build Coastguard Worker    '__memcpy_chk',
77*8d67ca89SAndroid Build Coastguard Worker    '__memmove_chk',
78*8d67ca89SAndroid Build Coastguard Worker    '__memset_chk',
79*8d67ca89SAndroid Build Coastguard Worker    '__open_2',
80*8d67ca89SAndroid Build Coastguard Worker    '__openat_2',
81*8d67ca89SAndroid Build Coastguard Worker    '__pread64_chk',
82*8d67ca89SAndroid Build Coastguard Worker    '__pread_chk',
83*8d67ca89SAndroid Build Coastguard Worker    '__read_chk',
84*8d67ca89SAndroid Build Coastguard Worker    '__readlink_chk',
85*8d67ca89SAndroid Build Coastguard Worker    '__register_atfork',
86*8d67ca89SAndroid Build Coastguard Worker    '__sF',
87*8d67ca89SAndroid Build Coastguard Worker    '__strcat_chk',
88*8d67ca89SAndroid Build Coastguard Worker    '__strchr_chk',
89*8d67ca89SAndroid Build Coastguard Worker    '__strcpy_chk',
90*8d67ca89SAndroid Build Coastguard Worker    '__strlcat_chk',
91*8d67ca89SAndroid Build Coastguard Worker    '__strlcpy_chk',
92*8d67ca89SAndroid Build Coastguard Worker    '__strlen_chk',
93*8d67ca89SAndroid Build Coastguard Worker    '__strncat_chk',
94*8d67ca89SAndroid Build Coastguard Worker    '__strncpy_chk',
95*8d67ca89SAndroid Build Coastguard Worker    '__strncpy_chk2',
96*8d67ca89SAndroid Build Coastguard Worker    '__strrchr_chk',
97*8d67ca89SAndroid Build Coastguard Worker    '__system_property_area_serial',
98*8d67ca89SAndroid Build Coastguard Worker    '__system_property_find',
99*8d67ca89SAndroid Build Coastguard Worker    '__system_property_foreach',
100*8d67ca89SAndroid Build Coastguard Worker    '__system_property_get',
101*8d67ca89SAndroid Build Coastguard Worker    '__system_property_read',
102*8d67ca89SAndroid Build Coastguard Worker    '__system_property_serial',
103*8d67ca89SAndroid Build Coastguard Worker    '__system_property_set',
104*8d67ca89SAndroid Build Coastguard Worker    '__umask_chk',
105*8d67ca89SAndroid Build Coastguard Worker    '__vsnprintf_chk',
106*8d67ca89SAndroid Build Coastguard Worker    '__vsprintf_chk',
107*8d67ca89SAndroid Build Coastguard Worker    'android_dlopen_ext',
108*8d67ca89SAndroid Build Coastguard Worker    'android_set_abort_message',
109*8d67ca89SAndroid Build Coastguard Worker    'arc4random_buf',
110*8d67ca89SAndroid Build Coastguard Worker    'dl_unwind_find_exidx',
111*8d67ca89SAndroid Build Coastguard Worker    'fts_close',
112*8d67ca89SAndroid Build Coastguard Worker    'fts_open',
113*8d67ca89SAndroid Build Coastguard Worker    'fts_read',
114*8d67ca89SAndroid Build Coastguard Worker    'fts_set',
115*8d67ca89SAndroid Build Coastguard Worker    'getprogname',
116*8d67ca89SAndroid Build Coastguard Worker    'gettid',
117*8d67ca89SAndroid Build Coastguard Worker    'isnanf',
118*8d67ca89SAndroid Build Coastguard Worker    'lseek64',
119*8d67ca89SAndroid Build Coastguard Worker    'lstat64',
120*8d67ca89SAndroid Build Coastguard Worker    'mallinfo',
121*8d67ca89SAndroid Build Coastguard Worker    'malloc_info',
122*8d67ca89SAndroid Build Coastguard Worker    'pread64',
123*8d67ca89SAndroid Build Coastguard Worker    'pthread_gettid_np',
124*8d67ca89SAndroid Build Coastguard Worker    'pwrite64',
125*8d67ca89SAndroid Build Coastguard Worker    'res_mkquery',
126*8d67ca89SAndroid Build Coastguard Worker    'strlcpy',
127*8d67ca89SAndroid Build Coastguard Worker    'strtoll_l',
128*8d67ca89SAndroid Build Coastguard Worker    'strtoull_l',
129*8d67ca89SAndroid Build Coastguard Worker    'tgkill',
130*8d67ca89SAndroid Build Coastguard Worker])
131*8d67ca89SAndroid Build Coastguard Worker
132*8d67ca89SAndroid Build Coastguard Worker
133*8d67ca89SAndroid Build Coastguard WorkerDefinitions = Dict[str, LoadedLibrary]
134*8d67ca89SAndroid Build Coastguard Worker
135*8d67ca89SAndroid Build Coastguard Workerdef build_symbol_index(lib: LoadedLibrary) -> Definitions:
136*8d67ca89SAndroid Build Coastguard Worker    defs: Dict[str, LoadedLibrary] = {}
137*8d67ca89SAndroid Build Coastguard Worker    for lib in bfs_walk(lib):
138*8d67ca89SAndroid Build Coastguard Worker        for sym in lib.syms.values():
139*8d67ca89SAndroid Build Coastguard Worker            if not sym.defined: continue
140*8d67ca89SAndroid Build Coastguard Worker            defs.setdefault(sym.name, lib)
141*8d67ca89SAndroid Build Coastguard Worker    return defs
142*8d67ca89SAndroid Build Coastguard Worker
143*8d67ca89SAndroid Build Coastguard Worker
144*8d67ca89SAndroid Build Coastguard Workerdef check_rels(root: LoadedLibrary, defs: Definitions) -> None:
145*8d67ca89SAndroid Build Coastguard Worker    # Find every symbol for every relocation in the load group.
146*8d67ca89SAndroid Build Coastguard Worker    has_missing = False
147*8d67ca89SAndroid Build Coastguard Worker    for lib in bfs_walk(root):
148*8d67ca89SAndroid Build Coastguard Worker        rels = lib.rels
149*8d67ca89SAndroid Build Coastguard Worker        for sym in rels.got + rels.jump_slots + [sym for off, sym in rels.symbolic]:
150*8d67ca89SAndroid Build Coastguard Worker            if sym.name not in defs:
151*8d67ca89SAndroid Build Coastguard Worker                if sym.is_weak:
152*8d67ca89SAndroid Build Coastguard Worker                    pass # print('info: weak undefined', lib.soname, r)
153*8d67ca89SAndroid Build Coastguard Worker                else:
154*8d67ca89SAndroid Build Coastguard Worker                    print(f'error: {lib.soname}: unresolved relocation to {sym.name}')
155*8d67ca89SAndroid Build Coastguard Worker                    has_missing = True
156*8d67ca89SAndroid Build Coastguard Worker    if has_missing: sys.exit('error: had unresolved relocations')
157*8d67ca89SAndroid Build Coastguard Worker
158*8d67ca89SAndroid Build Coastguard Worker
159*8d67ca89SAndroid Build Coastguard Worker# Obscure names to avoid polluting Android code search.
160*8d67ca89SAndroid Build Coastguard Workerdef rot13(text: str) -> str:
161*8d67ca89SAndroid Build Coastguard Worker    if g_obfuscate:
162*8d67ca89SAndroid Build Coastguard Worker        result = codecs.getencoder("rot-13")(text)[0]
163*8d67ca89SAndroid Build Coastguard Worker        assert isinstance(result, str)
164*8d67ca89SAndroid Build Coastguard Worker        return result
165*8d67ca89SAndroid Build Coastguard Worker    else:
166*8d67ca89SAndroid Build Coastguard Worker        return text
167*8d67ca89SAndroid Build Coastguard Worker
168*8d67ca89SAndroid Build Coastguard Worker
169*8d67ca89SAndroid Build Coastguard Workerdef make_asm_file(lib: LoadedLibrary, is_main: bool, out_filename: Path, map_out_filename: Path,
170*8d67ca89SAndroid Build Coastguard Worker                  defs: Definitions) -> bool:
171*8d67ca89SAndroid Build Coastguard Worker
172*8d67ca89SAndroid Build Coastguard Worker    def trans_sym(name: str, ver: Optional[str]) -> Optional[str]:
173*8d67ca89SAndroid Build Coastguard Worker        nonlocal defs
174*8d67ca89SAndroid Build Coastguard Worker        d = defs.get(name)
175*8d67ca89SAndroid Build Coastguard Worker        if d is not None and d.soname in kBionicSonames:
176*8d67ca89SAndroid Build Coastguard Worker            if name in kBionicIgnoredSymbols: return None
177*8d67ca89SAndroid Build Coastguard Worker            # Discard relocations to newer Bionic symbols, because there aren't many of them, and
178*8d67ca89SAndroid Build Coastguard Worker            # they would limit where the benchmark can run.
179*8d67ca89SAndroid Build Coastguard Worker            if ver == 'LIBC': return name
180*8d67ca89SAndroid Build Coastguard Worker            return None
181*8d67ca89SAndroid Build Coastguard Worker        return 'b_' + rot13(name)
182*8d67ca89SAndroid Build Coastguard Worker
183*8d67ca89SAndroid Build Coastguard Worker    versions: Dict[Optional[str], List[str]] = {}
184*8d67ca89SAndroid Build Coastguard Worker
185*8d67ca89SAndroid Build Coastguard Worker    with open(out_filename, 'w') as out:
186*8d67ca89SAndroid Build Coastguard Worker        out.write(f'// AUTO-GENERATED BY {os.path.basename(__file__)} -- do not edit manually\n')
187*8d67ca89SAndroid Build Coastguard Worker        out.write(f'#include "{g_benchmark_name}_asm.h"\n')
188*8d67ca89SAndroid Build Coastguard Worker        out.write('.data\n')
189*8d67ca89SAndroid Build Coastguard Worker        out.write('.p2align 4\n')
190*8d67ca89SAndroid Build Coastguard Worker
191*8d67ca89SAndroid Build Coastguard Worker        if is_main:
192*8d67ca89SAndroid Build Coastguard Worker            out.write('.text\n' 'MAIN\n')
193*8d67ca89SAndroid Build Coastguard Worker
194*8d67ca89SAndroid Build Coastguard Worker        for d in lib.syms.values():
195*8d67ca89SAndroid Build Coastguard Worker            if not d.defined: continue
196*8d67ca89SAndroid Build Coastguard Worker            sym = trans_sym(d.name, None)
197*8d67ca89SAndroid Build Coastguard Worker            if sym is None: continue
198*8d67ca89SAndroid Build Coastguard Worker            binding = 'weak' if d.bind == SymBind.Weak else 'globl'
199*8d67ca89SAndroid Build Coastguard Worker            if d.kind == SymKind.Func:
200*8d67ca89SAndroid Build Coastguard Worker                out.write('.text\n'
201*8d67ca89SAndroid Build Coastguard Worker                          f'.{binding} {sym}\n'
202*8d67ca89SAndroid Build Coastguard Worker                          f'.type {sym},%function\n'
203*8d67ca89SAndroid Build Coastguard Worker                          f'{sym}:\n'
204*8d67ca89SAndroid Build Coastguard Worker                          'nop\n')
205*8d67ca89SAndroid Build Coastguard Worker            else: # SymKind.Var
206*8d67ca89SAndroid Build Coastguard Worker                out.write('.data\n'
207*8d67ca89SAndroid Build Coastguard Worker                          f'.{binding} {sym}\n'
208*8d67ca89SAndroid Build Coastguard Worker                          f'.type {sym},%object\n'
209*8d67ca89SAndroid Build Coastguard Worker                          f'{sym}:\n'
210*8d67ca89SAndroid Build Coastguard Worker                          f'.space __SIZEOF_POINTER__\n')
211*8d67ca89SAndroid Build Coastguard Worker            versions.setdefault(d.ver_name, []).append(sym)
212*8d67ca89SAndroid Build Coastguard Worker
213*8d67ca89SAndroid Build Coastguard Worker        out.write('.text\n')
214*8d67ca89SAndroid Build Coastguard Worker        for r in lib.rels.jump_slots:
215*8d67ca89SAndroid Build Coastguard Worker            sym = trans_sym(r.name, r.ver)
216*8d67ca89SAndroid Build Coastguard Worker            if sym is None: continue
217*8d67ca89SAndroid Build Coastguard Worker            if r.is_weak: out.write(f'.weak {sym}\n')
218*8d67ca89SAndroid Build Coastguard Worker            out.write(f'CALL({sym})\n')
219*8d67ca89SAndroid Build Coastguard Worker        out.write('.text\n')
220*8d67ca89SAndroid Build Coastguard Worker        for r in lib.rels.got:
221*8d67ca89SAndroid Build Coastguard Worker            sym = trans_sym(r.name, r.ver)
222*8d67ca89SAndroid Build Coastguard Worker            if sym is None: continue
223*8d67ca89SAndroid Build Coastguard Worker            if r.is_weak: out.write(f'.weak {sym}\n')
224*8d67ca89SAndroid Build Coastguard Worker            out.write(f'GOT_RELOC({sym})\n')
225*8d67ca89SAndroid Build Coastguard Worker
226*8d67ca89SAndroid Build Coastguard Worker        out.write('.data\n')
227*8d67ca89SAndroid Build Coastguard Worker        out.write('local_label:\n')
228*8d67ca89SAndroid Build Coastguard Worker
229*8d67ca89SAndroid Build Coastguard Worker        image = []
230*8d67ca89SAndroid Build Coastguard Worker        for off in lib.rels.relative:
231*8d67ca89SAndroid Build Coastguard Worker            image.append((off, f'DATA_WORD(local_label)\n'))
232*8d67ca89SAndroid Build Coastguard Worker        for off, r in lib.rels.symbolic:
233*8d67ca89SAndroid Build Coastguard Worker            sym = trans_sym(r.name, r.ver)
234*8d67ca89SAndroid Build Coastguard Worker            if sym is None: continue
235*8d67ca89SAndroid Build Coastguard Worker            text = f'DATA_WORD({sym})\n'
236*8d67ca89SAndroid Build Coastguard Worker            if r.is_weak: text += f'.weak {sym}\n'
237*8d67ca89SAndroid Build Coastguard Worker            image.append((off, text))
238*8d67ca89SAndroid Build Coastguard Worker        image.sort()
239*8d67ca89SAndroid Build Coastguard Worker
240*8d67ca89SAndroid Build Coastguard Worker        cur_off = 0
241*8d67ca89SAndroid Build Coastguard Worker        for off, text in image:
242*8d67ca89SAndroid Build Coastguard Worker            if cur_off < off:
243*8d67ca89SAndroid Build Coastguard Worker                out.write(f'.space (__SIZEOF_POINTER__ * {off - cur_off})\n')
244*8d67ca89SAndroid Build Coastguard Worker                cur_off = off
245*8d67ca89SAndroid Build Coastguard Worker            out.write(text)
246*8d67ca89SAndroid Build Coastguard Worker            cur_off += 1
247*8d67ca89SAndroid Build Coastguard Worker
248*8d67ca89SAndroid Build Coastguard Worker    has_map_file = False
249*8d67ca89SAndroid Build Coastguard Worker    if len(versions) > 0 and list(versions.keys()) != [None]:
250*8d67ca89SAndroid Build Coastguard Worker        has_map_file = True
251*8d67ca89SAndroid Build Coastguard Worker        with open(map_out_filename, 'w') as out:
252*8d67ca89SAndroid Build Coastguard Worker            if None in versions:
253*8d67ca89SAndroid Build Coastguard Worker                print(f'error: {out_filename} has both unversioned and versioned symbols')
254*8d67ca89SAndroid Build Coastguard Worker                print(versions.keys())
255*8d67ca89SAndroid Build Coastguard Worker                sys.exit(1)
256*8d67ca89SAndroid Build Coastguard Worker            for ver in sorted(versions.keys()):
257*8d67ca89SAndroid Build Coastguard Worker                assert ver is not None
258*8d67ca89SAndroid Build Coastguard Worker                out.write(f'{rot13(ver)} {{\n')
259*8d67ca89SAndroid Build Coastguard Worker                if len(versions[ver]) > 0:
260*8d67ca89SAndroid Build Coastguard Worker                    out.write('  global:\n')
261*8d67ca89SAndroid Build Coastguard Worker                    out.write(''.join(f'    {x};\n' for x in versions[ver]))
262*8d67ca89SAndroid Build Coastguard Worker                out.write(f'}};\n')
263*8d67ca89SAndroid Build Coastguard Worker
264*8d67ca89SAndroid Build Coastguard Worker    return has_map_file
265*8d67ca89SAndroid Build Coastguard Worker
266*8d67ca89SAndroid Build Coastguard Worker
267*8d67ca89SAndroid Build Coastguard Workerclass LibNames:
268*8d67ca89SAndroid Build Coastguard Worker    def __init__(self, root: LoadedLibrary):
269*8d67ca89SAndroid Build Coastguard Worker        self._root = root
270*8d67ca89SAndroid Build Coastguard Worker        self._names: Dict[LoadedLibrary, str] = {}
271*8d67ca89SAndroid Build Coastguard Worker        all_libs = [x for x in bfs_walk(root) if x is not root and x.soname not in kBionicSonames]
272*8d67ca89SAndroid Build Coastguard Worker        num_digits = math.ceil(math.log10(len(all_libs) + 1))
273*8d67ca89SAndroid Build Coastguard Worker        if g_obfuscate:
274*8d67ca89SAndroid Build Coastguard Worker            self._names = {x : f'{i:0{num_digits}}' for i, x in enumerate(all_libs)}
275*8d67ca89SAndroid Build Coastguard Worker        else:
276*8d67ca89SAndroid Build Coastguard Worker            self._names = {x : re.sub(r'\.so$', '', x.soname) for x in all_libs}
277*8d67ca89SAndroid Build Coastguard Worker
278*8d67ca89SAndroid Build Coastguard Worker    def name(self, lib: LoadedLibrary) -> str:
279*8d67ca89SAndroid Build Coastguard Worker        if lib is self._root:
280*8d67ca89SAndroid Build Coastguard Worker            return f'{g_benchmark_name}_main'
281*8d67ca89SAndroid Build Coastguard Worker        else:
282*8d67ca89SAndroid Build Coastguard Worker            return f'lib{g_benchmark_name}_{self._names[lib]}'
283*8d67ca89SAndroid Build Coastguard Worker
284*8d67ca89SAndroid Build Coastguard Worker
285*8d67ca89SAndroid Build Coastguard Worker# Generate a ninja file directly that builds the benchmark using a C compiler driver and ninja.
286*8d67ca89SAndroid Build Coastguard Worker# Using a driver directly can be faster than building with Soong, and it allows testing
287*8d67ca89SAndroid Build Coastguard Worker# configurations that Soong can't target, like musl.
288*8d67ca89SAndroid Build Coastguard Workerdef make_ninja_benchmark(root: LoadedLibrary, defs: Definitions, cc: str, out: Path) -> None:
289*8d67ca89SAndroid Build Coastguard Worker
290*8d67ca89SAndroid Build Coastguard Worker    lib_names = LibNames(root)
291*8d67ca89SAndroid Build Coastguard Worker
292*8d67ca89SAndroid Build Coastguard Worker    def lib_dso_name(lib: LoadedLibrary) -> str:
293*8d67ca89SAndroid Build Coastguard Worker        return lib_names.name(lib) + '.so'
294*8d67ca89SAndroid Build Coastguard Worker
295*8d67ca89SAndroid Build Coastguard Worker    ninja = open(out / 'build.ninja', 'w')
296*8d67ca89SAndroid Build Coastguard Worker    include_path = os.path.relpath(os.path.dirname(__file__) + '/../include', out)
297*8d67ca89SAndroid Build Coastguard Worker    common_flags = f"-Wl,-rpath-link,. -lm -I{include_path}"
298*8d67ca89SAndroid Build Coastguard Worker    ninja.write(textwrap.dedent(f'''\
299*8d67ca89SAndroid Build Coastguard Worker        rule exe
300*8d67ca89SAndroid Build Coastguard Worker            command = {cc} -fpie -pie $in -o $out {common_flags} $extra_args
301*8d67ca89SAndroid Build Coastguard Worker        rule dso
302*8d67ca89SAndroid Build Coastguard Worker            command = {cc} -fpic -shared $in -o $out -Wl,-soname,$out {common_flags} $extra_args
303*8d67ca89SAndroid Build Coastguard Worker    '''))
304*8d67ca89SAndroid Build Coastguard Worker
305*8d67ca89SAndroid Build Coastguard Worker    for lib in bfs_walk(root):
306*8d67ca89SAndroid Build Coastguard Worker        if lib.soname in kBionicSonames: continue
307*8d67ca89SAndroid Build Coastguard Worker
308*8d67ca89SAndroid Build Coastguard Worker        lib_base_name = lib_names.name(lib)
309*8d67ca89SAndroid Build Coastguard Worker        asm_name = lib_base_name + '.S'
310*8d67ca89SAndroid Build Coastguard Worker        map_name = lib_base_name + '.map'
311*8d67ca89SAndroid Build Coastguard Worker        asm_path = out / asm_name
312*8d67ca89SAndroid Build Coastguard Worker        map_path = out / map_name
313*8d67ca89SAndroid Build Coastguard Worker
314*8d67ca89SAndroid Build Coastguard Worker        has_map_file = make_asm_file(lib, lib is root, asm_path, map_path, defs)
315*8d67ca89SAndroid Build Coastguard Worker        needed = ' '.join([lib_dso_name(x) for x in lib.needed if x.soname not in kBionicSonames])
316*8d67ca89SAndroid Build Coastguard Worker
317*8d67ca89SAndroid Build Coastguard Worker        if lib is root:
318*8d67ca89SAndroid Build Coastguard Worker            ninja.write(f'build {lib_base_name}: exe {asm_name} {needed}\n')
319*8d67ca89SAndroid Build Coastguard Worker        else:
320*8d67ca89SAndroid Build Coastguard Worker            ninja.write(f'build {lib_dso_name(lib)}: dso {asm_name} {needed}\n')
321*8d67ca89SAndroid Build Coastguard Worker        if has_map_file:
322*8d67ca89SAndroid Build Coastguard Worker            ninja.write(f'    extra_args = -Wl,--version-script={map_name}\n')
323*8d67ca89SAndroid Build Coastguard Worker
324*8d67ca89SAndroid Build Coastguard Worker    ninja.close()
325*8d67ca89SAndroid Build Coastguard Worker
326*8d67ca89SAndroid Build Coastguard Worker    subprocess.run(['ninja', '-C', str(out), lib_names.name(root)], check=True)
327*8d67ca89SAndroid Build Coastguard Worker
328*8d67ca89SAndroid Build Coastguard Worker
329*8d67ca89SAndroid Build Coastguard Workerdef make_soong_benchmark(root: LoadedLibrary, defs: Definitions, out: Path) -> None:
330*8d67ca89SAndroid Build Coastguard Worker
331*8d67ca89SAndroid Build Coastguard Worker    lib_names = LibNames(root)
332*8d67ca89SAndroid Build Coastguard Worker
333*8d67ca89SAndroid Build Coastguard Worker    bp = open(out / 'Android.bp', 'w')
334*8d67ca89SAndroid Build Coastguard Worker    bp.write(f'// AUTO-GENERATED BY {os.path.basename(__file__)} -- do not edit\n')
335*8d67ca89SAndroid Build Coastguard Worker
336*8d67ca89SAndroid Build Coastguard Worker    bp.write(f'package {{ default_applicable_licenses: ["bionic_benchmarks_license"], }}\n')
337*8d67ca89SAndroid Build Coastguard Worker    bp.write(f'cc_defaults {{\n')
338*8d67ca89SAndroid Build Coastguard Worker    bp.write(f'    name: "{g_benchmark_name}_all_libs",\n')
339*8d67ca89SAndroid Build Coastguard Worker    bp.write(f'    runtime_libs: [\n')
340*8d67ca89SAndroid Build Coastguard Worker    for lib in bfs_walk(root):
341*8d67ca89SAndroid Build Coastguard Worker        if lib.soname in kBionicSonames: continue
342*8d67ca89SAndroid Build Coastguard Worker        if lib is root: continue
343*8d67ca89SAndroid Build Coastguard Worker        bp.write(f'        "{lib_names.name(lib)}",\n')
344*8d67ca89SAndroid Build Coastguard Worker    bp.write(f'    ],\n')
345*8d67ca89SAndroid Build Coastguard Worker    bp.write(f'}}\n')
346*8d67ca89SAndroid Build Coastguard Worker
347*8d67ca89SAndroid Build Coastguard Worker    for lib in bfs_walk(root):
348*8d67ca89SAndroid Build Coastguard Worker        if lib.soname in kBionicSonames: continue
349*8d67ca89SAndroid Build Coastguard Worker
350*8d67ca89SAndroid Build Coastguard Worker        lib_base_name = lib_names.name(lib)
351*8d67ca89SAndroid Build Coastguard Worker        asm_name = lib_base_name + '.S'
352*8d67ca89SAndroid Build Coastguard Worker        map_name = lib_base_name + '.map'
353*8d67ca89SAndroid Build Coastguard Worker        asm_path = out / asm_name
354*8d67ca89SAndroid Build Coastguard Worker        map_path = out / map_name
355*8d67ca89SAndroid Build Coastguard Worker
356*8d67ca89SAndroid Build Coastguard Worker        has_map_file = make_asm_file(lib, lib is root, asm_path, map_path, defs)
357*8d67ca89SAndroid Build Coastguard Worker
358*8d67ca89SAndroid Build Coastguard Worker        if lib is root:
359*8d67ca89SAndroid Build Coastguard Worker            bp.write(f'cc_binary {{\n')
360*8d67ca89SAndroid Build Coastguard Worker            bp.write(f'    defaults: ["{g_benchmark_name}_binary"],\n')
361*8d67ca89SAndroid Build Coastguard Worker        else:
362*8d67ca89SAndroid Build Coastguard Worker            bp.write(f'cc_test_library {{\n')
363*8d67ca89SAndroid Build Coastguard Worker            bp.write(f'    defaults: ["{g_benchmark_name}_library"],\n')
364*8d67ca89SAndroid Build Coastguard Worker        bp.write(f'    name: "{lib_base_name}",\n')
365*8d67ca89SAndroid Build Coastguard Worker        bp.write(f'    srcs: ["{asm_name}"],\n')
366*8d67ca89SAndroid Build Coastguard Worker        bp.write(f'    shared_libs: [\n')
367*8d67ca89SAndroid Build Coastguard Worker        for need in lib.needed:
368*8d67ca89SAndroid Build Coastguard Worker            if need.soname in kBionicSonames: continue
369*8d67ca89SAndroid Build Coastguard Worker            bp.write(f'        "{lib_names.name(need)}",\n')
370*8d67ca89SAndroid Build Coastguard Worker        bp.write(f'    ],\n')
371*8d67ca89SAndroid Build Coastguard Worker        if has_map_file:
372*8d67ca89SAndroid Build Coastguard Worker            bp.write(f'    version_script: "{map_name}",\n')
373*8d67ca89SAndroid Build Coastguard Worker        bp.write('}\n')
374*8d67ca89SAndroid Build Coastguard Worker
375*8d67ca89SAndroid Build Coastguard Worker    bp.close()
376*8d67ca89SAndroid Build Coastguard Worker
377*8d67ca89SAndroid Build Coastguard Worker
378*8d67ca89SAndroid Build Coastguard Workerdef main() -> None:
379*8d67ca89SAndroid Build Coastguard Worker    parser = argparse.ArgumentParser()
380*8d67ca89SAndroid Build Coastguard Worker    parser.add_argument('input', type=str)
381*8d67ca89SAndroid Build Coastguard Worker    parser.add_argument('out_dir', type=str)
382*8d67ca89SAndroid Build Coastguard Worker    parser.add_argument('--ninja', action='store_true',
383*8d67ca89SAndroid Build Coastguard Worker                        help='Generate a benchmark using a compiler and ninja rather than Soong')
384*8d67ca89SAndroid Build Coastguard Worker    parser.add_argument('--cc',
385*8d67ca89SAndroid Build Coastguard Worker                        help='For --ninja, a target-specific C clang driver and flags (e.g. "'
386*8d67ca89SAndroid Build Coastguard Worker                             '$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android29-clang'
387*8d67ca89SAndroid Build Coastguard Worker                             ' -fuse-ld=lld")')
388*8d67ca89SAndroid Build Coastguard Worker
389*8d67ca89SAndroid Build Coastguard Worker    args = parser.parse_args()
390*8d67ca89SAndroid Build Coastguard Worker
391*8d67ca89SAndroid Build Coastguard Worker    if args.ninja:
392*8d67ca89SAndroid Build Coastguard Worker        if args.cc is None: sys.exit('error: --cc required with --ninja')
393*8d67ca89SAndroid Build Coastguard Worker
394*8d67ca89SAndroid Build Coastguard Worker    out = Path(args.out_dir)
395*8d67ca89SAndroid Build Coastguard Worker    with open(Path(args.input)) as f:
396*8d67ca89SAndroid Build Coastguard Worker        root = json_to_elf_tree(json.load(f))
397*8d67ca89SAndroid Build Coastguard Worker    defs = build_symbol_index(root)
398*8d67ca89SAndroid Build Coastguard Worker    check_rels(root, defs)
399*8d67ca89SAndroid Build Coastguard Worker
400*8d67ca89SAndroid Build Coastguard Worker    if out.exists(): shutil.rmtree(out)
401*8d67ca89SAndroid Build Coastguard Worker    os.makedirs(str(out))
402*8d67ca89SAndroid Build Coastguard Worker
403*8d67ca89SAndroid Build Coastguard Worker    if args.ninja:
404*8d67ca89SAndroid Build Coastguard Worker        make_ninja_benchmark(root, defs, args.cc, out)
405*8d67ca89SAndroid Build Coastguard Worker    else:
406*8d67ca89SAndroid Build Coastguard Worker        make_soong_benchmark(root, defs, out)
407*8d67ca89SAndroid Build Coastguard Worker
408*8d67ca89SAndroid Build Coastguard Worker
409*8d67ca89SAndroid Build Coastguard Workerif __name__ == '__main__':
410*8d67ca89SAndroid Build Coastguard Worker    main()
411