summaryrefslogtreecommitdiff
path: root/Tools/c-analyzer/c_analyzer/symbols/_nm.py
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/c-analyzer/c_analyzer/symbols/_nm.py')
-rw-r--r--Tools/c-analyzer/c_analyzer/symbols/_nm.py117
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