1d722e3fbSopenharmony_ci#!/usr/bin/env python3
2d722e3fbSopenharmony_ci
3d722e3fbSopenharmony_ciimport argparse
4d722e3fbSopenharmony_ciimport os
5d722e3fbSopenharmony_ciimport platform
6d722e3fbSopenharmony_ciimport subprocess
7d722e3fbSopenharmony_ci
8d722e3fbSopenharmony_ci# This list contains symbols that _might_ be exported for some platforms
9d722e3fbSopenharmony_ciPLATFORM_SYMBOLS = [
10d722e3fbSopenharmony_ci    '__bss_end__',
11d722e3fbSopenharmony_ci    '__bss_start__',
12d722e3fbSopenharmony_ci    '__bss_start',
13d722e3fbSopenharmony_ci    '__end__',
14d722e3fbSopenharmony_ci    '_bss_end__',
15d722e3fbSopenharmony_ci    '_edata',
16d722e3fbSopenharmony_ci    '_end',
17d722e3fbSopenharmony_ci    '_fini',
18d722e3fbSopenharmony_ci    '_init',
19d722e3fbSopenharmony_ci]
20d722e3fbSopenharmony_ci
21d722e3fbSopenharmony_ci
22d722e3fbSopenharmony_cidef get_symbols(nm, lib):
23d722e3fbSopenharmony_ci    '''
24d722e3fbSopenharmony_ci    List all the (non platform-specific) symbols exported by the library
25d722e3fbSopenharmony_ci    '''
26d722e3fbSopenharmony_ci    symbols = []
27d722e3fbSopenharmony_ci    platform_name = platform.system()
28d722e3fbSopenharmony_ci    output = subprocess.check_output([nm, '-gP', lib],
29d722e3fbSopenharmony_ci                                     stderr=open(os.devnull, 'w')).decode("ascii")
30d722e3fbSopenharmony_ci    for line in output.splitlines():
31d722e3fbSopenharmony_ci        fields = line.split()
32d722e3fbSopenharmony_ci        if len(fields) == 2 or fields[1] == 'U':
33d722e3fbSopenharmony_ci            continue
34d722e3fbSopenharmony_ci        symbol_name = fields[0]
35d722e3fbSopenharmony_ci        if platform_name == 'Linux':
36d722e3fbSopenharmony_ci            if symbol_name in PLATFORM_SYMBOLS:
37d722e3fbSopenharmony_ci                continue
38d722e3fbSopenharmony_ci        elif platform_name == 'Darwin':
39d722e3fbSopenharmony_ci            assert symbol_name[0] == '_'
40d722e3fbSopenharmony_ci            symbol_name = symbol_name[1:]
41d722e3fbSopenharmony_ci        symbols.append(symbol_name)
42d722e3fbSopenharmony_ci
43d722e3fbSopenharmony_ci    return symbols
44d722e3fbSopenharmony_ci
45d722e3fbSopenharmony_ci
46d722e3fbSopenharmony_cidef main():
47d722e3fbSopenharmony_ci    parser = argparse.ArgumentParser()
48d722e3fbSopenharmony_ci    parser.add_argument('--symbols-file',
49d722e3fbSopenharmony_ci                        action='store',
50d722e3fbSopenharmony_ci                        required=True,
51d722e3fbSopenharmony_ci                        help='path to file containing symbols')
52d722e3fbSopenharmony_ci    parser.add_argument('--lib',
53d722e3fbSopenharmony_ci                        action='store',
54d722e3fbSopenharmony_ci                        required=True,
55d722e3fbSopenharmony_ci                        help='path to library')
56d722e3fbSopenharmony_ci    parser.add_argument('--nm',
57d722e3fbSopenharmony_ci                        action='store',
58d722e3fbSopenharmony_ci                        required=True,
59d722e3fbSopenharmony_ci                        help='path to binary (or name in $PATH)')
60d722e3fbSopenharmony_ci    args = parser.parse_args()
61d722e3fbSopenharmony_ci
62d722e3fbSopenharmony_ci    try:
63d722e3fbSopenharmony_ci        lib_symbols = get_symbols(args.nm, args.lib)
64d722e3fbSopenharmony_ci    except:
65d722e3fbSopenharmony_ci        # We can't run this test, but we haven't technically failed it either
66d722e3fbSopenharmony_ci        # Return the GNU "skip" error code
67d722e3fbSopenharmony_ci        exit(77)
68d722e3fbSopenharmony_ci    mandatory_symbols = []
69d722e3fbSopenharmony_ci    optional_symbols = []
70d722e3fbSopenharmony_ci    with open(args.symbols_file) as symbols_file:
71d722e3fbSopenharmony_ci        qualifier_optional = '(optional)'
72d722e3fbSopenharmony_ci        for line in symbols_file.readlines():
73d722e3fbSopenharmony_ci
74d722e3fbSopenharmony_ci            # Strip comments
75d722e3fbSopenharmony_ci            line = line.split('#')[0]
76d722e3fbSopenharmony_ci            line = line.strip()
77d722e3fbSopenharmony_ci            if not line:
78d722e3fbSopenharmony_ci                continue
79d722e3fbSopenharmony_ci
80d722e3fbSopenharmony_ci            # Line format:
81d722e3fbSopenharmony_ci            # [qualifier] symbol
82d722e3fbSopenharmony_ci            qualifier = None
83d722e3fbSopenharmony_ci            symbol = None
84d722e3fbSopenharmony_ci
85d722e3fbSopenharmony_ci            fields = line.split()
86d722e3fbSopenharmony_ci            if len(fields) == 1:
87d722e3fbSopenharmony_ci                symbol = fields[0]
88d722e3fbSopenharmony_ci            elif len(fields) == 2:
89d722e3fbSopenharmony_ci                qualifier = fields[0]
90d722e3fbSopenharmony_ci                symbol = fields[1]
91d722e3fbSopenharmony_ci            else:
92d722e3fbSopenharmony_ci                print(args.symbols_file + ': invalid format: ' + line)
93d722e3fbSopenharmony_ci                exit(1)
94d722e3fbSopenharmony_ci
95d722e3fbSopenharmony_ci            # The only supported qualifier is 'optional', which means the
96d722e3fbSopenharmony_ci            # symbol doesn't have to be exported by the library
97d722e3fbSopenharmony_ci            if qualifier and not qualifier == qualifier_optional:
98d722e3fbSopenharmony_ci                print(args.symbols_file + ': invalid qualifier: ' + qualifier)
99d722e3fbSopenharmony_ci                exit(1)
100d722e3fbSopenharmony_ci
101d722e3fbSopenharmony_ci            if qualifier == qualifier_optional:
102d722e3fbSopenharmony_ci                optional_symbols.append(symbol)
103d722e3fbSopenharmony_ci            else:
104d722e3fbSopenharmony_ci                mandatory_symbols.append(symbol)
105d722e3fbSopenharmony_ci
106d722e3fbSopenharmony_ci    unknown_symbols = []
107d722e3fbSopenharmony_ci    for symbol in lib_symbols:
108d722e3fbSopenharmony_ci        if symbol in mandatory_symbols:
109d722e3fbSopenharmony_ci            continue
110d722e3fbSopenharmony_ci        if symbol in optional_symbols:
111d722e3fbSopenharmony_ci            continue
112d722e3fbSopenharmony_ci        unknown_symbols.append(symbol)
113d722e3fbSopenharmony_ci
114d722e3fbSopenharmony_ci    missing_symbols = [
115d722e3fbSopenharmony_ci        sym for sym in mandatory_symbols if sym not in lib_symbols
116d722e3fbSopenharmony_ci    ]
117d722e3fbSopenharmony_ci
118d722e3fbSopenharmony_ci    for symbol in unknown_symbols:
119d722e3fbSopenharmony_ci        print(args.lib + ': unknown symbol exported: ' + symbol)
120d722e3fbSopenharmony_ci
121d722e3fbSopenharmony_ci    for symbol in missing_symbols:
122d722e3fbSopenharmony_ci        print(args.lib + ': missing symbol: ' + symbol)
123d722e3fbSopenharmony_ci
124d722e3fbSopenharmony_ci    if unknown_symbols or missing_symbols:
125d722e3fbSopenharmony_ci        exit(1)
126d722e3fbSopenharmony_ci    exit(0)
127d722e3fbSopenharmony_ci
128d722e3fbSopenharmony_ci
129d722e3fbSopenharmony_ciif __name__ == '__main__':
130d722e3fbSopenharmony_ci    main()
131