xref: /third_party/mesa3d/bin/symbols-check.py (revision bf215546)
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