19bd392adSmrg#!/usr/bin/env python3 29bd392adSmrg 39bd392adSmrgimport argparse 49bd392adSmrgimport os 59bd392adSmrgimport platform 69bd392adSmrgimport subprocess 79bd392adSmrg 89bd392adSmrg# This list contains symbols that _might_ be exported for some platforms 99bd392adSmrgPLATFORM_SYMBOLS = [ 10bbff01ceSmrg '_GLOBAL_OFFSET_TABLE_', 119bd392adSmrg '__bss_end__', 129bd392adSmrg '__bss_start__', 139bd392adSmrg '__bss_start', 149bd392adSmrg '__end__', 159bd392adSmrg '_bss_end__', 169bd392adSmrg '_edata', 179bd392adSmrg '_end', 189bd392adSmrg '_fini', 199bd392adSmrg '_init', 20bbff01ceSmrg '_fbss', 21bbff01ceSmrg '_fdata', 22bbff01ceSmrg '_ftext', 239bd392adSmrg] 249bd392adSmrg 259bd392adSmrg 269bd392adSmrgdef get_symbols(nm, lib): 279bd392adSmrg ''' 289bd392adSmrg List all the (non platform-specific) symbols exported by the library 299bd392adSmrg ''' 309bd392adSmrg symbols = [] 319bd392adSmrg platform_name = platform.system() 329bd392adSmrg output = subprocess.check_output([nm, '-gP', lib], 339bd392adSmrg stderr=open(os.devnull, 'w')).decode("ascii") 349bd392adSmrg for line in output.splitlines(): 359bd392adSmrg fields = line.split() 369bd392adSmrg if len(fields) == 2 or fields[1] == 'U': 379bd392adSmrg continue 389bd392adSmrg symbol_name = fields[0] 399bd392adSmrg if platform_name == 'Linux': 409bd392adSmrg if symbol_name in PLATFORM_SYMBOLS: 419bd392adSmrg continue 429bd392adSmrg elif platform_name == 'Darwin': 439bd392adSmrg assert symbol_name[0] == '_' 449bd392adSmrg symbol_name = symbol_name[1:] 459bd392adSmrg symbols.append(symbol_name) 469bd392adSmrg 479bd392adSmrg return symbols 489bd392adSmrg 499bd392adSmrg 509bd392adSmrgdef main(): 519bd392adSmrg parser = argparse.ArgumentParser() 529bd392adSmrg parser.add_argument('--symbols-file', 539bd392adSmrg action='store', 549bd392adSmrg required=True, 559bd392adSmrg help='path to file containing symbols') 569bd392adSmrg parser.add_argument('--lib', 579bd392adSmrg action='store', 589bd392adSmrg required=True, 599bd392adSmrg help='path to library') 609bd392adSmrg parser.add_argument('--nm', 619bd392adSmrg action='store', 629bd392adSmrg required=True, 639bd392adSmrg help='path to binary (or name in $PATH)') 649bd392adSmrg args = parser.parse_args() 659bd392adSmrg 669bd392adSmrg try: 679bd392adSmrg lib_symbols = get_symbols(args.nm, args.lib) 689bd392adSmrg except: 699bd392adSmrg # We can't run this test, but we haven't technically failed it either 709bd392adSmrg # Return the GNU "skip" error code 719bd392adSmrg exit(77) 729bd392adSmrg mandatory_symbols = [] 739bd392adSmrg optional_symbols = [] 749bd392adSmrg with open(args.symbols_file) as symbols_file: 759bd392adSmrg qualifier_optional = '(optional)' 769bd392adSmrg for line in symbols_file.readlines(): 779bd392adSmrg 789bd392adSmrg # Strip comments 799bd392adSmrg line = line.split('#')[0] 809bd392adSmrg line = line.strip() 819bd392adSmrg if not line: 829bd392adSmrg continue 839bd392adSmrg 849bd392adSmrg # Line format: 859bd392adSmrg # [qualifier] symbol 869bd392adSmrg qualifier = None 879bd392adSmrg symbol = None 889bd392adSmrg 899bd392adSmrg fields = line.split() 909bd392adSmrg if len(fields) == 1: 919bd392adSmrg symbol = fields[0] 929bd392adSmrg elif len(fields) == 2: 939bd392adSmrg qualifier = fields[0] 949bd392adSmrg symbol = fields[1] 959bd392adSmrg else: 969bd392adSmrg print(args.symbols_file + ': invalid format: ' + line) 979bd392adSmrg exit(1) 989bd392adSmrg 999bd392adSmrg # The only supported qualifier is 'optional', which means the 1009bd392adSmrg # symbol doesn't have to be exported by the library 1019bd392adSmrg if qualifier and not qualifier == qualifier_optional: 1029bd392adSmrg print(args.symbols_file + ': invalid qualifier: ' + qualifier) 1039bd392adSmrg exit(1) 1049bd392adSmrg 1059bd392adSmrg if qualifier == qualifier_optional: 1069bd392adSmrg optional_symbols.append(symbol) 1079bd392adSmrg else: 1089bd392adSmrg mandatory_symbols.append(symbol) 1099bd392adSmrg 1109bd392adSmrg unknown_symbols = [] 1119bd392adSmrg for symbol in lib_symbols: 1129bd392adSmrg if symbol in mandatory_symbols: 1139bd392adSmrg continue 1149bd392adSmrg if symbol in optional_symbols: 1159bd392adSmrg continue 1169bd392adSmrg unknown_symbols.append(symbol) 1179bd392adSmrg 1189bd392adSmrg missing_symbols = [ 1199bd392adSmrg sym for sym in mandatory_symbols if sym not in lib_symbols 1209bd392adSmrg ] 1219bd392adSmrg 1229bd392adSmrg for symbol in unknown_symbols: 1239bd392adSmrg print(args.lib + ': unknown symbol exported: ' + symbol) 1249bd392adSmrg 1259bd392adSmrg for symbol in missing_symbols: 1269bd392adSmrg print(args.lib + ': missing symbol: ' + symbol) 1279bd392adSmrg 1289bd392adSmrg if unknown_symbols or missing_symbols: 1299bd392adSmrg exit(1) 1309bd392adSmrg exit(0) 1319bd392adSmrg 1329bd392adSmrg 1339bd392adSmrgif __name__ == '__main__': 1349bd392adSmrg main() 135