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