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" * (3 - len(version
)))
27 return data
[0], data
[1], priority
30 def invert_mapping(symbol_map
):
31 """Transforms a map from Key->Value to Value->Key."""
32 store
= defaultdict(list)
33 for symbol
, (version
, _
) in symbol_map
.items():
34 store
[version
].append(symbol
)
36 for k
, v
in store
.items():
39 result
.sort(key
=lambda x
: x
[0])
43 def intersection(llvm
, gcc
):
45 Finds the intersection between the symbols extracted from compiler-rt.a/libunwind.a
50 suffix_triple
= split_suffix(i
)
54 symbol
, version_name
, version_number
= suffix_triple
56 if symbol
not in common_symbols
:
57 common_symbols
[symbol
] = (version_name
, version_number
)
59 if version_number
< common_symbols
[symbol
][1]:
60 common_symbols
[symbol
] = (version_name
, version_number
)
61 return invert_mapping(common_symbols
)
64 def find_function_names(path
):
66 Runs readelf on a binary and reduces to only defined functions. Equivalent to
67 `llvm-readelf --wide ${path} | grep 'FUNC' | grep -v 'UND' | awk '{print $8}'`.
69 result
= subprocess
.run(args
=["llvm-readelf", "-su", path
], capture_output
=True)
71 if result
.returncode
!= 0:
72 print(result
.stderr
.decode("utf-8"), file=sys
.stderr
)
75 stdout
= result
.stdout
.decode("utf-8")
76 stdout
= filter(lambda x
: "FUNC" in x
and "UND" not in x
, stdout
.split("\n"))
77 stdout
= chain(map(lambda x
: filter(None, x
), (i
.split(" ") for i
in stdout
)))
79 return [list(i
)[7] for i
in stdout
]
82 def to_file(versioned_symbols
):
83 path
= f
"{os.path.dirname(os.path.realpath(__file__))}/new-gcc_s-symbols"
84 with
open(path
, "w") as f
:
86 "Do not check this version script in: you should instead work "
87 "out which symbols are missing in `lib/gcc_s.ver` and then "
88 "integrate them into `lib/gcc_s.ver`. For more information, "
89 "please see `doc/LLVMLibgcc.rst`.\n"
91 for version
, symbols
in versioned_symbols
:
92 f
.write(f
"{version} {{\n")
99 parser
= argparse
.ArgumentParser()
103 help="Path to `libclang_rt.builtins-${ARCH}.a`.",
107 "--libunwind", type=str, help="Path to `libunwind.a`.", required
=True
112 help="Path to `libgcc_s.so.1`. Note that unlike the other two arguments, this is a dynamic library.",
115 return parser
.parse_args()
120 llvm
= find_function_names(args
.compiler_rt
) + find_function_names(args
.libunwind
)
121 gcc
= find_function_names(args
.libgcc_s
)
122 versioned_symbols
= intersection(llvm
, gcc
)
123 # TODO(cjdb): work out a way to integrate new symbols in with the existing
125 to_file(versioned_symbols
)
128 if __name__
== "__main__":