Bitcoin Core  31.0.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 
4 Usage 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 
15 import re
16 import sys
17 import subprocess
18 
19 import lief
20 
21 
22 class UnexpectedExport(RuntimeError):
23  pass
24 
25 
26 def 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 
37 def 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 
50 def 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 
57 def 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 
71 if __name__ == "__main__":
72  sys.exit(main())
def grep_expected_symbols()
Definition: symbol-check.py:37
def check_symbols(library, expected_exports)
Definition: symbol-check.py:50
def get_exported_exports(library)
Definition: symbol-check.py:26