1bf215546Sopenharmony_ci#!/usr/bin/env python3 2bf215546Sopenharmony_ci 3bf215546Sopenharmony_ciimport argparse 4bf215546Sopenharmony_ciimport os 5bf215546Sopenharmony_ciimport platform 6bf215546Sopenharmony_ciimport subprocess 7bf215546Sopenharmony_ci 8bf215546Sopenharmony_ci# This list contains symbols that _might_ be exported for some platforms 9bf215546Sopenharmony_ciPLATFORM_SYMBOLS = [ 10bf215546Sopenharmony_ci '__bss_end__', 11bf215546Sopenharmony_ci '__bss_start__', 12bf215546Sopenharmony_ci '__bss_start', 13bf215546Sopenharmony_ci '__cxa_guard_abort', 14bf215546Sopenharmony_ci '__cxa_guard_acquire', 15bf215546Sopenharmony_ci '__cxa_guard_release', 16bf215546Sopenharmony_ci '__end__', 17bf215546Sopenharmony_ci '__odr_asan._glapi_Context', 18bf215546Sopenharmony_ci '__odr_asan._glapi_Dispatch', 19bf215546Sopenharmony_ci '_bss_end__', 20bf215546Sopenharmony_ci '_edata', 21bf215546Sopenharmony_ci '_end', 22bf215546Sopenharmony_ci '_fini', 23bf215546Sopenharmony_ci '_init', 24bf215546Sopenharmony_ci '_fbss', 25bf215546Sopenharmony_ci '_fdata', 26bf215546Sopenharmony_ci '_ftext', 27bf215546Sopenharmony_ci] 28bf215546Sopenharmony_ci 29bf215546Sopenharmony_cidef get_symbols_nm(nm, lib): 30bf215546Sopenharmony_ci ''' 31bf215546Sopenharmony_ci List all the (non platform-specific) symbols exported by the library 32bf215546Sopenharmony_ci using `nm` 33bf215546Sopenharmony_ci ''' 34bf215546Sopenharmony_ci symbols = [] 35bf215546Sopenharmony_ci platform_name = platform.system() 36bf215546Sopenharmony_ci output = subprocess.check_output([nm, '-gP', lib], 37bf215546Sopenharmony_ci stderr=open(os.devnull, 'w')).decode("ascii") 38bf215546Sopenharmony_ci for line in output.splitlines(): 39bf215546Sopenharmony_ci fields = line.split() 40bf215546Sopenharmony_ci if len(fields) == 2 or fields[1] == 'U': 41bf215546Sopenharmony_ci continue 42bf215546Sopenharmony_ci symbol_name = fields[0] 43bf215546Sopenharmony_ci if platform_name == 'Linux': 44bf215546Sopenharmony_ci if symbol_name in PLATFORM_SYMBOLS: 45bf215546Sopenharmony_ci continue 46bf215546Sopenharmony_ci elif platform_name == 'Darwin': 47bf215546Sopenharmony_ci assert symbol_name[0] == '_' 48bf215546Sopenharmony_ci symbol_name = symbol_name[1:] 49bf215546Sopenharmony_ci symbols.append(symbol_name) 50bf215546Sopenharmony_ci return symbols 51bf215546Sopenharmony_ci 52bf215546Sopenharmony_ci 53bf215546Sopenharmony_cidef get_symbols_dumpbin(dumpbin, lib): 54bf215546Sopenharmony_ci ''' 55bf215546Sopenharmony_ci List all the (non platform-specific) symbols exported by the library 56bf215546Sopenharmony_ci using `dumpbin` 57bf215546Sopenharmony_ci ''' 58bf215546Sopenharmony_ci symbols = [] 59bf215546Sopenharmony_ci output = subprocess.check_output([dumpbin, '/exports', lib], 60bf215546Sopenharmony_ci stderr=open(os.devnull, 'w')).decode("ascii") 61bf215546Sopenharmony_ci for line in output.splitlines(): 62bf215546Sopenharmony_ci fields = line.split() 63bf215546Sopenharmony_ci # The lines with the symbols are made of at least 4 columns; see details below 64bf215546Sopenharmony_ci if len(fields) < 4: 65bf215546Sopenharmony_ci continue 66bf215546Sopenharmony_ci try: 67bf215546Sopenharmony_ci # Making sure the first 3 columns are a dec counter, a hex counter 68bf215546Sopenharmony_ci # and a hex address 69bf215546Sopenharmony_ci _ = int(fields[0], 10) 70bf215546Sopenharmony_ci _ = int(fields[1], 16) 71bf215546Sopenharmony_ci _ = int(fields[2], 16) 72bf215546Sopenharmony_ci except ValueError: 73bf215546Sopenharmony_ci continue 74bf215546Sopenharmony_ci symbol_name = fields[3] 75bf215546Sopenharmony_ci # De-mangle symbols 76bf215546Sopenharmony_ci if symbol_name[0] == '_' and '@' in symbol_name: 77bf215546Sopenharmony_ci symbol_name = symbol_name[1:].split('@')[0] 78bf215546Sopenharmony_ci symbols.append(symbol_name) 79bf215546Sopenharmony_ci return symbols 80bf215546Sopenharmony_ci 81bf215546Sopenharmony_ci 82bf215546Sopenharmony_cidef main(): 83bf215546Sopenharmony_ci parser = argparse.ArgumentParser() 84bf215546Sopenharmony_ci parser.add_argument('--symbols-file', 85bf215546Sopenharmony_ci action='store', 86bf215546Sopenharmony_ci required=True, 87bf215546Sopenharmony_ci help='path to file containing symbols') 88bf215546Sopenharmony_ci parser.add_argument('--lib', 89bf215546Sopenharmony_ci action='store', 90bf215546Sopenharmony_ci required=True, 91bf215546Sopenharmony_ci help='path to library') 92bf215546Sopenharmony_ci parser.add_argument('--nm', 93bf215546Sopenharmony_ci action='store', 94bf215546Sopenharmony_ci help='path to binary (or name in $PATH)') 95bf215546Sopenharmony_ci parser.add_argument('--dumpbin', 96bf215546Sopenharmony_ci action='store', 97bf215546Sopenharmony_ci help='path to binary (or name in $PATH)') 98bf215546Sopenharmony_ci parser.add_argument('--ignore-symbol', 99bf215546Sopenharmony_ci action='append', 100bf215546Sopenharmony_ci help='do not process this symbol') 101bf215546Sopenharmony_ci args = parser.parse_args() 102bf215546Sopenharmony_ci 103bf215546Sopenharmony_ci try: 104bf215546Sopenharmony_ci if platform.system() == 'Windows': 105bf215546Sopenharmony_ci if not args.dumpbin: 106bf215546Sopenharmony_ci parser.error('--dumpbin is mandatory') 107bf215546Sopenharmony_ci lib_symbols = get_symbols_dumpbin(args.dumpbin, args.lib) 108bf215546Sopenharmony_ci else: 109bf215546Sopenharmony_ci if not args.nm: 110bf215546Sopenharmony_ci parser.error('--nm is mandatory') 111bf215546Sopenharmony_ci lib_symbols = get_symbols_nm(args.nm, args.lib) 112bf215546Sopenharmony_ci except: 113bf215546Sopenharmony_ci # We can't run this test, but we haven't technically failed it either 114bf215546Sopenharmony_ci # Return the GNU "skip" error code 115bf215546Sopenharmony_ci exit(77) 116bf215546Sopenharmony_ci mandatory_symbols = [] 117bf215546Sopenharmony_ci optional_symbols = [] 118bf215546Sopenharmony_ci with open(args.symbols_file) as symbols_file: 119bf215546Sopenharmony_ci qualifier_optional = '(optional)' 120bf215546Sopenharmony_ci for line in symbols_file.readlines(): 121bf215546Sopenharmony_ci 122bf215546Sopenharmony_ci # Strip comments 123bf215546Sopenharmony_ci line = line.split('#')[0] 124bf215546Sopenharmony_ci line = line.strip() 125bf215546Sopenharmony_ci if not line: 126bf215546Sopenharmony_ci continue 127bf215546Sopenharmony_ci 128bf215546Sopenharmony_ci # Line format: 129bf215546Sopenharmony_ci # [qualifier] symbol 130bf215546Sopenharmony_ci qualifier = None 131bf215546Sopenharmony_ci symbol = None 132bf215546Sopenharmony_ci 133bf215546Sopenharmony_ci fields = line.split() 134bf215546Sopenharmony_ci if len(fields) == 1: 135bf215546Sopenharmony_ci symbol = fields[0] 136bf215546Sopenharmony_ci elif len(fields) == 2: 137bf215546Sopenharmony_ci qualifier = fields[0] 138bf215546Sopenharmony_ci symbol = fields[1] 139bf215546Sopenharmony_ci else: 140bf215546Sopenharmony_ci print(args.symbols_file + ': invalid format: ' + line) 141bf215546Sopenharmony_ci exit(1) 142bf215546Sopenharmony_ci 143bf215546Sopenharmony_ci # The only supported qualifier is 'optional', which means the 144bf215546Sopenharmony_ci # symbol doesn't have to be exported by the library 145bf215546Sopenharmony_ci if qualifier and not qualifier == qualifier_optional: 146bf215546Sopenharmony_ci print(args.symbols_file + ': invalid qualifier: ' + qualifier) 147bf215546Sopenharmony_ci exit(1) 148bf215546Sopenharmony_ci 149bf215546Sopenharmony_ci if qualifier == qualifier_optional: 150bf215546Sopenharmony_ci optional_symbols.append(symbol) 151bf215546Sopenharmony_ci else: 152bf215546Sopenharmony_ci mandatory_symbols.append(symbol) 153bf215546Sopenharmony_ci 154bf215546Sopenharmony_ci unknown_symbols = [] 155bf215546Sopenharmony_ci for symbol in lib_symbols: 156bf215546Sopenharmony_ci if symbol in mandatory_symbols: 157bf215546Sopenharmony_ci continue 158bf215546Sopenharmony_ci if symbol in optional_symbols: 159bf215546Sopenharmony_ci continue 160bf215546Sopenharmony_ci if args.ignore_symbol and symbol in args.ignore_symbol: 161bf215546Sopenharmony_ci continue 162bf215546Sopenharmony_ci if symbol[:2] == '_Z': 163bf215546Sopenharmony_ci # As ajax found out, the compiler intentionally exports symbols 164bf215546Sopenharmony_ci # that we explicitely asked it not to export, and we can't do 165bf215546Sopenharmony_ci # anything about it: 166bf215546Sopenharmony_ci # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=36022#c4 167bf215546Sopenharmony_ci continue 168bf215546Sopenharmony_ci unknown_symbols.append(symbol) 169bf215546Sopenharmony_ci 170bf215546Sopenharmony_ci missing_symbols = [ 171bf215546Sopenharmony_ci sym for sym in mandatory_symbols if sym not in lib_symbols 172bf215546Sopenharmony_ci ] 173bf215546Sopenharmony_ci 174bf215546Sopenharmony_ci for symbol in unknown_symbols: 175bf215546Sopenharmony_ci print(args.lib + ': unknown symbol exported: ' + symbol) 176bf215546Sopenharmony_ci 177bf215546Sopenharmony_ci for symbol in missing_symbols: 178bf215546Sopenharmony_ci print(args.lib + ': missing symbol: ' + symbol) 179bf215546Sopenharmony_ci 180bf215546Sopenharmony_ci if unknown_symbols or missing_symbols: 181bf215546Sopenharmony_ci exit(1) 182bf215546Sopenharmony_ci exit(0) 183bf215546Sopenharmony_ci 184bf215546Sopenharmony_ci 185bf215546Sopenharmony_ciif __name__ == '__main__': 186bf215546Sopenharmony_ci main() 187