diff options
author | Eric Engestrom <[email protected]> | 2018-10-11 13:08:42 +0100 |
---|---|---|
committer | Eric Engestrom <[email protected]> | 2019-07-10 11:27:51 +0000 |
commit | 176f350fcf1d3024482c0307d2ce9eec836ad487 (patch) | |
tree | 3652d0ca78e15c454d630ba5d2b21a81331ed14f /bin/symbols-check.py | |
parent | 62362a4abb6f068472d12f884f12ba9a2cb2ca0d (diff) |
symbols-check: introduce new python script
I've re-written this in bash a couple times over the years, and then
I realised python is much more portable and already required by Mesa, so
we might as well make use of it.
I decided to still use the build system's NM instead of re-implementing
symbols extraction, to offload the complexity of keeping it compatible
with many systems (Linux, Unix, BSD, MacOS, etc.), especially when
cross-building.
This new script checks not only that nothing is exported when it
shouldn't be, but also that everything that should be exported is.
Sometimes, some symbols _can_ be exported but don't have to be, in which
case they can be prefixed with `(optional)`.
Signed-off-by: Eric Engestrom <[email protected]>
Reviewed-by Dylan Baker <[email protected]>
Reviewed-by: Emil Velikov <[email protected]>
Diffstat (limited to 'bin/symbols-check.py')
-rw-r--r-- | bin/symbols-check.py | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/bin/symbols-check.py b/bin/symbols-check.py new file mode 100644 index 00000000000..001a727a03e --- /dev/null +++ b/bin/symbols-check.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python + +import argparse +import subprocess +import os + +# This list contains symbols that _might_ be exported for some platforms +PLATFORM_SYMBOLS = [ + '__bss_end__', + '__bss_start__', + '__bss_start', + '__end__', + '_bss_end__', + '_edata', + '_end', + '_fini', + '_init', +] + + +def get_symbols(nm, lib): + ''' + List all the (non platform-specific) symbols exported by the library + ''' + symbols = [] + output = subprocess.check_output([nm, '--format=bsd', '-D', '--defined-only', lib], + stderr=open(os.devnull, 'w')).decode("ascii") + for line in output.splitlines(): + (_, _, symbol_name) = line.split() + symbols.append(symbol_name) + return symbols + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--symbols-file', + action='store', + required=True, + help='path to file containing symbols') + parser.add_argument('--lib', + action='store', + required=True, + help='path to library') + parser.add_argument('--nm', + action='store', + required=True, + help='path to binary (or name in $PATH)') + args = parser.parse_args() + + lib_symbols = get_symbols(args.nm, args.lib) + mandatory_symbols = [] + optional_symbols = [] + with open(args.symbols_file) as symbols_file: + qualifier_optional = '(optional)' + for line in symbols_file.readlines(): + + # Strip comments + line = line.split('#')[0] + line = line.strip() + if not line: + continue + + # Line format: + # [qualifier] symbol + qualifier = None + symbol = None + + fields = line.split() + if len(fields) == 1: + symbol = fields[0] + elif len(fields) == 2: + qualifier = fields[0] + symbol = fields[1] + else: + print(args.symbols_file + ': invalid format: ' + line) + exit(1) + + # The only supported qualifier is 'optional', which means the + # symbol doesn't have to be exported by the library + if qualifier and not qualifier == qualifier_optional: + print(args.symbols_file + ': invalid qualifier: ' + qualifier) + exit(1) + + if qualifier == qualifier_optional: + optional_symbols.append(symbol) + else: + mandatory_symbols.append(symbol) + + unknown_symbols = [] + for symbol in lib_symbols: + if symbol in mandatory_symbols: + continue + if symbol in optional_symbols: + continue + if symbol in PLATFORM_SYMBOLS: + continue + unknown_symbols.append(symbol) + + missing_symbols = [ + sym for sym in mandatory_symbols if sym not in lib_symbols + ] + + for symbol in unknown_symbols: + print(args.lib + ': unknown symbol exported: ' + symbol) + + for symbol in missing_symbols: + print(args.lib + ': missing symbol: ' + symbol) + + if unknown_symbols or missing_symbols: + exit(1) + exit(0) + + +if __name__ == '__main__': + main() |