diff options
Diffstat (limited to 'Tools/c-analyzer/c_analyzer/symbols/_nm.py')
-rw-r--r-- | Tools/c-analyzer/c_analyzer/symbols/_nm.py | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/Tools/c-analyzer/c_analyzer/symbols/_nm.py b/Tools/c-analyzer/c_analyzer/symbols/_nm.py new file mode 100644 index 0000000000..f3a75a6d4b --- /dev/null +++ b/Tools/c-analyzer/c_analyzer/symbols/_nm.py @@ -0,0 +1,117 @@ +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 |