118 lines
2.9 KiB
Python
118 lines
2.9 KiB
Python
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
|