scripts: only parse the binary once in symbol-check.py

This commit is contained in:
fanquake 2021-07-02 14:37:06 +08:00
parent 309eac9019
commit 8242ae230e
No known key found for this signature in database
GPG Key ID: 2EEB9F5CC09526C1

View File

@ -11,7 +11,7 @@ Example usage:
find ../path/to/binaries -type f -executable | xargs python3 contrib/devtools/symbol-check.py find ../path/to/binaries -type f -executable | xargs python3 contrib/devtools/symbol-check.py
''' '''
import sys import sys
from typing import List, Optional from typing import List
import lief import lief
@ -144,9 +144,8 @@ def check_version(max_versions, version, arch) -> bool:
else: else:
return ver <= max_versions[lib][arch] return ver <= max_versions[lib][arch]
def check_imported_symbols(filename) -> bool: def check_imported_symbols(binary) -> bool:
ok: bool = True ok: bool = True
binary = lief.parse(filename)
for symbol in binary.imported_symbols: for symbol in binary.imported_symbols:
if not symbol.imported: if not symbol.imported:
@ -161,9 +160,8 @@ def check_imported_symbols(filename) -> bool:
ok = False ok = False
return ok return ok
def check_exported_symbols(filename) -> bool: def check_exported_symbols(binary) -> bool:
ok: bool = True ok: bool = True
binary = lief.parse(filename)
for symbol in binary.dynamic_symbols: for symbol in binary.dynamic_symbols:
if not symbol.exported: if not symbol.exported:
@ -171,22 +169,20 @@ def check_exported_symbols(filename) -> bool:
name = symbol.name name = symbol.name
if binary.header.machine_type == LIEF_ELF_ARCH_RISCV or name in IGNORE_EXPORTS: if binary.header.machine_type == LIEF_ELF_ARCH_RISCV or name in IGNORE_EXPORTS:
continue continue
print(f'{filename}: export of symbol {name} not allowed!') print(f'{binary.name}: export of symbol {name} not allowed!')
ok = False ok = False
return ok return ok
def check_ELF_libraries(filename) -> bool: def check_ELF_libraries(binary) -> bool:
ok: bool = True ok: bool = True
binary = lief.parse(filename)
for library in binary.libraries: for library in binary.libraries:
if library not in ELF_ALLOWED_LIBRARIES: if library not in ELF_ALLOWED_LIBRARIES:
print(f'{filename}: {library} is not in ALLOWED_LIBRARIES!') print(f'{filename}: {library} is not in ALLOWED_LIBRARIES!')
ok = False ok = False
return ok return ok
def check_MACHO_libraries(filename) -> bool: def check_MACHO_libraries(binary) -> bool:
ok: bool = True ok: bool = True
binary = lief.parse(filename)
for dylib in binary.libraries: for dylib in binary.libraries:
split = dylib.name.split('/') split = dylib.name.split('/')
if split[-1] not in MACHO_ALLOWED_LIBRARIES: if split[-1] not in MACHO_ALLOWED_LIBRARIES:
@ -194,29 +190,25 @@ def check_MACHO_libraries(filename) -> bool:
ok = False ok = False
return ok return ok
def check_MACHO_min_os(filename) -> bool: def check_MACHO_min_os(binary) -> bool:
binary = lief.parse(filename)
if binary.build_version.minos == [10,15,0]: if binary.build_version.minos == [10,15,0]:
return True return True
return False return False
def check_MACHO_sdk(filename) -> bool: def check_MACHO_sdk(binary) -> bool:
binary = lief.parse(filename)
if binary.build_version.sdk == [10, 15, 6]: if binary.build_version.sdk == [10, 15, 6]:
return True return True
return False return False
def check_PE_libraries(filename) -> bool: def check_PE_libraries(binary) -> bool:
ok: bool = True ok: bool = True
binary = lief.parse(filename)
for dylib in binary.libraries: for dylib in binary.libraries:
if dylib not in PE_ALLOWED_LIBRARIES: if dylib not in PE_ALLOWED_LIBRARIES:
print(f'{dylib} is not in ALLOWED_LIBRARIES!') print(f'{dylib} is not in ALLOWED_LIBRARIES!')
ok = False ok = False
return ok return ok
def check_PE_subsystem_version(filename) -> bool: def check_PE_subsystem_version(binary) -> bool:
binary = lief.parse(filename)
major: int = binary.optional_header.major_subsystem_version major: int = binary.optional_header.major_subsystem_version
minor: int = binary.optional_header.minor_subsystem_version minor: int = binary.optional_header.minor_subsystem_version
if major == 6 and minor == 1: if major == 6 and minor == 1:
@ -240,30 +232,20 @@ CHECKS = {
] ]
} }
def identify_executable(executable) -> Optional[str]:
with open(filename, 'rb') as f:
magic = f.read(4)
if magic.startswith(b'MZ'):
return 'PE'
elif magic.startswith(b'\x7fELF'):
return 'ELF'
elif magic.startswith(b'\xcf\xfa'):
return 'MACHO'
return None
if __name__ == '__main__': if __name__ == '__main__':
retval: int = 0 retval: int = 0
for filename in sys.argv[1:]: for filename in sys.argv[1:]:
try: try:
etype = identify_executable(filename) binary = lief.parse(filename)
if etype is None: etype = binary.format.name
print(f'{filename}: unknown format') if etype == lief.EXE_FORMATS.UNKNOWN:
print(f'{filename}: unknown executable format')
retval = 1 retval = 1
continue continue
failed: List[str] = [] failed: List[str] = []
for (name, func) in CHECKS[etype]: for (name, func) in CHECKS[etype]:
if not func(filename): if not func(binary):
failed.append(name) failed.append(name)
if failed: if failed:
print(f'{filename}: failed {" ".join(failed)}') print(f'{filename}: failed {" ".join(failed)}')