import os.path import shutil from c_analyzer.common import util, info from .info import Symbol # XXX need tests: # * iter_symbols NM_KINDS = { 'b': Symbol.KIND.VARIABLE, # uninitialized 'd': Symbol.KIND.VARIABLE, # initialized #'g': Symbol.KIND.VARIABLE, # uninitialized #'s': Symbol.KIND.VARIABLE, # initialized 't': Symbol.KIND.FUNCTION, } SPECIAL_SYMBOLS = { # binary format (e.g. ELF) '__bss_start', '__data_start', '__dso_handle', '_DYNAMIC', '_edata', '_end', '__environ@@GLIBC_2.2.5', '_GLOBAL_OFFSET_TABLE_', '__JCR_END__', '__JCR_LIST__', '__TMC_END__', } def _is_special_symbol(name): if name in SPECIAL_SYMBOLS: return True if '@@GLIBC' in name: return True return False def iter_symbols(binfile, *, nm=None, handle_id=None, _which=shutil.which, _run=util.run_cmd, ): """Yield a Symbol for each relevant entry reported by the "nm" command.""" if nm is None: nm = _which('nm') if not nm: raise NotImplementedError if handle_id is None: handle_id = info.ID argv = [nm, '--line-numbers', binfile, ] try: output = _run(argv) except Exception: if nm is None: # XXX Use dumpbin.exe /SYMBOLS on Windows. raise NotImplementedError raise for line in output.splitlines(): (name, kind, external, filename, funcname, ) = _parse_nm_line(line) if kind != Symbol.KIND.VARIABLE: continue elif _is_special_symbol(name): continue yield Symbol( id=handle_id(filename, funcname, name), kind=kind, external=external, ) def _parse_nm_line(line): _origline = line _, _, line = line.partition(' ') # strip off the address line = line.strip() kind, _, line = line.partition(' ') line = line.strip() external = kind.isupper() kind = NM_KINDS.get(kind.lower(), Symbol.KIND.OTHER) name, _, filename = line.partition('\t') name = name.strip() if filename: filename = os.path.relpath(filename.partition(':')[0]) else: filename = info.UNKNOWN name, islocal = _parse_nm_name(name, kind) funcname = info.UNKNOWN if islocal else None return name, kind, external, filename, funcname def _parse_nm_name(name, kind): if kind != Symbol.KIND.VARIABLE: return name, None if _is_special_symbol(name): return name, None actual, sep, digits = name.partition('.') if not sep: return name, False if not digits.isdigit(): raise Exception(f'got bogus name {name}') return actual, True