forked from llvm/llvm-project
- Notifications
You must be signed in to change notification settings - Fork 339
/
Copy pathgenerate_version_script.py
executable file
·129 lines (106 loc) · 4.23 KB
/
generate_version_script.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#!/usr/bin/env python3
# Generates a version script for an architecture so that it can be incorporated
# into gcc_s.ver.
fromcollectionsimportdefaultdict
fromitertoolsimportchain
importargparse, subprocess, sys, os
defsplit_suffix(symbol):
"""
Splits a symbol such as `__gttf2@GCC_3.0` into a triple representing its
function name (__gttf2), version name (GCC_3.0), and version number (300).
The version number acts as a priority. Since earlier versions are more
accessible and are likely to be used more, the lower the number is, the higher
its priortiy. A symbol that has a '@@' instead of '@' has been designated by
the linker as the default symbol, and is awarded a priority of -1.
"""
if"@"notinsymbol:
returnNone
data= [iforiinfilter(lambdas: s, symbol.split("@"))]
_, version=data[-1].split("_")
version=version.replace(".", "")
priority=-1if"@@"insymbolelseint(version+"0"* (3-len(version)))
returndata[0], data[1], priority
definvert_mapping(symbol_map):
"""Transforms a map from Key->Value to Value->Key."""
store=defaultdict(list)
forsymbol, (version, _) insymbol_map.items():
store[version].append(symbol)
result= []
fork, vinstore.items():
v.sort()
result.append((k, v))
result.sort(key=lambdax: x[0])
returnresult
defintersection(llvm, gcc):
"""
Finds the intersection between the symbols extracted from compiler-rt.a/libunwind.a
and libgcc_s.so.1.
"""
common_symbols= {}
foriingcc:
suffix_triple=split_suffix(i)
ifnotsuffix_triple:
continue
symbol, version_name, version_number=suffix_triple
ifsymbolinllvm:
ifsymbolnotincommon_symbols:
common_symbols[symbol] = (version_name, version_number)
continue
ifversion_number<common_symbols[symbol][1]:
common_symbols[symbol] = (version_name, version_number)
returninvert_mapping(common_symbols)
deffind_function_names(path):
"""
Runs readelf on a binary and reduces to only defined functions. Equivalent to
`llvm-readelf --wide ${path} | grep 'FUNC' | grep -v 'UND' | awk '{print $8}'`.
"""
result=subprocess.run(args=["llvm-readelf", "-su", path], capture_output=True)
ifresult.returncode!=0:
print(result.stderr.decode("utf-8"), file=sys.stderr)
sys.exit(1)
stdout=result.stdout.decode("utf-8")
stdout=filter(lambdax: "FUNC"inxand"UND"notinx, stdout.split("\n"))
stdout=chain(map(lambdax: filter(None, x), (i.split(" ") foriinstdout)))
return [list(i)[7] foriinstdout]
defto_file(versioned_symbols):
path=f"{os.path.dirname(os.path.realpath(__file__))}/new-gcc_s-symbols"
withopen(path, "w") asf:
f.write(
"Do not check this version script in: you should instead work "
"out which symbols are missing in `lib/gcc_s.ver` and then "
"integrate them into `lib/gcc_s.ver`. For more information, "
"please see `doc/LLVMLibgcc.rst`.\n"
)
forversion, symbolsinversioned_symbols:
f.write(f"{version} {{\n")
foriinsymbols:
f.write(f" {i};\n")
f.write("};\n\n")
defread_args():
parser=argparse.ArgumentParser()
parser.add_argument(
"--compiler_rt",
type=str,
help="Path to `libclang_rt.builtins-${ARCH}.a`.",
required=True,
)
parser.add_argument(
"--libunwind", type=str, help="Path to `libunwind.a`.", required=True
)
parser.add_argument(
"--libgcc_s",
type=str,
help="Path to `libgcc_s.so.1`. Note that unlike the other two arguments, this is a dynamic library.",
required=True,
)
returnparser.parse_args()
defmain():
args=read_args()
llvm=find_function_names(args.compiler_rt) +find_function_names(args.libunwind)
gcc=find_function_names(args.libgcc_s)
versioned_symbols=intersection(llvm, gcc)
# TODO(cjdb): work out a way to integrate new symbols in with the existing
# ones
to_file(versioned_symbols)
if__name__=="__main__":
main()