[LoongArch] Implement MCTargetExpr::fixELFSymbolsInTLSFixups hook
[llvm-project.git] / llvm-libgcc / generate_version_script.py
blob98850d4f4a2de1f19cfbbd415c62df01b869a66c
1 #!/usr/bin/env python3
3 # Generates a version script for an architecture so that it can be incorporated
4 # into gcc_s.ver.
6 from collections import defaultdict
7 from itertools import chain
8 import argparse, subprocess, sys, os
11 def split_suffix(symbol):
12 """
13 Splits a symbol such as `__gttf2@GCC_3.0` into a triple representing its
14 function name (__gttf2), version name (GCC_3.0), and version number (300).
16 The version number acts as a priority. Since earlier versions are more
17 accessible and are likely to be used more, the lower the number is, the higher
18 its priortiy. A symbol that has a '@@' instead of '@' has been designated by
19 the linker as the default symbol, and is awarded a priority of -1.
20 """
21 if '@' not in symbol:
22 return None
23 data = [i for i in filter(lambda s: s, symbol.split('@'))]
24 _, version = data[-1].split('_')
25 version = version.replace('.', '')
26 priority = -1 if '@@' in symbol else int(version + '0' *
27 (3 - len(version)))
28 return data[0], data[1], priority
31 def invert_mapping(symbol_map):
32 """Transforms a map from Key->Value to Value->Key."""
33 store = defaultdict(list)
34 for symbol, (version, _) in symbol_map.items():
35 store[version].append(symbol)
36 result = []
37 for k, v in store.items():
38 v.sort()
39 result.append((k, v))
40 result.sort(key=lambda x: x[0])
41 return result
44 def intersection(llvm, gcc):
45 """
46 Finds the intersection between the symbols extracted from compiler-rt.a/libunwind.a
47 and libgcc_s.so.1.
48 """
49 common_symbols = {}
50 for i in gcc:
51 suffix_triple = split_suffix(i)
52 if not suffix_triple:
53 continue
55 symbol, version_name, version_number = suffix_triple
56 if symbol in llvm:
57 if symbol not in common_symbols:
58 common_symbols[symbol] = (version_name, version_number)
59 continue
60 if version_number < common_symbols[symbol][1]:
61 common_symbols[symbol] = (version_name, version_number)
62 return invert_mapping(common_symbols)
65 def find_function_names(path):
66 """
67 Runs readelf on a binary and reduces to only defined functions. Equivalent to
68 `llvm-readelf --wide ${path} | grep 'FUNC' | grep -v 'UND' | awk '{print $8}'`.
69 """
70 result = subprocess.run(args=['llvm-readelf', '-su', path],
71 capture_output=True)
73 if result.returncode != 0:
74 print(result.stderr.decode('utf-8'), file=sys.stderr)
75 sys.exit(1)
77 stdout = result.stdout.decode('utf-8')
78 stdout = filter(lambda x: 'FUNC' in x and 'UND' not in x,
79 stdout.split('\n'))
80 stdout = chain(
81 map(lambda x: filter(None, x), (i.split(' ') for i in stdout)))
83 return [list(i)[7] for i in stdout]
86 def to_file(versioned_symbols):
87 path = f'{os.path.dirname(os.path.realpath(__file__))}/new-gcc_s-symbols'
88 with open(path, 'w') as f:
89 f.write('Do not check this version script in: you should instead work '
90 'out which symbols are missing in `lib/gcc_s.ver` and then '
91 'integrate them into `lib/gcc_s.ver`. For more information, '
92 'please see `doc/LLVMLibgcc.rst`.\n')
93 for version, symbols in versioned_symbols:
94 f.write(f'{version} {{\n')
95 for i in symbols:
96 f.write(f' {i};\n')
97 f.write('};\n\n')
100 def read_args():
101 parser = argparse.ArgumentParser()
102 parser.add_argument('--compiler_rt',
103 type=str,
104 help='Path to `libclang_rt.builtins-${ARCH}.a`.',
105 required=True)
106 parser.add_argument('--libunwind',
107 type=str,
108 help='Path to `libunwind.a`.',
109 required=True)
110 parser.add_argument(
111 '--libgcc_s',
112 type=str,
113 help=
114 'Path to `libgcc_s.so.1`. Note that unlike the other two arguments, this is a dynamic library.',
115 required=True)
116 return parser.parse_args()
119 def main():
120 args = read_args()
121 llvm = find_function_names(args.compiler_rt) + find_function_names(
122 args.libunwind)
123 gcc = find_function_names(args.libgcc_s)
124 versioned_symbols = intersection(llvm, gcc)
125 # TODO(cjdb): work out a way to integrate new symbols in with the existing
126 # ones
127 to_file(versioned_symbols)
130 if __name__ == '__main__':
131 main()