Bitcoin Core 29.99.0
P2P Digital Currency
symbol-check.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2"""Check that a libsecp256k1 shared library exports only expected symbols.
3
4Usage examples:
5 - When building with Autotools:
6 ./tools/symbol-check.py .libs/libsecp256k1.so
7 ./tools/symbol-check.py .libs/libsecp256k1-<V>.dll
8 ./tools/symbol-check.py .libs/libsecp256k1.dylib
9
10 - When building with CMake:
11 ./tools/symbol-check.py build/lib/libsecp256k1.so
12 ./tools/symbol-check.py build/bin/libsecp256k1-<V>.dll
13 ./tools/symbol-check.py build/lib/libsecp256k1.dylib"""
14
15import re
16import sys
17import subprocess
18
19import lief
20
21
22class UnexpectedExport(RuntimeError):
23 pass
24
25
26def get_exported_exports(library) -> list[str]:
27 """Adapter function to get exported symbols based on the library format."""
28 if library.format == lief.Binary.FORMATS.ELF:
29 return [symbol.name for symbol in library.exported_symbols]
30 elif library.format == lief.Binary.FORMATS.PE:
31 return [entry.name for entry in library.get_export().entries]
32 elif library.format == lief.Binary.FORMATS.MACHO:
33 return [symbol.name[1:] for symbol in library.exported_symbols]
34 raise NotImplementedError(f"Unsupported format: {library.format}")
35
36
37def grep_expected_symbols() -> list[str]:
38 """Guess the list of expected exported symbols from the source code."""
39 grep_output = subprocess.check_output(
40 ["git", "grep", r"^\s*SECP256K1_API", "--", "include"],
41 universal_newlines=True,
42 encoding="utf-8"
43 )
44 lines = grep_output.split("\n")
45 pattern = re.compile(r'\bsecp256k1_\w+')
46 exported: list[str] = [pattern.findall(line)[-1] for line in lines if line.strip()]
47 return exported
48
49
50def check_symbols(library, expected_exports) -> None:
51 """Check that the library exports only the expected symbols."""
52 actual_exports = get_exported_exports(library)
53 unexpected_exports = set(actual_exports) - set(expected_exports)
54 if unexpected_exports != set():
55 raise UnexpectedExport(f"Unexpected exported symbols: {unexpected_exports}")
56
57def main():
58 if len(sys.argv) != 2:
59 print(__doc__)
60 return 1
61 library = lief.parse(sys.argv[1])
62 expected_exports = grep_expected_symbols()
63 try:
64 check_symbols(library, expected_exports)
65 except UnexpectedExport as e:
66 print(f"{sys.argv[0]}: In {sys.argv[1]}: {e}")
67 return 1
68 return 0
69
70
71if __name__ == "__main__":
72 sys.exit(main())
list[str] get_exported_exports(library)
Definition: symbol-check.py:26
list[str] grep_expected_symbols()
Definition: symbol-check.py:37
None check_symbols(library, expected_exports)
Definition: symbol-check.py:50