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