3 # Generates a version script for an architecture so that it can be incorporated
6 from collections
import defaultdict
7 from itertools
import chain
8 import argparse
, subprocess
, sys
, os
11 def split_suffix(symbol
):
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.
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' *
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
)
37 for k
, v
in store
.items():
40 result
.sort(key
=lambda x
: x
[0])
44 def intersection(llvm
, gcc
):
46 Finds the intersection between the symbols extracted from compiler-rt.a/libunwind.a
51 suffix_triple
= split_suffix(i
)
55 symbol
, version_name
, version_number
= suffix_triple
57 if symbol
not in common_symbols
:
58 common_symbols
[symbol
] = (version_name
, version_number
)
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
):
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}'`.
70 result
= subprocess
.run(args
=['llvm-readelf', '-su', path
],
73 if result
.returncode
!= 0:
74 print(result
.stderr
.decode('utf-8'), file=sys
.stderr
)
77 stdout
= result
.stdout
.decode('utf-8')
78 stdout
= filter(lambda x
: 'FUNC' in x
and 'UND' not in x
,
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')
101 parser
= argparse
.ArgumentParser()
102 parser
.add_argument('--compiler_rt',
104 help='Path to `libclang_rt.builtins-${ARCH}.a`.',
106 parser
.add_argument('--libunwind',
108 help='Path to `libunwind.a`.',
114 'Path to `libgcc_s.so.1`. Note that unlike the other two arguments, this is a dynamic library.',
116 return parser
.parse_args()
121 llvm
= find_function_names(args
.compiler_rt
) + find_function_names(
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
127 to_file(versioned_symbols
)
130 if __name__
== '__main__':