summaryrefslogtreecommitdiffstats
path: root/bin/symbols-check.py
diff options
context:
space:
mode:
authorEric Engestrom <[email protected]>2018-10-11 13:08:42 +0100
committerEric Engestrom <[email protected]>2019-07-10 11:27:51 +0000
commit176f350fcf1d3024482c0307d2ce9eec836ad487 (patch)
tree3652d0ca78e15c454d630ba5d2b21a81331ed14f /bin/symbols-check.py
parent62362a4abb6f068472d12f884f12ba9a2cb2ca0d (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.py115
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()