1import os.path 2import shutil 3 4from c_analyzer.common import util, info 5 6from .info import Symbol 7 8 9# XXX need tests: 10# * iter_symbols 11 12NM_KINDS = { 13 'b': Symbol.KIND.VARIABLE, # uninitialized 14 'd': Symbol.KIND.VARIABLE, # initialized 15 #'g': Symbol.KIND.VARIABLE, # uninitialized 16 #'s': Symbol.KIND.VARIABLE, # initialized 17 't': Symbol.KIND.FUNCTION, 18 } 19 20SPECIAL_SYMBOLS = { 21 # binary format (e.g. ELF) 22 '__bss_start', 23 '__data_start', 24 '__dso_handle', 25 '_DYNAMIC', 26 '_edata', 27 '_end', 28 '__environ@@GLIBC_2.2.5', 29 '_GLOBAL_OFFSET_TABLE_', 30 '__JCR_END__', 31 '__JCR_LIST__', 32 '__TMC_END__', 33 } 34 35 36def _is_special_symbol(name): 37 if name in SPECIAL_SYMBOLS: 38 return True 39 if '@@GLIBC' in name: 40 return True 41 return False 42 43 44def iter_symbols(binfile, *, 45 nm=None, 46 handle_id=None, 47 _which=shutil.which, 48 _run=util.run_cmd, 49 ): 50 """Yield a Symbol for each relevant entry reported by the "nm" command.""" 51 if nm is None: 52 nm = _which('nm') 53 if not nm: 54 raise NotImplementedError 55 if handle_id is None: 56 handle_id = info.ID 57 58 argv = [nm, 59 '--line-numbers', 60 binfile, 61 ] 62 try: 63 output = _run(argv) 64 except Exception: 65 if nm is None: 66 # XXX Use dumpbin.exe /SYMBOLS on Windows. 67 raise NotImplementedError 68 raise 69 for line in output.splitlines(): 70 (name, kind, external, filename, funcname, 71 ) = _parse_nm_line(line) 72 if kind != Symbol.KIND.VARIABLE: 73 continue 74 elif _is_special_symbol(name): 75 continue 76 yield Symbol( 77 id=handle_id(filename, funcname, name), 78 kind=kind, 79 external=external, 80 ) 81 82 83def _parse_nm_line(line): 84 _origline = line 85 _, _, line = line.partition(' ') # strip off the address 86 line = line.strip() 87 88 kind, _, line = line.partition(' ') 89 line = line.strip() 90 external = kind.isupper() 91 kind = NM_KINDS.get(kind.lower(), Symbol.KIND.OTHER) 92 93 name, _, filename = line.partition('\t') 94 name = name.strip() 95 if filename: 96 filename = os.path.relpath(filename.partition(':')[0]) 97 else: 98 filename = info.UNKNOWN 99 100 name, islocal = _parse_nm_name(name, kind) 101 funcname = info.UNKNOWN if islocal else None 102 return name, kind, external, filename, funcname 103 104 105def _parse_nm_name(name, kind): 106 if kind != Symbol.KIND.VARIABLE: 107 return name, None 108 if _is_special_symbol(name): 109 return name, None 110 111 actual, sep, digits = name.partition('.') 112 if not sep: 113 return name, False 114 115 if not digits.isdigit(): 116 raise Exception(f'got bogus name {name}') 117 return actual, True 118