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