Skip to content

Commit cc7933d

Browse files
committed
Add contrib/symbol-check.py
1 parent 9a8d65f commit cc7933d

File tree

1 file changed

+119
-0
lines changed

1 file changed

+119
-0
lines changed

tools/symbol-check.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) 2014 Wladimir J. van der Laan
3+
# Distributed under the MIT software license, see the accompanying
4+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
'''
6+
A script to check that a secp256k1 shared library
7+
exports only expected symbols.
8+
9+
Example usage:
10+
11+
./tools/symbol-check.py .libs/libsecp256k1.so.0.0.0
12+
or
13+
./tools/symbol-check.py .libs/libsecp256k1-0.dll
14+
or
15+
./tools/symbol-check.py .libs/libsecp256k1.0.dylib
16+
'''
17+
import re
18+
import sys
19+
import subprocess
20+
from typing import List
21+
22+
import lief #type:ignore
23+
24+
def grep_exported_symbols() -> List[str]:
25+
grep_output = subprocess.check_output(["git", "grep", "^SECP256K1_API", "--", "include"], universal_newlines=True, encoding="utf8")
26+
lines = grep_output.split("\n")
27+
exports: List[str] = []
28+
pattern = re.compile(r'\bsecp256k1_\w+')
29+
for line in lines:
30+
if line.strip():
31+
function_name = pattern.findall(line)[-1]
32+
exports.append(function_name)
33+
return exports
34+
35+
def check_ELF_exported_symbols(library, expected_exports) -> bool:
36+
ok: bool = True
37+
elf_lib: lief.ELF.Binary = library.concrete
38+
39+
for symbol in elf_lib.exported_symbols:
40+
name: str = symbol.name
41+
if name in expected_exports:
42+
continue
43+
print(f'{filename}: export of symbol {name} not expected')
44+
ok = False
45+
return ok
46+
47+
def check_PE_exported_functions(library, expected_exports) -> bool:
48+
ok: bool = True
49+
pe_lib: lief.PE.Binary = library.concrete
50+
51+
for function in pe_lib.exported_functions:
52+
name: str = function.name
53+
if name in expected_exports:
54+
continue
55+
print(f'{filename}: export of function {name} not expected')
56+
ok = False
57+
return ok
58+
59+
def check_MACHO_exported_functions(library, expected_exports) -> bool:
60+
ok: bool = True
61+
macho_lib: lief.MACHO.Binary = library.concrete
62+
63+
for function in macho_lib.exported_functions:
64+
name: str = function.name[1:]
65+
if name in expected_exports:
66+
continue
67+
print(f'{filename}: export of function {name} not expected')
68+
ok = False
69+
return ok
70+
71+
CHECKS = {
72+
lief.EXE_FORMATS.ELF: [
73+
('EXPORTED_SYMBOLS', check_ELF_exported_symbols),
74+
],
75+
lief.EXE_FORMATS.PE: [
76+
('EXPORTED_FUNCTIONS', check_PE_exported_functions),
77+
],
78+
lief.EXE_FORMATS.MACHO: [
79+
('EXPORTED_FUNCTIONS', check_MACHO_exported_functions),
80+
]
81+
}
82+
83+
USAGE = """
84+
symbol-check.py is a script to check that a secp256k1 shared library
85+
exports only expected symbols.
86+
87+
Usage:
88+
./tools/symbol-check.py <library>
89+
90+
"""
91+
92+
if __name__ == '__main__':
93+
if len(sys.argv) != 2:
94+
sys.exit(USAGE)
95+
96+
filename: str = sys.argv[1]
97+
try:
98+
library: lief.Binary = lief.parse(filename)
99+
exe_format: lief.EXE_FORMATS = library.format
100+
if exe_format == lief.EXE_FORMATS.UNKNOWN:
101+
print(f'{filename}: unknown executable format')
102+
sys.exit(1)
103+
104+
obj_type = library.abstract.header.object_type
105+
if obj_type != lief.OBJECT_TYPES.LIBRARY:
106+
print(f'{filename}: unsupported object type, only LIBRARY type is supported')
107+
sys.exit(1)
108+
109+
expected_exports = grep_exported_symbols()
110+
failed: List[str] = []
111+
for (name, func) in CHECKS[exe_format]:
112+
if not func(library, expected_exports):
113+
failed.append(name)
114+
if failed:
115+
print(f'{filename}: failed {" ".join(failed)}')
116+
sys.exit(1)
117+
except IOError:
118+
print(f'{filename}: cannot open')
119+
sys.exit(1)

0 commit comments

Comments
 (0)