├── .gitignore ├── LICENSE.txt ├── README.md ├── __init__.py ├── bridge.py ├── io ├── dwarf_expr.py ├── dwarf_import.py └── elftools_extras.py ├── location_index.py ├── mapped_model.py ├── mapping.py ├── model ├── __init__.py ├── abstract_elements.py ├── analysis_model.py ├── attributes.py ├── component.py ├── concrete_elements.py ├── locations.py ├── observer.py ├── qualified_name.py ├── resource_schema.py └── touch_flags.py ├── plugin.json └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020-2023 Vector 35 Inc 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to 5 | deal in the Software without restriction, including without limitation the 6 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | sell copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DWARF Import (Deprecated) (v1.2.1) 2 | Author: **Vector 35 Inc** 3 | 4 | _Imports DWARF Info from ELFs_ 5 | 6 | ## Description: 7 | 8 | This plugin is deprecated. 9 | 10 | Please use our new DWARF Import plugin, which is shipped with the product by default: https://github.com/Vector35/binaryninja-api/issues/3206#issuecomment-1629342753 11 | 12 | 13 | ## Minimum Version 14 | 15 | This plugin requires the following minimum version of Binary Ninja: 16 | 17 | * 3000 18 | 19 | 20 | ## Required Dependencies 21 | 22 | The following dependencies are required for this plugin: 23 | 24 | * pip - pyelftools==0.27 25 | 26 | 27 | ## License 28 | 29 | This plugin is released under a MIT license. 30 | ## Metadata Version 31 | 32 | 2 33 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright(c) 2020 Vector 35 Inc 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files(the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and / or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | from binaryninja import BackgroundTaskThread 22 | from binaryninja.binaryview import BinaryReader 23 | from binaryninja.log import log_error, log_warn, log_alert 24 | from binaryninja.interaction import OpenFileNameField, get_form_input 25 | from binaryninja.plugin import PluginCommand 26 | from .bridge import BinjaBridge 27 | from .mapped_model import AnalysisSession 28 | from elftools.elf.elffile import ELFFile 29 | import os 30 | 31 | 32 | class DWARF_loader(BackgroundTaskThread): 33 | def __init__(self, bv, debug_file=None): 34 | BackgroundTaskThread.__init__(self) 35 | self.view = bv 36 | self.debug_file = debug_file 37 | self.progress = "" 38 | 39 | def run(self): 40 | # Open the binary. 41 | analysis_session = AnalysisSession(binary_view = self.view, debug_file = self.debug_file) 42 | 43 | if analysis_session.binary_view is None or analysis_session.binary_view.arch is None: 44 | log_error("Unable to import dwarf") 45 | 46 | # Setup the translator. 47 | bridge = BinjaBridge(analysis_session) 48 | bridge.translate_model() 49 | 50 | # Finalize the analysis. 51 | analysis_session.binary_view.update_analysis() 52 | 53 | 54 | def load_symbols(bv): 55 | log_alert("This plugin is deprecated. While it will still work, we suggest that you uninstall this plugin and use our new one. See this plugin's readme.md for details.") 56 | try: 57 | if bv.query_metadata("dwarf_info_applied") == 1: 58 | log_warn("DWARF Debug Info has already been applied to this binary view") 59 | return 60 | except KeyError: 61 | bv.store_metadata("dwarf_info_applied", True) 62 | DWARF_loader(bv).start() 63 | 64 | 65 | def load_symbols_from_file(bv): 66 | log_alert("This plugin is deprecated. While it will still work, we suggest that you uninstall this plugin and use our new one. See this plugin's readme.md for details.") 67 | try: 68 | if bv.query_metadata("dwarf_info_applied") == 1: 69 | log_warn("DWARF Debug Info has already been applied to this binary view") 70 | return 71 | except KeyError: 72 | bv.store_metadata("dwarf_info_applied", True) 73 | 74 | file_choice = OpenFileNameField("Debug file") 75 | get_form_input([file_choice], "Open debug file") 76 | 77 | if not file_choice.result or not os.path.exists(file_choice.result): 78 | log_error(f"Input file `{file_choice.result}` does not exist") 79 | bv.store_metadata("dwarf_info_applied", False) 80 | return 81 | 82 | DWARF_loader(bv, file_choice.result).start() 83 | 84 | 85 | def is_valid(bv): 86 | raw = False 87 | elf = False 88 | if bv.parent_view is None: 89 | return False 90 | for view in bv.parent_view.available_view_types: 91 | if view.name == "ELF": 92 | elf = True 93 | elif view.name == "Raw": 94 | raw = True 95 | reader = BinaryReader(bv.file.raw) 96 | reader.tell = lambda: reader.offset 97 | return raw and elf and ELFFile(reader).has_dwarf_info() 98 | 99 | 100 | PluginCommand.register("DWARF Import (Deprecated)\\Load DWARF Symbols", "Load DWARF Symbols from the current file", load_symbols, is_valid) 101 | PluginCommand.register("DWARF Import (Deprecated)\\Load DWARF Symbols From File", "Load DWARF Symbols from another file", load_symbols_from_file, lambda bv: True) 102 | -------------------------------------------------------------------------------- /bridge.py: -------------------------------------------------------------------------------- 1 | # Copyright(c) 2020-2023 Vector 35 Inc 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files(the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and / or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | import sys 22 | import logging 23 | import concurrent.futures 24 | from uuid import UUID 25 | from typing import List, Optional, Iterable, Set, MutableMapping 26 | from collections import defaultdict 27 | from .model import QualifiedName, Component 28 | from .mapped_model import AnalysisSession 29 | from .model.observer import Observer 30 | from .model.concrete_elements import ( 31 | Element, 32 | Variable, 33 | Constant, 34 | Function, LocalVariable, Parameter, 35 | Type, BaseType, CompositeType, EnumType, StructType, UnionType, ClassType, 36 | AliasType, PointerType, ArrayType, FunctionType, ConstType, 37 | VariableStorage, Field, 38 | VariadicType, VolatileType, 39 | PointerToMemberType, StringType 40 | ) 41 | from .model.locations import LocationType 42 | from .location_index import LocationIndex, VariableUpdate, VariableUpdateStatus 43 | from .mapping import BinjaMap 44 | import binaryninja as bn 45 | 46 | 47 | def get_function_platform(fn: Function): 48 | if fn.arch == 'ARM': 49 | return bn.Platform['linux-armv7'] 50 | elif fn.arch == 'Thumb': 51 | return bn.Platform['linux-thumb2'] 52 | else: 53 | return None 54 | 55 | 56 | def rank_of_type(ty: Type) -> int: 57 | if ty.name.is_empty: 58 | return 100 59 | elif isinstance(ty, BaseType): 60 | return 0 61 | elif isinstance(ty, EnumType): 62 | return 0 63 | elif isinstance(ty, AliasType): 64 | return rank_of_type(ty.resolve_alias()) 65 | elif isinstance(ty, ArrayType): 66 | return rank_of_type(ty.element_type) 67 | elif isinstance(ty, CompositeType): 68 | for f in ty.fields(): 69 | if isinstance(f, CompositeType): 70 | return 50 71 | ranks = [rank_of_type(f.field.type) for f in ty.fields()] 72 | return max(ranks) if len(ranks) > 0 else 0 73 | elif isinstance(ty, PointerToMemberType): 74 | return 100 75 | else: 76 | raise NotImplementedError(f'rank_of_type {ty.__class__.__name__}') 77 | 78 | 79 | class BinjaBridge(Observer, bn.BinaryDataNotification): 80 | def __init__(self, session: AnalysisSession, parameters_mode: str = 'inferred', 81 | log_level=logging.INFO, parent_logger=None): 82 | Observer.__init__(self) 83 | bn.BinaryDataNotification.__init__(self) 84 | if parent_logger is not None: 85 | self._log = parent_logger.getChild('Bridge') 86 | self._log.setLevel(log_level) 87 | else: 88 | logging.basicConfig(level=log_level) 89 | self._log = logging.getLogger('bridge') 90 | self._session: AnalysisSession = session 91 | self._model = session.model 92 | self._mapping: BinjaMap = session.mapping 93 | self._parameters_mode = parameters_mode 94 | self._version_table: MutableMapping[UUID, int] = dict() 95 | self._s2b_types: MutableMapping[UUID, bn.Type] = dict() 96 | self._b2s_types: MutableMapping[bn.QualifiedName, Type] = dict() 97 | self._base_types: MutableMapping[bn.QualifiedName, bn.Type] = dict() 98 | self._builtin_types: Set[bn.QualifiedName] = set() 99 | self._typelib_defined_types = set() 100 | self._session.model.add_observer(self) 101 | self._binary_view: bn.BinaryView = session.binary_view 102 | self.statistics = defaultdict(int) 103 | self._batch_mode = False 104 | self._translation_executor = None 105 | 106 | def translate_model(self, max_workers=None): 107 | self.translate_model_types() 108 | 109 | for fn in self._session.model.functions: 110 | if fn.entry_addr is not None: 111 | self._binary_view.create_user_function(fn.entry_addr, get_function_platform(fn)) 112 | 113 | self._log.debug('Waiting for auto analysis after defining types and functions...') 114 | self._binary_view.update_analysis_and_wait() 115 | 116 | with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor: 117 | self._translation_executor = executor 118 | 119 | futures = [] 120 | for v in self._session.model.variables: 121 | futures.append(executor.submit(self._translate_variable, v)) 122 | 123 | futures = [] 124 | for fn in self._session.model.functions: 125 | futures.append(executor.submit(self._translate_function, fn)) 126 | concurrent.futures.wait(futures) 127 | 128 | self._log.debug('Waiting for auto analysis after translating functions...') 129 | self._binary_view.update_analysis_and_wait() 130 | 131 | futures = [] 132 | for fn in self._session.model.functions: 133 | futures.append(executor.submit(self._translate_function_signature, fn)) 134 | concurrent.futures.wait(futures) 135 | 136 | self._translation_executor = None 137 | 138 | self._binary_view.commit_undo_actions() 139 | 140 | def cancel(self, wait: bool): 141 | if self._translation_executor is not None: 142 | self._translation_executor.shutdown(wait=wait, cancel_futures=True) 143 | 144 | def translate_model_types(self, overwrite: bool = False): 145 | if overwrite is False: 146 | self._typelib_defined_types = set(self._binary_view.types.keys()) 147 | 148 | for ty in sorted(self._session.model.types, key=lambda ty: rank_of_type(ty)): 149 | self._translate_type(ty) 150 | 151 | def _translate_component(self, component: Component, **kwargs): 152 | self._log.debug(f'Translating component ("{component.name}")') 153 | 154 | num_created = 0 155 | for start in filter(None, map(lambda f: f.entry_addr, component.functions())): 156 | binja_function = self._binary_view.get_function_at(start) 157 | if binja_function is None: 158 | self._binary_view.create_user_function(start) 159 | num_created += 1 160 | if num_created > 0: 161 | self._log.debug(f'Created {num_created} new function(s).') 162 | self._log.debug('Waiting for auto analysis...') 163 | self._binary_view.update_analysis_and_wait() 164 | 165 | self._translate_component_elements(component, component.elements(), **kwargs) 166 | 167 | def _translate_component_elements( 168 | self, 169 | component: Component, 170 | elements: Iterable[Element], 171 | do_return_type=True 172 | ): 173 | mapped_functions = [] 174 | for el in filter(self._mapping.is_newer, elements): 175 | if isinstance(el, Type): 176 | self._translate_type(el) 177 | elif isinstance(el, Variable): 178 | self._translate_variable(el) 179 | elif isinstance(el, Function): 180 | if self._translate_function(el): 181 | mapped_functions.append(el) 182 | elif isinstance(el, Constant): 183 | self._translate_constant(el) 184 | else: 185 | self._log.warning(f'untranslated element: {type(el)}') 186 | 187 | if do_return_type is True: 188 | self._log.debug('Waiting for auto analysis...') 189 | self._binary_view.update_analysis_and_wait() 190 | for function in mapped_functions: 191 | self._translate_function_signature(function) 192 | 193 | def _translate_constant(self, const: Constant): 194 | pass 195 | 196 | def _translate_variable(self, var: Variable): 197 | if not self._mapping.is_newer(var): 198 | return False 199 | 200 | self._version_table[var.uuid] = var.version 201 | self._log.debug(f'Translating variable ("{var.name}", {var.version}, {(var.addr if var.addr is not None else 0):x})') 202 | 203 | if var.addr is None or var.type is None: 204 | if var.addr is None: 205 | self._log.debug(f'Variable "{var.name}" has no address.') 206 | if var.type is None: 207 | self._log.debug(f'Variable "{var.name}" has no type.') 208 | self.statistics['num_globals_unresolved'] += 1 209 | return 210 | 211 | binja_type = self._construct_binja_type(var.type, as_specifier=True) 212 | 213 | binja_var = self._binary_view.get_data_var_at(var.addr) 214 | if binja_var is None or binja_var.type != binja_type: 215 | self._binary_view.define_user_data_var(var.addr, binja_type) 216 | binja_var = self._binary_view.get_data_var_at(var.addr) 217 | 218 | if binja_var is None: 219 | self._log.error(f'Unable to define variable "{var.name}" at 0x{var.addr:x}') 220 | self.statistics['num_globals_unresolved'] += 1 221 | return 222 | 223 | symbol = self._binary_view.get_symbol_at(var.addr) 224 | if symbol is None or symbol.short_name != var.name: 225 | self._binary_view.define_user_symbol(bn.Symbol(bn.SymbolType.DataSymbol, var.addr, str(var.name))) 226 | 227 | self.statistics['num_globals_resolved'] += 1 228 | 229 | def _translate_function(self, fn: Function) -> bool: 230 | if not self._mapping.is_newer(fn): 231 | return False 232 | 233 | self._version_table[fn.uuid] = fn.version 234 | self._log.debug(f'Translating function ("{fn.name}", {fn.version}, {fn.entry_addr:x})') 235 | if fn.entry_addr is None: 236 | return False 237 | binja_function = self._binary_view.get_function_at(fn.entry_addr) 238 | if binja_function is None: 239 | self._binary_view.create_user_function(fn.entry_addr) 240 | self._log.info('Updating analysis and waiting...') 241 | self._binary_view.update_analysis_and_wait() 242 | binja_function = self._binary_view.get_function_at(fn.entry_addr) 243 | if binja_function is None: 244 | self._log.warning(f'Unable to create a function at {fn.entry_addr:x}') 245 | self.statistics['num_functions_not_found'] += 1 246 | return False 247 | 248 | binja_function.can_return = bn.BoolWithConfidence(not fn.no_return) 249 | 250 | binja_symbol: bn.CoreSymbol = binja_function.symbol 251 | if fn.name.is_empty is False and binja_symbol.short_name != str(fn.name): 252 | self._rename_symbol(binja_symbol, str(fn.name)) 253 | 254 | if binja_function.mlil is None: 255 | if binja_function.analysis_skipped: 256 | binja_function.analysis_skipped = False 257 | self._binary_view.update_analysis_and_wait() 258 | if binja_function.mlil is None: 259 | if binja_function.analysis_skipped: 260 | self._log.warning( 261 | f'Function skipped a: {binja_function.symbol.short_name}' 262 | f' (reason = {binja_function.analysis_skip_reason.name}, {binja_function.analysis_performance_info})') 263 | self.statistics['num_functions_skipped'] += 1 264 | return False 265 | else: 266 | self._binary_view.update_analysis_and_wait() 267 | if binja_function.mlil is None: 268 | if binja_function.analysis_skipped: 269 | self._log.warning( 270 | f'Function skipped b: {binja_function.symbol.short_name}' 271 | f' (reason = {binja_function.analysis_skip_reason.name}, {binja_function.analysis_performance_info})') 272 | self.statistics['num_functions_skipped'] += 1 273 | return False 274 | else: 275 | assert False, 'No MLIL, update did not fix, and analysis not skipped.' 276 | 277 | local_vars = LocationIndex(binja_function, fn.frame_base, self._log) 278 | if self._parameters_mode == 'inferred': 279 | for p in fn.parameters: 280 | self._translate_parameter(p, binja_function, local_vars) 281 | if fn.variables is not None: 282 | for v in fn.variables: 283 | self._translate_local_variable(v, binja_function, local_vars) 284 | 285 | if fn.inlined_functions: 286 | for inlined_function in fn.inlined_functions: 287 | self._translate_inlined_function(inlined_function, binja_function, local_vars) 288 | 289 | local_vars.propagate_names() 290 | self.statistics['num_functions_processed'] += 1 291 | return True 292 | 293 | def _translate_function_signature(self, fn: Function): 294 | self._log.debug(f'Translating function signature ("{fn.name}", {fn.version}, {fn.entry_addr:x})') 295 | 296 | if fn.entry_addr is None: 297 | return 298 | binja_fn = self._binary_view.get_function_at(fn.entry_addr) 299 | assert(isinstance(binja_fn, bn.Function)) 300 | 301 | return_type = bn.Type.void() 302 | if fn.return_value is not None: 303 | return_type = self._construct_binja_type(fn.return_value.type, as_specifier=True) 304 | 305 | if self._parameters_mode == 'declared': 306 | self._translate_function_type(fn, binja_fn) 307 | 308 | elif self._parameters_mode == 'inferred': 309 | valid_parameter_names = [p.local_name for p in fn.parameters] 310 | binja_params = binja_fn.type.parameters 311 | any_matched = False 312 | n_args = len(binja_params) 313 | for i in range(n_args - 1, -1, -1): 314 | if binja_params[i].name in valid_parameter_names: 315 | n_args = i + 1 316 | any_matched = True 317 | break 318 | 319 | if not any_matched: 320 | self._translate_function_type(fn, binja_fn) 321 | return 322 | 323 | if n_args != len(binja_params) or fn.variadic: 324 | func_type: bn.FunctionType = binja_fn.type 325 | assert func_type.calling_convention 326 | binja_fn.type = bn.Type.function( 327 | return_type, 328 | binja_params[:n_args], 329 | func_type.calling_convention, 330 | fn.variadic, 331 | func_type.stack_adjustment) 332 | return 333 | 334 | if return_type != binja_fn.type.return_value: 335 | binja_fn.return_type = return_type 336 | 337 | def _translate_inlined_function( 338 | self, 339 | inlined_function: Function, 340 | binja_function: bn.Function, 341 | local_vars: LocationIndex 342 | ): 343 | for p in inlined_function.parameters: 344 | if p.name and p.name != 'this': 345 | self._translate_parameter(p, binja_function, local_vars) 346 | self._translate_function_elements(inlined_function, (), binja_function, local_vars) 347 | 348 | def _translate_function_elements( 349 | self, 350 | fn: Function, 351 | elements: Iterable[Element], 352 | binja_function: bn.Function, 353 | locals: LocationIndex 354 | ): 355 | for el in elements: 356 | if isinstance(el, LocalVariable): 357 | self._translate_local_variable(el, binja_function, locals) 358 | elif isinstance(el, Parameter): 359 | self._translate_parameter(el, binja_function, locals) 360 | if fn.inlined_functions is not None: 361 | for inlined_function in fn.inlined_functions: 362 | self._translate_inlined_function(inlined_function, binja_function, locals) 363 | 364 | def _translate_type(self, ty: Type): 365 | if not self._mapping.is_newer(ty): 366 | return 367 | if ty.uuid in self._s2b_types: 368 | return 369 | if ty.name.is_empty: 370 | return 371 | if ty.name in self._typelib_defined_types: 372 | return 373 | 374 | registered_name = bn.QualifiedName(ty.name) 375 | binja_type = self._construct_binja_type(ty) 376 | 377 | self._s2b_types[ty.uuid] = binja_type 378 | self._b2s_types[registered_name] = ty 379 | 380 | if registered_name in self._builtin_types: 381 | self._log.debug(f'Not translating built-in type {registered_name} as {binja_type} ({binja_type.type_class.name})') 382 | return 383 | 384 | if registered_name in self._binary_view.types: 385 | self._log.debug( 386 | f'Redefining {registered_name} as {binja_type} ' 387 | f'(previously {self._binary_view.types[ty.name]})') 388 | self._binary_view.define_user_type(registered_name, binja_type) 389 | 390 | def is_refinement_of(self, a, b) -> bool: 391 | if a.name != b.name: 392 | return False 393 | if len(b.members) == 0 and len(a.members) > 0: 394 | return True 395 | return False 396 | 397 | def _generate_typeid(self, binja_name: bn.QualifiedName) -> str: 398 | typeid = self._binary_view.get_type_id(binja_name) 399 | if typeid is not None: 400 | return typeid 401 | 402 | self._binary_view.define_user_type(binja_name, bn.Type.void()) 403 | typeid = self._binary_view.get_type_id(binja_name) 404 | assert(typeid is not None) 405 | return typeid 406 | 407 | def _construct_binja_type(self, ty: Type, as_specifier: bool = False) -> bn.Type: 408 | binja_type: Optional[bn.Type] = None 409 | binja_name = bn.QualifiedName(ty.name) 410 | 411 | if ty.uuid in self._s2b_types: 412 | if as_specifier and ty.name.is_anonymous is False: 413 | ntrc = bn.NamedTypeReferenceClass.UnknownNamedTypeClass 414 | if isinstance(ty, ClassType): 415 | ntrc = bn.NamedTypeReferenceClass.ClassNamedTypeClass 416 | elif isinstance(ty, StructType): 417 | ntrc = bn.NamedTypeReferenceClass.StructNamedTypeClass 418 | elif isinstance(ty, UnionType): 419 | ntrc = bn.NamedTypeReferenceClass.UnionNamedTypeClass 420 | elif isinstance(ty, EnumType): 421 | ntrc = bn.NamedTypeReferenceClass.EnumNamedTypeClass 422 | binja_type = bn.Type.named_type( 423 | bn.NamedTypeReferenceBuilder.create( 424 | name=binja_name, 425 | type_id=self._generate_typeid(binja_name), 426 | type_class=ntrc, 427 | width=(0 if ty.byte_size is None else ty.byte_size) 428 | ) 429 | ) 430 | else: 431 | binja_type = self._s2b_types[ty.uuid] 432 | return binja_type 433 | 434 | bv = self._binary_view 435 | assert(bv.arch) 436 | if isinstance(ty, BaseType): 437 | if binja_name in self._base_types: 438 | binja_type = self._base_types[binja_name] 439 | else: 440 | try: 441 | binja_type, _ = bv.parse_type_string(str(ty.name)) 442 | self._base_types[binja_name] = binja_type 443 | self._builtin_types.add(binja_name) 444 | except Exception: 445 | if ty.byte_size is not None: 446 | binja_type = bn.Type.int(ty.byte_size, False) 447 | self._base_types[binja_name] = binja_type 448 | self._builtin_types.add(binja_name) 449 | else: 450 | binja_type = bn.Type.named_type( 451 | bn.NamedTypeReferenceBuilder.create( 452 | name=binja_name, 453 | type_id=self._generate_typeid(binja_name), 454 | width=0 455 | ), 456 | ) 457 | self._base_types[binja_name] = binja_type 458 | elif isinstance(ty, PointerType): 459 | binja_target_type = self._construct_binja_type(ty.target_type, as_specifier=True) 460 | binja_ref_type = bn.ReferenceType.PointerReferenceType 461 | if ty.nullable is False: 462 | binja_ref_type = bn.ReferenceType.ReferenceReferenceType 463 | binja_type = bn.Type.pointer(bv.arch, binja_target_type, ref_type=binja_ref_type) 464 | elif isinstance(ty, ArrayType): 465 | binja_element_type = self._construct_binja_type(ty.element_type, as_specifier=True) 466 | count = 0 if ty.count is None else ty.count 467 | if count > 65535: 468 | count = 0 469 | binja_type = bn.Type.array(binja_element_type, count) 470 | elif isinstance(ty, EnumType): 471 | if as_specifier and ty.name.is_anonymous is False: 472 | ntrc = bn.NamedTypeReferenceClass.EnumNamedTypeClass 473 | binja_type = bn.Type.named_type( 474 | bn.NamedTypeReferenceBuilder.create( 475 | name=binja_name, 476 | type_id=self._generate_typeid(binja_name), 477 | type_class=ntrc, 478 | width=(0 if ty.byte_size is None else ty.byte_size) 479 | ) 480 | ) 481 | e = bn.EnumerationBuilder.create(members=[]) 482 | for m in ty.enumerators: 483 | e.append(m.label, m.value) 484 | byte_size = (0 if ty.byte_size is None else ty.byte_size) 485 | binja_type = bn.Type.enumeration_type(bv.arch, e, byte_size) 486 | elif isinstance(ty, CompositeType): 487 | if as_specifier and ty.name.is_anonymous is False: 488 | ntrc = bn.NamedTypeReferenceClass.UnknownNamedTypeClass 489 | if isinstance(ty, ClassType): 490 | ntrc = bn.NamedTypeReferenceClass.ClassNamedTypeClass 491 | elif isinstance(ty, StructType): 492 | ntrc = bn.NamedTypeReferenceClass.StructNamedTypeClass 493 | elif isinstance(ty, UnionType): 494 | ntrc = bn.NamedTypeReferenceClass.UnionNamedTypeClass 495 | binja_type = bn.Type.named_type( 496 | bn.NamedTypeReferenceBuilder.create( 497 | name=binja_name, 498 | type_id=self._generate_typeid(binja_name), 499 | type_class=ntrc, 500 | width=(0 if ty.byte_size is None else ty.byte_size) 501 | ) 502 | ) 503 | else: 504 | if isinstance(ty, ClassType) or isinstance(ty, StructType): 505 | struct = bn.StructureBuilder.create() 506 | struct.type = bn.StructureVariant.StructStructureType 507 | if isinstance(ty, ClassType): 508 | struct.type = bn.StructureVariant.ClassStructureType 509 | if ty.byte_size is not None: 510 | struct.width = ty.byte_size 511 | for m in ty.fields(): 512 | field_type = self._construct_binja_type(m.field.type, as_specifier=True) 513 | field_name = m.field.local_name 514 | if m.field.offset is not None: 515 | struct.insert(m.field.offset, field_type, field_name) 516 | binja_type = bn.Type.structure_type(struct) 517 | elif isinstance(ty, UnionType): 518 | union = bn.StructureBuilder.create() 519 | union.type = bn.StructureVariant.UnionStructureType 520 | if ty.byte_size is not None: 521 | union.width = ty.byte_size 522 | for m in ty.fields(): 523 | field_type = self._construct_binja_type(m.field.type, as_specifier=as_specifier) 524 | field_name = m.field.local_name 525 | if m.field.offset is not None: 526 | union.insert(m.field.offset, field_type, field_name) 527 | binja_type = bn.Type.structure_type(union) 528 | else: 529 | assert(False) 530 | elif isinstance(ty, FunctionType): 531 | has_variable_args = False 532 | if ty.return_type is None: 533 | ret = bn.Type.void() 534 | else: 535 | ret = self._construct_binja_type(ty.return_type, as_specifier=True) 536 | params = [] 537 | for param_type in ty.parameters: 538 | if isinstance(param_type, VariadicType): 539 | has_variable_args = True 540 | else: 541 | params.append(self._construct_binja_type(param_type, as_specifier=True)) 542 | binja_type = bn.Type.function(ret, params, variable_arguments=has_variable_args) 543 | elif isinstance(ty, AliasType): 544 | binja_type = self._construct_binja_type(ty.type, as_specifier=not ty.type.name.is_anonymous) 545 | elif isinstance(ty, ConstType): 546 | binja_type = self._construct_binja_type(ty.type, as_specifier=True) 547 | temp = binja_type.mutable_copy() 548 | temp.const = True 549 | binja_type = temp.immutable_copy() 550 | assert(binja_type is not None) 551 | elif isinstance(ty, VolatileType): 552 | binja_type = self._construct_binja_type(ty.type, as_specifier=True) 553 | temp = binja_type.mutable_copy() 554 | temp.volatile = True 555 | binja_type = temp.immutable_copy() 556 | assert(binja_type is not None) 557 | elif isinstance(ty, PointerToMemberType): 558 | mp_struct = bn.StructureBuilder.create() 559 | mp_struct.type = bn.StructureVariant.StructStructureType 560 | binja_fn_type = self._construct_binja_type(ty.target_type, as_specifier=True) 561 | binja_ptr_type = bn.Type.pointer(bv.arch, binja_fn_type, ref_type=bn.ReferenceType.PointerReferenceType) 562 | mp_struct.insert(0, binja_ptr_type, 'member') 563 | binja_type = bn.Type.structure_type(mp_struct) 564 | elif isinstance(ty, StringType): 565 | if ty.is_null_terminated is False: 566 | assert(ty.byte_size is not None) 567 | binja_element_type = bn.Type.int(ty.char_size, sign=False) 568 | count = int(ty.byte_size / ty.char_size) 569 | if count > 65535: 570 | count = 0 571 | if not isinstance(count, int): 572 | raise Exception(f'invalid count for array. ({ty.byte_size=}) ({ty.char_size=})') 573 | binja_type = bn.Type.array(binja_element_type, count) 574 | else: 575 | raise NotImplementedError('null terminated string') 576 | else: 577 | raise NotImplementedError(type(ty)) 578 | 579 | return binja_type 580 | 581 | def _translate_parameter(self, param: Parameter, binja_function: bn.Function, local_vars: LocationIndex): 582 | sym = binja_function.symbol 583 | assert(sym is not None) 584 | 585 | if len(param.locations) == 0: 586 | self._log.debug(f'In {sym.short_name}(): parameter ("{param.name}") has no locations') 587 | return 588 | 589 | if param.type is None: 590 | self._log.debug(f'In {sym.short_name}(): parameter ("{param.name}") has no type') 591 | return 592 | 593 | binja_type = self._construct_binja_type(param.type, as_specifier=True) 594 | 595 | resolved = False 596 | for loc in param.locations: 597 | result = local_vars.update_variable(loc, binja_type, param.local_name) 598 | if result.status == VariableUpdateStatus.UPDATED: 599 | resolved = True 600 | 601 | if resolved: 602 | self.statistics['num_parameters_resolved'] += 1 603 | else: 604 | if param.function.is_inlined: 605 | return 606 | 607 | for loc in param.locations: 608 | assert(loc.type == LocationType.STATIC_LOCAL) 609 | if loc.begin == 0 or binja_function in self._binary_view.get_functions_containing(loc.begin): 610 | self.statistics['num_parameters_unresolved'] += 1 611 | self._log.debug( 612 | f'In {sym.short_name}(): ' 613 | f'unable to resolve parameter ("{param.name}", # locs = {len(param.locations)})') 614 | for loc in param.locations: 615 | self._log.debug(f' --> {loc}') 616 | break 617 | 618 | def _translate_local_variable(self, var: LocalVariable, binja_function: bn.Function, local_vars: LocationIndex): 619 | sym = binja_function.symbol 620 | assert(sym is not None) 621 | 622 | if len(var.locations) == 0: 623 | self._log.debug(f'In {sym.short_name}(): local variable has no locations ("{var.name}")') 624 | return 625 | 626 | results: List[VariableUpdate] = [] 627 | 628 | if var.type is None: 629 | self._log.debug(f'In {sym.short_name}(): local variable has no type ("{var.name}")') 630 | return 631 | 632 | binja_type = self._construct_binja_type(var.type, as_specifier=True) 633 | for loc in var.locations: 634 | assert(len(loc.expr) != 1 or not isinstance(loc.expr[0], int)) 635 | r = local_vars.update_variable(loc, binja_type, var.local_name) 636 | results.append(r) 637 | 638 | if any(map(lambda r: r.status == VariableUpdateStatus.CONFLICT, results)): 639 | self._log.debug(f'In {sym.short_name}(): local variable has conflicts ("{var.name}")') 640 | if var.function.is_inlined is False: 641 | self.statistics['num_variable_conflicts'] += 1 642 | var.function.set_attribute('has_variable_conflict', True) 643 | 644 | if any(map(lambda r: r.status == VariableUpdateStatus.UPDATED, results)): 645 | self.statistics['num_variables_resolved'] += 1 646 | for r in results: 647 | if r.status == VariableUpdateStatus.UPDATED: 648 | assert(r.storage_type is not None) 649 | assert(r.storage_id is not None) 650 | assert(isinstance(r.storage_type, int)) 651 | assert(isinstance(r.storage_id, int)) 652 | var.storage.append(VariableStorage(r.storage_type, r.storage_id)) 653 | else: 654 | self._log.debug(f'In {sym.short_name}(): unable to resolve variable ("{var.name}")') 655 | for loc in var.locations: 656 | self._log.debug(f' --> {loc}') 657 | self.statistics['num_variables_unresolved'] += 1 658 | return 659 | 660 | def _rename_symbol(self, symbol: bn.CoreSymbol, new_name: str): 661 | new_symbol = bn.Symbol(symbol.type, symbol.address, new_name, new_name, symbol.raw_name) 662 | self._binary_view.define_user_symbol(new_symbol) 663 | 664 | def _rename_variable(self, var: Variable, binja_var: bn.Variable): 665 | pass 666 | 667 | def _translate_function_type(self, function: Function, binja_function: bn.Function): 668 | if binja_function.mlil is None: 669 | if binja_function.analysis_skipped: 670 | binja_function.analysis_skipped = False 671 | self._binary_view.update_analysis_and_wait() 672 | if binja_function.mlil is None: 673 | if binja_function.analysis_skipped: 674 | self._log.warning( 675 | f'Function skipped 2: {binja_function.symbol.short_name}' 676 | f' (reason = {binja_function.analysis_skip_reason.name}, {binja_function.analysis_performance_info})') 677 | return False 678 | assert binja_function.mlil is not None 679 | else: 680 | self._binary_view.update_analysis_and_wait() 681 | if binja_function.mlil is None: 682 | if binja_function.analysis_skipped: 683 | self._log.warning( 684 | f'Function skipped 3: {binja_function.symbol.short_name}' 685 | f' (reason = {binja_function.analysis_skip_reason.name}, {binja_function.analysis_performance_info})') 686 | return False 687 | assert binja_function.mlil is not None 688 | else: 689 | assert binja_function.mlil is not None, 'No MLIL, update did not fix, and analysis not skipped.' 690 | 691 | try: 692 | local_vars = LocationIndex(binja_function, None, self._log) 693 | function_type = self._create_function_type(function, binja_function, local_vars) 694 | if function_type is not None: 695 | binja_function.type = function_type 696 | except Exception: 697 | self._log.warning('while creating function type', exc_info=sys.exc_info()) 698 | 699 | def _create_function_type( 700 | self, 701 | function: Function, 702 | binja_function: bn.Function, 703 | local_vars: LocationIndex 704 | ) -> Optional[bn.FunctionType]: 705 | if function.return_value is not None: 706 | return_type = self._construct_binja_type(function.return_value.type, as_specifier=True) 707 | else: 708 | return_type = bn.Type.void() 709 | 710 | parameters = [] 711 | is_variadic = False 712 | for p in function.parameters: 713 | if isinstance(p.type, VariadicType): 714 | is_variadic = True 715 | break 716 | 717 | binja_type = self._construct_binja_type(p.type, as_specifier=True) 718 | parameters.append(bn.FunctionParameter(binja_type, p.local_name)) 719 | 720 | return bn.Type.function( 721 | return_type, 722 | parameters, 723 | calling_convention=binja_function.type.calling_convention, 724 | variable_arguments=is_variadic, 725 | stack_adjust=binja_function.type.stack_adjustment) 726 | -------------------------------------------------------------------------------- /io/dwarf_expr.py: -------------------------------------------------------------------------------- 1 | # Copyright(c) 2020-2023 Vector 35 Inc 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files(the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and / or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | from elftools.dwarf.dwarf_expr import struct_parse, bytelist2string, DW_OP_name2opcode, DW_OP_opcode2name 22 | from elftools.dwarf.descriptions import _REG_NAMES_x86 23 | from .elftools_extras import describe_reg_name, _REG_NAMES_x64, _REG_NAMES_ARM, _REG_NAMES_MIPS, _REG_NAMES_POWERPC, _REG_NAMES_AArch64 24 | from ..model.locations import ExprOp, LocationType 25 | from typing import List, Optional, Union 26 | from io import BytesIO 27 | 28 | DW_OP_addr = 0x03 29 | DW_OP_deref = 0x06 30 | DW_OP_const1u = 0x08 31 | DW_OP_const1s = 0x09 32 | DW_OP_const2u = 0x0a 33 | DW_OP_const2s = 0x0b 34 | DW_OP_const4u = 0x0c 35 | DW_OP_const4s = 0x0d 36 | DW_OP_const8u = 0x0e 37 | DW_OP_const8s = 0x0f 38 | DW_OP_constu = 0x10 39 | DW_OP_consts = 0x11 40 | DW_OP_dup = 0x12 41 | DW_OP_drop = 0x13 42 | DW_OP_over = 0x14 43 | DW_OP_pick = 0x15 44 | DW_OP_swap = 0x16 45 | DW_OP_rot = 0x17 46 | DW_OP_xderef = 0x18 47 | DW_OP_abs = 0x19 48 | DW_OP_and = 0x1a 49 | DW_OP_div = 0x1b 50 | DW_OP_minus = 0x1c 51 | DW_OP_mod = 0x1d 52 | DW_OP_mul = 0x1e 53 | DW_OP_neg = 0x1f 54 | DW_OP_not = 0x20 55 | DW_OP_or = 0x21 56 | DW_OP_plus = 0x22 57 | DW_OP_plus_uconst = 0x23 58 | DW_OP_shl = 0x24 59 | DW_OP_shr = 0x25 60 | DW_OP_shra = 0x26 61 | DW_OP_xor = 0x27 62 | DW_OP_bra = 0x28 63 | DW_OP_eq = 0x29 64 | DW_OP_ge = 0x2a 65 | DW_OP_gt = 0x2b 66 | DW_OP_le = 0x2c 67 | DW_OP_lt = 0x2d 68 | DW_OP_ne = 0x2e 69 | DW_OP_skip = 0x2f 70 | DW_OP_lit0 = 0x30 71 | DW_OP_lit31 = 0x4f 72 | DW_OP_reg0 = 0x50 73 | DW_OP_reg31 = 0x6f 74 | DW_OP_breg0 = 0x70 75 | DW_OP_breg31 = 0x8f 76 | DW_OP_regx = 0x90 77 | DW_OP_fbreg = 0x91 78 | DW_OP_bregx = 0x92 79 | DW_OP_piece = 0x93 80 | DW_OP_deref_size = 0x94 81 | DW_OP_xderef_size = 0x95 82 | DW_OP_nop = 0x96 83 | DW_OP_push_object_address = 0x97 84 | DW_OP_call2 = 0x98 85 | DW_OP_call4 = 0x99 86 | DW_OP_call_ref = 0x9a 87 | DW_OP_form_tls_address = 0x9b 88 | DW_OP_call_frame_cfa = 0x9c 89 | DW_OP_bit_piece = 0x9d 90 | DW_OP_implicit_value = 0x9e 91 | DW_OP_stack_value = 0x9f 92 | DW_OP_implicit_pointer = 0xa0 93 | DW_OP_addrx = 0xa1 94 | DW_OP_constx = 0xa2 95 | DW_OP_entry_value = 0xa3 96 | DW_OP_const_type = 0xa4 97 | DW_OP_regval_type = 0xa5 98 | DW_OP_deref_type = 0xa6 99 | DW_OP_xderef_type = 0xa7 100 | DW_OP_convert = 0xa8 101 | DW_OP_reinterpret = 0xa9 102 | 103 | DW_OP_lo_user = 0xe0 104 | DW_OP_GNU_push_tls_address = 0xe0, 105 | DW_OP_GNU_implicit_pointer = 0xf2, 106 | DW_OP_GNU_entry_value = 0xf3, 107 | DW_OP_GNU_const_type = 0xf4, 108 | DW_OP_GNU_regval_type = 0xf5, 109 | DW_OP_GNU_deref_type = 0xf6, 110 | DW_OP_GNU_convert = 0xf7, 111 | DW_OP_GNU_reinterpret = 0xf9, 112 | DW_OP_GNU_parameter_ref = 0xfa, 113 | DW_OP_GNU_addr_index = 0xfb, 114 | DW_OP_GNU_const_index = 0xfc, 115 | DW_OP_hi_user = 0xff 116 | 117 | 118 | class StaticExprEvaluator(object): 119 | def __init__(self, structs): 120 | self.structs = structs 121 | self._init_dispatch_table() 122 | self.stream = None 123 | self._cur_opcode = None 124 | self._cur_args = [] 125 | 126 | def process_expr(self, expr: List[int]): 127 | self.save_expr = expr 128 | self.stream = BytesIO(bytelist2string(expr)) 129 | 130 | while True: 131 | byte = self.stream.read(1) 132 | if len(byte) == 0: 133 | break 134 | 135 | self._cur_opcode = ord(byte) 136 | self._cur_args = [] 137 | 138 | visitor = self._dispatch_table.get(self._cur_opcode, self._default_visitor) 139 | visitor(self._cur_opcode) 140 | 141 | ret = self._after_visit(self._cur_opcode, self._cur_args) 142 | if ret is not None and ret is False: 143 | break 144 | 145 | def _after_visit(self, opcode, args): 146 | pass 147 | 148 | def _default_visitor(self, opcode): 149 | pass 150 | 151 | def _visit_OP_with_no_args(self, opcode): 152 | self._cur_args = [] 153 | 154 | def _visit_OP_addr(self, opcode): 155 | self._cur_args = [ 156 | struct_parse(self.structs.Dwarf_target_addr(''), self.stream)] 157 | 158 | def _make_visitor_arg_struct(self, struct_arg): 159 | def visitor(opcode): 160 | self._cur_args = [struct_parse(struct_arg, self.stream)] 161 | return visitor 162 | 163 | def _make_visitor_arg_struct2(self, struct_arg1, struct_arg2): 164 | def visitor(opcode): 165 | self._cur_args = [ 166 | struct_parse(struct_arg1, self.stream), 167 | struct_parse(struct_arg2, self.stream)] 168 | return visitor 169 | 170 | def _make_visitor_arg_variable_len(self): 171 | def visitor(opcode): 172 | assert(self.stream is not None) 173 | n = struct_parse(self.structs.Dwarf_uleb128(''), self.stream) 174 | self._cur_args = [self.stream.read(n)] 175 | return visitor 176 | 177 | def _init_dispatch_table(self): 178 | self._dispatch_table = {} 179 | 180 | def add(opcode_name, func): 181 | self._dispatch_table[DW_OP_name2opcode[opcode_name]] = func 182 | 183 | add('DW_OP_addr', self._visit_OP_addr) 184 | add('DW_OP_const1u', self._make_visitor_arg_struct(self.structs.Dwarf_uint8(''))) 185 | add('DW_OP_const1s', self._make_visitor_arg_struct(self.structs.Dwarf_int8(''))) 186 | add('DW_OP_const2u', self._make_visitor_arg_struct(self.structs.Dwarf_uint16(''))) 187 | add('DW_OP_const2s', self._make_visitor_arg_struct(self.structs.Dwarf_int16(''))) 188 | add('DW_OP_const4u', self._make_visitor_arg_struct(self.structs.Dwarf_uint32(''))) 189 | add('DW_OP_const4s', self._make_visitor_arg_struct(self.structs.Dwarf_int32(''))) 190 | add('DW_OP_const8u', self._make_visitor_arg_struct(self.structs.Dwarf_uint64(''))) 191 | add('DW_OP_const8s', self._make_visitor_arg_struct(self.structs.Dwarf_int64(''))) 192 | add('DW_OP_constu', self._make_visitor_arg_struct(self.structs.Dwarf_uleb128(''))) 193 | add('DW_OP_consts', self._make_visitor_arg_struct(self.structs.Dwarf_sleb128(''))) 194 | add('DW_OP_pick', self._make_visitor_arg_struct(self.structs.Dwarf_uint8(''))) 195 | add('DW_OP_plus_uconst', self._make_visitor_arg_struct(self.structs.Dwarf_uleb128(''))) 196 | add('DW_OP_bra', self._make_visitor_arg_struct(self.structs.Dwarf_int16(''))) 197 | add('DW_OP_skip', self._make_visitor_arg_struct(self.structs.Dwarf_int16(''))) 198 | add('DW_OP_fbreg', self._make_visitor_arg_struct(self.structs.Dwarf_sleb128(''))) 199 | add('DW_OP_regx', self._make_visitor_arg_struct(self.structs.Dwarf_uleb128(''))) 200 | add('DW_OP_bregx', self._make_visitor_arg_struct2(self.structs.Dwarf_uleb128(''), self.structs.Dwarf_sleb128(''))) 201 | add('DW_OP_piece', self._make_visitor_arg_struct(self.structs.Dwarf_uleb128(''))) 202 | add('DW_OP_bit_piece', self._make_visitor_arg_struct2(self.structs.Dwarf_uleb128(''), self.structs.Dwarf_uleb128(''))) 203 | add('DW_OP_deref_size', self._make_visitor_arg_struct(self.structs.Dwarf_int8(''))) 204 | add('DW_OP_xderef_size', self._make_visitor_arg_struct(self.structs.Dwarf_int8(''))) 205 | add('DW_OP_call2', self._make_visitor_arg_struct(self.structs.Dwarf_uint16(''))) 206 | add('DW_OP_call4', self._make_visitor_arg_struct(self.structs.Dwarf_uint32(''))) 207 | add('DW_OP_call_ref', self._make_visitor_arg_struct(self.structs.Dwarf_offset(''))) 208 | add('DW_OP_implicit_value', self._make_visitor_arg_variable_len()) 209 | for n in range(0, 32): 210 | add('DW_OP_lit%s' % n, self._visit_OP_with_no_args) 211 | add('DW_OP_reg%s' % n, self._visit_OP_with_no_args) 212 | add('DW_OP_breg%s' % n, self._make_visitor_arg_struct(self.structs.Dwarf_sleb128(''))) 213 | for opname in [ 214 | 'DW_OP_deref', 'DW_OP_dup', 'DW_OP_drop', 'DW_OP_over', 215 | 'DW_OP_swap', 'DW_OP_swap', 'DW_OP_rot', 'DW_OP_xderef', 216 | 'DW_OP_abs', 'DW_OP_and', 'DW_OP_div', 'DW_OP_minus', 217 | 'DW_OP_mod', 'DW_OP_mul', 'DW_OP_neg', 'DW_OP_not', 218 | 'DW_OP_plus', 'DW_OP_shl', 'DW_OP_shr', 'DW_OP_shra', 219 | 'DW_OP_xor', 'DW_OP_eq', 'DW_OP_ge', 'DW_OP_gt', 220 | 'DW_OP_le', 'DW_OP_lt', 'DW_OP_ne', 'DW_OP_nop', 221 | 'DW_OP_push_object_address', 'DW_OP_form_tls_address', 222 | 'DW_OP_call_frame_cfa' 223 | ]: 224 | add(opname, self._visit_OP_with_no_args) 225 | 226 | 227 | class ExprEval(StaticExprEvaluator): 228 | def __init__(self, structs, arch): 229 | super(ExprEval, self).__init__(structs) 230 | self._arch = arch 231 | self._init_lookups() 232 | self._frame_base = None 233 | self._stack: List[Union[int, str, ExprOp]] = list() 234 | self._is_stack_value = False 235 | self._is_setting_frame_base = False 236 | 237 | def clear(self): 238 | self._stack = list() 239 | self._is_stack_value = False 240 | self._is_setting_frame_base = False 241 | 242 | @property 243 | def stack(self): 244 | return self._stack 245 | 246 | @property 247 | def frame_base(self): 248 | return self._frame_base 249 | 250 | @property 251 | def value(self): 252 | return self._stack[-1] 253 | 254 | def set_frame_base(self, expr): 255 | if expr is None: 256 | self._frame_base = None 257 | return 258 | self.clear() 259 | self._is_setting_frame_base = True 260 | self.process_expr(expr) 261 | self._is_setting_frame_base = False 262 | if self._stack == [ExprOp.CFA]: 263 | self._frame_base = ExprOp.CFA 264 | else: 265 | self._frame_base = None 266 | 267 | def _init_lookups(self): 268 | self._const_ops = set([ 269 | DW_OP_addr, 270 | DW_OP_const1u, DW_OP_const1s, DW_OP_const2u, DW_OP_const2s, 271 | DW_OP_const4u, DW_OP_const4s, DW_OP_constu, DW_OP_consts, 272 | DW_OP_const8u, DW_OP_const8s 273 | ]) 274 | self._ops_with_decimal_arg = set([ 275 | 'DW_OP_const1u', 'DW_OP_const1s', 'DW_OP_const2u', 'DW_OP_const2s', 276 | 'DW_OP_const4u', 'DW_OP_const4s', 'DW_OP_constu', 'DW_OP_consts', 277 | 'DW_OP_pick', 'DW_OP_plus_uconst', 'DW_OP_bra', 'DW_OP_skip', 278 | 'DW_OP_fbreg', 'DW_OP_piece', 'DW_OP__size', 279 | 'DW_OP_xderef_size', 'DW_OP_regx', 'DW_OP_const8u', 'DW_OP_const8s']) 280 | 281 | for n in range(0, 32): 282 | self._ops_with_decimal_arg.add('DW_OP_breg%s' % n) 283 | 284 | self._ops_with_two_decimal_args = set([ 285 | 'DW_OP_bregx', 'DW_OP_bit_piece']) 286 | 287 | self._ops_with_hex_arg = set( 288 | ['DW_OP_addr', 'DW_OP_call2', 'DW_OP_call4', 'DW_OP_call_ref']) 289 | 290 | self._dynamic_ops = set([ 291 | DW_OP_shl, 292 | DW_OP_deref, 293 | DW_OP_deref_size, 294 | DW_OP_pick, 295 | DW_OP_abs 296 | ]) 297 | self._unsupported_ops = set([ 298 | DW_OP_piece, 299 | DW_OP_dup, 300 | DW_OP_bra 301 | ]) 302 | 303 | def _after_visit(self, opcode, args) -> Optional[bool]: 304 | if opcode == DW_OP_stack_value: 305 | self._is_stack_value = True 306 | return False 307 | elif opcode in self._const_ops: 308 | self._stack.append(args[0]) 309 | elif DW_OP_lit0 <= opcode and opcode <= DW_OP_lit31: 310 | self._stack.append(opcode - DW_OP_lit0) 311 | elif DW_OP_reg0 <= opcode and opcode <= DW_OP_reg31: 312 | regnum = opcode - DW_OP_reg0 313 | self._stack.append(describe_reg_name(regnum, self._arch)) 314 | elif DW_OP_breg0 <= opcode and opcode <= DW_OP_breg31: 315 | regnum = opcode - DW_OP_breg0 316 | regname = describe_reg_name(regnum, self._arch) 317 | self._stack.extend([regname, args[0], ExprOp.ADD]) 318 | elif opcode == DW_OP_fbreg and isinstance(self._frame_base, ExprOp): 319 | self._stack.extend([self._frame_base, args[0], ExprOp.ADD]) 320 | elif opcode == DW_OP_fbreg and self._frame_base is None: 321 | self._stack.extend([ExprOp.CFA, args[0], ExprOp.ADD]) 322 | elif opcode == DW_OP_regx: 323 | regnum = args[0] 324 | regname = describe_reg_name(regnum, self._arch) 325 | self._stack.append(regname) 326 | elif opcode == DW_OP_bregx: 327 | regnum = args[0] 328 | regname = describe_reg_name(regnum, self._arch) 329 | self._stack.extend([regname, args[1], ExprOp.ADD]) 330 | elif opcode == DW_OP_piece and len(self._stack) == 0: 331 | pass 332 | elif opcode == DW_OP_piece and len(self._stack) > 0 and isinstance(self.stack[-1], str): 333 | self._stack.extend([args[0], ExprOp.VAR_FIELD]) 334 | elif opcode == DW_OP_bit_piece and len(self._stack) == 0: 335 | pass 336 | elif opcode == DW_OP_bit_piece and len(self._stack) > 0 and isinstance(self.stack[-1], str) and args[1] == 0: 337 | pass 338 | elif opcode == DW_OP_call_frame_cfa: 339 | self._stack.append(ExprOp.CFA) 340 | elif opcode in self._dynamic_ops: 341 | self._stack.append(ExprOp.DYNAMIC) 342 | return False 343 | elif opcode == DW_OP_plus: 344 | self._stack.append(ExprOp.ADD) 345 | elif opcode == DW_OP_not: 346 | self._stack.append(ExprOp.NOT) 347 | elif opcode == DW_OP_neg: 348 | self._stack.append(ExprOp.NEG) 349 | elif opcode == DW_OP_or: 350 | self._stack.append(ExprOp.OR) 351 | elif opcode == DW_OP_ne: 352 | self._stack.append(ExprOp.NE) 353 | elif opcode == DW_OP_eq: 354 | self._stack.append(ExprOp.EQ) 355 | elif opcode == DW_OP_le: 356 | self._stack.append(ExprOp.LE) 357 | elif opcode == DW_OP_ge: 358 | self._stack.append(ExprOp.GE) 359 | elif opcode == DW_OP_gt: 360 | self._stack.append(ExprOp.GT) 361 | elif opcode == DW_OP_lt: 362 | self._stack.append(ExprOp.LT) 363 | elif opcode == DW_OP_and: 364 | self._stack.append(ExprOp.AND) 365 | elif opcode == DW_OP_minus: 366 | self._stack.append(ExprOp.MINUS) 367 | elif opcode == DW_OP_shra: 368 | self._stack.append(ExprOp.ASHR) 369 | elif opcode == DW_OP_xor: 370 | self._stack.append(ExprOp.XOR) 371 | elif opcode == DW_OP_mul: 372 | self._stack.append(ExprOp.MUL) 373 | elif opcode == DW_OP_mod: 374 | self._stack.append(ExprOp.MOD) 375 | elif opcode == DW_OP_div: 376 | self._stack.append(ExprOp.DIV) 377 | elif opcode == DW_OP_shr: 378 | self._stack.append(ExprOp.SHR) 379 | elif opcode == DW_OP_plus_uconst: 380 | self._stack.append(ExprOp.PLUS_IMM) 381 | self._stack.append(args[0]) 382 | elif opcode == DW_OP_over: 383 | self._stack.append(ExprOp.OVER) 384 | elif opcode == DW_OP_implicit_value: 385 | v = int.from_bytes(args[0], 'little') 386 | self._stack.append(v) 387 | self._is_stack_value = True 388 | 389 | elif DW_OP_lo_user <= opcode and opcode <= DW_OP_hi_user: 390 | self._stack.append(ExprOp.UNSUPPORTED) 391 | return False 392 | elif opcode in self._unsupported_ops: 393 | self._stack.append(ExprOp.UNSUPPORTED) 394 | return False 395 | else: 396 | if not self._is_setting_frame_base: 397 | print('Expr:', [hex(x) for x in self.save_expr]) 398 | print('Args:', args) 399 | print('Stack:', self._stack) 400 | print('Frame:', self._frame_base) 401 | raise Exception( 402 | f'unimplemented opcode: ' 403 | f'{hex(opcode)} {DW_OP_opcode2name.get(opcode,"UNK")}\nFrame: {self._frame_base}') 404 | 405 | 406 | class LocExprParser(StaticExprEvaluator): 407 | def __init__(self, structs, arch): 408 | super(LocExprParser, self).__init__(structs) 409 | self._arch = arch 410 | self._init_lookups() 411 | self._frame_base = None 412 | self._stack: List[Union[int, str, ExprOp]] = list() 413 | self._is_stack_value = False 414 | self._is_setting_frame_base = False 415 | 416 | def clear(self): 417 | self._stack = list() 418 | self._is_stack_value = False 419 | self._is_setting_frame_base = False 420 | self._loc_type = None 421 | 422 | def reset(self): 423 | self._stack = list() 424 | self._is_stack_value = False 425 | self._is_setting_frame_base = False 426 | self._loc_type = None 427 | 428 | def parse(self, loc_expr): 429 | self.clear() 430 | self.process_expr(loc_expr) 431 | if len(self._stack) == 0: 432 | self._loc_type = None 433 | elif self._stack[-1] == ExprOp.DYNAMIC: 434 | self._loc_type = LocationType.DYNAMIC 435 | elif self._stack[-1] == ExprOp.UNSUPPORTED: 436 | self._loc_type = LocationType.UNSUPPORTED 437 | else: 438 | if len(self._stack) == 1 and isinstance(self._stack[-1], int): 439 | if self._is_stack_value: 440 | self._loc_type = None 441 | else: 442 | self._loc_type = LocationType.STATIC_GLOBAL 443 | else: 444 | self._loc_type = LocationType.STATIC_LOCAL 445 | 446 | @property 447 | def location_type(self): 448 | return self._loc_type 449 | 450 | @property 451 | def stack(self): 452 | return self._stack 453 | 454 | @property 455 | def frame_base(self): 456 | return self._frame_base 457 | 458 | @property 459 | def value(self): 460 | return self._stack[-1] 461 | 462 | def set_frame_base(self, expr): 463 | if expr is None: 464 | self._frame_base = None 465 | return 466 | self.clear() 467 | self._is_setting_frame_base = True 468 | self.process_expr(expr) 469 | self._is_setting_frame_base = False 470 | if self._stack == [ExprOp.CFA]: 471 | self._frame_base = ExprOp.CFA 472 | else: 473 | self._frame_base = None 474 | 475 | def _init_lookups(self): 476 | self._const_ops = set([ 477 | DW_OP_addr, 478 | DW_OP_const1u, DW_OP_const1s, DW_OP_const2u, DW_OP_const2s, 479 | DW_OP_const4u, DW_OP_const4s, DW_OP_constu, DW_OP_consts, 480 | DW_OP_const8u, DW_OP_const8s 481 | ]) 482 | self._ops_with_decimal_arg = set([ 483 | 'DW_OP_const1u', 'DW_OP_const1s', 'DW_OP_const2u', 'DW_OP_const2s', 484 | 'DW_OP_const4u', 'DW_OP_const4s', 'DW_OP_constu', 'DW_OP_consts', 485 | 'DW_OP_pick', 'DW_OP_plus_uconst', 'DW_OP_bra', 'DW_OP_skip', 486 | 'DW_OP_fbreg', 'DW_OP_piece', 'DW_OP__size', 487 | 'DW_OP_xderef_size', 'DW_OP_regx', 'DW_OP_const8u', 'DW_OP_const8s']) 488 | 489 | for n in range(0, 32): 490 | self._ops_with_decimal_arg.add('DW_OP_breg%s' % n) 491 | 492 | self._ops_with_two_decimal_args = set([ 493 | 'DW_OP_bregx', 'DW_OP_bit_piece']) 494 | 495 | self._ops_with_hex_arg = set( 496 | ['DW_OP_addr', 'DW_OP_call2', 'DW_OP_call4', 'DW_OP_call_ref']) 497 | 498 | self._dynamic_ops = set([ 499 | DW_OP_shl, 500 | DW_OP_deref, 501 | DW_OP_deref_size, 502 | DW_OP_pick, 503 | DW_OP_abs 504 | ]) 505 | self._unsupported_ops = set([ 506 | DW_OP_piece, 507 | DW_OP_bit_piece, 508 | DW_OP_dup, 509 | DW_OP_bra, 510 | 0x2, 511 | 0x0 512 | ]) 513 | 514 | def _reg_list(self): 515 | if self._arch == "AArch64": 516 | return _REG_NAMES_AArch64 517 | if self._arch == "x86": 518 | return _REG_NAMES_x86 519 | if self._arch == "x64": 520 | return _REG_NAMES_x64 521 | if self._arch == 'ARM': 522 | return _REG_NAMES_ARM 523 | if self._arch == 'MIPS': 524 | return _REG_NAMES_MIPS 525 | if self._arch == 'PowerPC': 526 | return _REG_NAMES_POWERPC 527 | assert False, 'unrecognized arch: %s' % self._arch 528 | 529 | def _after_visit(self, opcode, args) -> Optional[bool]: 530 | if opcode == DW_OP_stack_value: 531 | self._is_stack_value = True 532 | return False 533 | elif opcode in self._const_ops: 534 | self._stack.append(args[0]) 535 | elif DW_OP_lit0 <= opcode and opcode <= DW_OP_lit31: 536 | self._stack.append(opcode - DW_OP_lit0) 537 | elif DW_OP_reg0 <= opcode and opcode <= DW_OP_reg31: 538 | regnum = opcode - DW_OP_reg0 539 | self._stack.append(describe_reg_name(regnum, self._arch)) 540 | elif DW_OP_breg0 <= opcode and opcode <= DW_OP_breg31: 541 | regnum = opcode - DW_OP_breg0 542 | regname = describe_reg_name(regnum, self._arch) 543 | self._stack.extend([regname, args[0], ExprOp.ADD]) 544 | self._stack.append(ExprOp.DYNAMIC) 545 | return False 546 | elif opcode == DW_OP_fbreg and isinstance(self._frame_base, ExprOp): 547 | self._stack.extend([self._frame_base, args[0], ExprOp.ADD]) 548 | elif opcode == DW_OP_fbreg and self._frame_base is None: 549 | self._stack.extend([ExprOp.CFA, args[0], ExprOp.ADD]) 550 | elif opcode == DW_OP_regx: 551 | regnum = args[0] 552 | if regnum < len(self._reg_list()): 553 | regname = describe_reg_name(regnum, self._arch) 554 | self._stack.append(regname) 555 | else: 556 | print('Unsupported reg num:', regnum) 557 | elif opcode == DW_OP_bregx: 558 | regnum = args[0] 559 | if regnum < len(self._reg_list()): 560 | regname = describe_reg_name(regnum, self._arch) 561 | self._stack.extend([regname, args[1], ExprOp.ADD]) 562 | else: 563 | print('Unsupported reg num:', regnum) 564 | elif opcode == DW_OP_piece and len(self._stack) == 0: 565 | pass 566 | elif opcode == DW_OP_piece and len(self._stack) > 0 and isinstance(self.stack[-1], str): 567 | self._stack.extend([args[0], ExprOp.VAR_FIELD]) 568 | elif opcode == DW_OP_bit_piece and len(self._stack) == 0: 569 | pass 570 | elif opcode == DW_OP_bit_piece and len(self._stack) > 0 and isinstance(self.stack[-1], str) and args[1] == 0: 571 | pass 572 | elif opcode == DW_OP_call_frame_cfa: 573 | self._stack.append(ExprOp.CFA) 574 | elif opcode in self._dynamic_ops: 575 | self._stack.append(ExprOp.DYNAMIC) 576 | return False 577 | elif opcode == DW_OP_plus: 578 | self._stack.append(ExprOp.ADD) 579 | elif opcode == DW_OP_not: 580 | self._stack.append(ExprOp.NOT) 581 | elif opcode == DW_OP_neg: 582 | self._stack.append(ExprOp.NEG) 583 | elif opcode == DW_OP_or: 584 | self._stack.append(ExprOp.OR) 585 | elif opcode == DW_OP_ne: 586 | self._stack.append(ExprOp.NE) 587 | elif opcode == DW_OP_eq: 588 | self._stack.append(ExprOp.EQ) 589 | elif opcode == DW_OP_le: 590 | self._stack.append(ExprOp.LE) 591 | elif opcode == DW_OP_ge: 592 | self._stack.append(ExprOp.GE) 593 | elif opcode == DW_OP_gt: 594 | self._stack.append(ExprOp.GT) 595 | elif opcode == DW_OP_lt: 596 | self._stack.append(ExprOp.LT) 597 | elif opcode == DW_OP_and: 598 | self._stack.append(ExprOp.AND) 599 | elif opcode == DW_OP_minus: 600 | self._stack.append(ExprOp.MINUS) 601 | elif opcode == DW_OP_shra: 602 | self._stack.append(ExprOp.ASHR) 603 | elif opcode == DW_OP_xor: 604 | self._stack.append(ExprOp.XOR) 605 | elif opcode == DW_OP_mul: 606 | self._stack.append(ExprOp.MUL) 607 | elif opcode == DW_OP_mod: 608 | self._stack.append(ExprOp.MOD) 609 | elif opcode == DW_OP_div: 610 | self._stack.append(ExprOp.DIV) 611 | elif opcode == DW_OP_shr: 612 | self._stack.append(ExprOp.SHR) 613 | elif opcode == DW_OP_plus_uconst: 614 | self._stack.append(ExprOp.PLUS_IMM) 615 | self._stack.append(args[0]) 616 | elif opcode == DW_OP_over: 617 | self._stack.append(ExprOp.OVER) 618 | elif opcode == DW_OP_implicit_value: 619 | v = int.from_bytes(args[0], 'little') 620 | self._stack.append(v) 621 | self._is_stack_value = True 622 | 623 | elif DW_OP_lo_user <= opcode and opcode <= DW_OP_hi_user: 624 | self._stack.append(ExprOp.UNSUPPORTED) 625 | return False 626 | elif opcode in self._unsupported_ops: 627 | self._stack.append(ExprOp.UNSUPPORTED) 628 | return False 629 | else: 630 | if not self._is_setting_frame_base: 631 | print('Expr:', [hex(x) for x in self.save_expr]) 632 | print('Args:', args) 633 | print('Stack:', self._stack) 634 | print('Frame:', self._frame_base) 635 | raise Exception( 636 | f'unimplemented opcode: ' 637 | f'{hex(opcode)} {DW_OP_opcode2name.get(opcode,"UNK")}\nFrame: {self._frame_base}') 638 | -------------------------------------------------------------------------------- /io/dwarf_import.py: -------------------------------------------------------------------------------- 1 | # Copyright(c) 2020-2023 Vector 35 Inc 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files(the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and / or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | # Copyright (c) 2020 Vector 35 Inc 21 | 22 | import os 23 | import sys 24 | import elftools 25 | import elftools.dwarf.ranges 26 | import elftools.dwarf.constants as dw 27 | import logging 28 | from enum import Enum, auto 29 | from collections import defaultdict 30 | from itertools import tee 31 | from typing import MutableMapping, Iterator, List, Set, Optional, Union, Tuple, Any, ValuesView 32 | from elftools.elf.elffile import ELFFile, SymbolTableSection 33 | from elftools.dwarf.compileunit import CompileUnit 34 | from .dwarf_expr import LocExprParser 35 | from elftools.dwarf.die import AttributeValue, DIE 36 | from elftools.dwarf.locationlists import LocationEntry, BaseAddressEntry 37 | from ..model import QualifiedName, Component, AnalysisModel 38 | from ..model.locations import ExprOp, Location, LocationType 39 | from ..model.concrete_elements import ( 40 | Element, 41 | Type, 42 | Constant, 43 | Variable, 44 | Function, 45 | BaseType, AliasType, ArrayType, PointerType, 46 | CompositeType, ConstType, VolatileType, StructType, ClassType, UnionType, 47 | EnumType, Enumerator, FunctionType, StringType, PointerToMemberType, 48 | LocalVariable, 49 | Parameter, 50 | Field, 51 | VOID, VOID_PTR, VARIADIC 52 | ) 53 | 54 | 55 | class Language(Enum): 56 | C89 = dw.DW_LANG_C89 57 | C = dw.DW_LANG_C 58 | Ada83 = dw.DW_LANG_Ada83 59 | C_plus_plus = dw.DW_LANG_C_plus_plus 60 | Cobol74 = dw.DW_LANG_Cobol74 61 | Cobol85 = dw.DW_LANG_Cobol85 62 | Fortran77 = dw.DW_LANG_Fortran77 63 | Fortran90 = dw.DW_LANG_Fortran90 64 | Pascal83 = dw.DW_LANG_Pascal83 65 | Modula2 = dw.DW_LANG_Modula2 66 | Java = dw.DW_LANG_Java 67 | C99 = dw.DW_LANG_C99 68 | Ada95 = dw.DW_LANG_Ada95 69 | Fortran95 = dw.DW_LANG_Fortran95 70 | PLI = dw.DW_LANG_PLI 71 | ObjC = dw.DW_LANG_ObjC 72 | ObjC_plus_plus = dw.DW_LANG_ObjC_plus_plus 73 | UPC = dw.DW_LANG_UPC 74 | D = dw.DW_LANG_D 75 | Python = dw.DW_LANG_Python 76 | OpenCL = dw.DW_LANG_OpenCL 77 | Go = dw.DW_LANG_Go 78 | Modula3 = dw.DW_LANG_Modula3 79 | Haskell = dw.DW_LANG_Haskell 80 | C_plus_plus_03 = dw.DW_LANG_C_plus_plus_03 81 | C_plus_plus_11 = dw.DW_LANG_C_plus_plus_11 82 | OCaml = dw.DW_LANG_OCaml 83 | Rust = dw.DW_LANG_Rust 84 | C11 = dw.DW_LANG_C11 85 | Swift = dw.DW_LANG_Swift 86 | Julia = dw.DW_LANG_Julia 87 | Dylan = dw.DW_LANG_Dylan 88 | C_plus_plus_14 = dw.DW_LANG_C_plus_plus_14 89 | Fortran03 = dw.DW_LANG_Fortran03 90 | Fortran08 = dw.DW_LANG_Fortran08 91 | RenderScript = dw.DW_LANG_RenderScript 92 | BLISS = dw.DW_LANG_BLISS 93 | Mips_Assembler = dw.DW_LANG_Mips_Assembler 94 | Upc = dw.DW_LANG_Upc 95 | HP_Bliss = dw.DW_LANG_HP_Bliss 96 | HP_Basic91 = dw.DW_LANG_HP_Basic91 97 | HP_Pascal91 = dw.DW_LANG_HP_Pascal91 98 | HP_IMacro = dw.DW_LANG_HP_IMacro 99 | HP_Assembler = dw.DW_LANG_HP_Assembler 100 | GOOGLE_RenderScript = dw.DW_LANG_GOOGLE_RenderScript 101 | BORLAND_Delphi = dw.DW_LANG_BORLAND_Delphi 102 | 103 | 104 | def partition(items, predicate=bool): 105 | a, b = tee((predicate(item), item) for item in items) 106 | return ((item for pred, item in a if pred), 107 | (item for pred, item in b if not pred)) 108 | 109 | 110 | CONCRETE_SUBPROGRAM_ATTRIBUTES = set([ 111 | 'DW_AT_low_pc', 112 | 'DW_AT_high_pc', 113 | 'DW_AT_ranges', 114 | 'DW_AT_entry_pc', 115 | 'DW_AT_location', 116 | 'DW_AT_return_addr', 117 | 'DW_AT_start_scope', 118 | 'DW_AT_segment' 119 | ]) 120 | 121 | TYPE_TAGS = set([ 122 | 'DW_TAG_array_type', 123 | 'DW_TAG_base_type', 124 | 'DW_TAG_const_type', 125 | 'DW_TAG_pointer_type', 126 | 'DW_TAG_structure_type', 127 | 'DW_TAG_typedef', 128 | 'DW_TAG_union_type' 129 | ]) 130 | 131 | 132 | class SubprogramType(Enum): 133 | IMPORTED = auto() 134 | CONCRETE = auto() 135 | ARTIFICIAL = auto() 136 | 137 | 138 | def has_name(die: DIE) -> bool: 139 | return 'DW_AT_name' in die.attributes 140 | 141 | 142 | def has_size(die: DIE) -> bool: 143 | return 'DW_AT_byte_size' in die.attributes 144 | 145 | 146 | def size_of(die: DIE) -> int: 147 | return die.attributes['DW_AT_byte_size'].value 148 | 149 | 150 | def has_type(die: DIE) -> bool: 151 | return 'DW_AT_type' in die.attributes 152 | 153 | 154 | def has_member_offset(die: DIE) -> bool: 155 | return 'DW_AT_data_member_location' in die.attributes or 'DW_AT_data_bit_offset' in die.attributes 156 | 157 | 158 | class UnresolvableDIEError(Exception): 159 | def __init__(self, source_die, attribute_name): 160 | self.source_die = source_die 161 | self.attribute_name = attribute_name 162 | 163 | def __str__(self): 164 | return f'Unable to resolve the DIE specified by the "{self.attribute_name}" attribute in {self.source_die}' 165 | 166 | def __repr__(self): 167 | return f'' 168 | 169 | 170 | class DWARFData(object): 171 | def __init__(self, elf_file: ELFFile, debug_root: Optional[str] = None): 172 | self._elf_file = elf_file 173 | self._debug_root = debug_root 174 | self._arch = elf_file.get_machine_arch() 175 | self._dwarf_info = elf_file.get_dwarf_info() 176 | self._range_lists = self._dwarf_info.range_lists() 177 | self._location_lists = self._dwarf_info.location_lists() 178 | self._die_map = dict() 179 | self._line_programs = dict() 180 | self._debug_str = None 181 | self._logger = logging.getLogger('sidekick.DWARFData') 182 | self._index() 183 | 184 | def _index(self): 185 | for cu in self._dwarf_info.iter_CUs(): 186 | self._die_map.update({die.offset: die for die in filter(None, cu.iter_DIEs())}) 187 | 188 | def get_alt_filename(self) -> Optional[str]: 189 | section = self._elf_file.get_section_by_name('.gnu_debugaltlink') 190 | if section is None: 191 | section = self._elf_file.get_section_by_name('.gnu_debuglink') 192 | if section is None: 193 | section = self._elf_file.get_section_by_name('.debug_sup') 194 | if section is None: 195 | return None 196 | b: bytes = section.data() 197 | end = b.find(0) 198 | alt_filename = b[:end].decode('utf-8') 199 | if alt_filename[0] == '/': 200 | alt_filename = alt_filename[1:] 201 | if self._debug_root is None: 202 | return None 203 | alt_filename = os.path.join(self._debug_root, alt_filename) 204 | return alt_filename 205 | 206 | def iter_compile_units(self) -> Iterator[DIE]: 207 | return filter(lambda die: die.tag == 'DW_TAG_compile_unit', map(lambda cu: cu.get_top_DIE(), self._dwarf_info.iter_CUs())) 208 | 209 | def iter_partial_units(self) -> Iterator[DIE]: 210 | return filter(lambda die: die.tag == 'DW_TAG_partial_unit', map(lambda cu: cu.get_top_DIE(), self._dwarf_info.iter_CUs())) 211 | 212 | def iter_section_starts(self) -> Iterator[int]: 213 | return filter(lambda addr: addr > 0, map(lambda section: section['sh_addr'], self._elf_file.iter_sections())) 214 | 215 | def get_die_at_offset(self, offset) -> DIE: 216 | if offset not in self._die_map: 217 | raise ValueError(f'offset ({offset}) not in DIE map') 218 | return self._die_map[offset] 219 | 220 | def get_location_list(self, ll_offset) -> List[Union[LocationEntry, BaseAddressEntry]]: 221 | assert(self._location_lists) 222 | return self._location_lists.get_location_list_at_offset(ll_offset) 223 | 224 | def get_line_program(self, die: DIE): 225 | memo_key = die.cu.cu_die_offset 226 | if memo_key in self._line_programs: 227 | return self._line_programs[memo_key] 228 | line_program = self._dwarf_info.line_program_for_CU(die.cu) 229 | return self._line_programs.setdefault(memo_key, line_program) 230 | 231 | def get_range_list_at_offset(self, offset): 232 | assert(self._range_lists) 233 | return self._range_lists.get_range_list_at_offset(offset) 234 | 235 | def get_debug_str_at_offset(self, offset: int): 236 | if self._debug_str is None: 237 | section = self._elf_file.get_section_by_name('.debug_str') 238 | assert(section is not None) 239 | self._debug_str = section.data() 240 | 241 | end = self._debug_str.find(0, offset) 242 | if end == -1: 243 | end = len(self._debug_str) 244 | b = self._debug_str[offset:end] 245 | return b.decode('utf-8') 246 | 247 | 248 | class DWARFDB(object): 249 | REFERENCE_FORMS = { 250 | 'DW_FORM_ref1', 251 | 'DW_FORM_ref2', 252 | 'DW_FORM_ref4', 253 | 'DW_FORM_ref8', 254 | 'DW_FORM_ref_addr', 255 | 'DW_FORM_ref_sig8', 256 | 'DW_FORM_ref_sup4', 257 | 'DW_FORM_ref_sup8' 258 | } 259 | 260 | def __init__(self, elf_file: ELFFile, debug_root: Optional[str] = None): 261 | self._logger = logging.getLogger('sidekick.DWARFDB') 262 | self._base_addresses = dict() 263 | self._pri = DWARFData(elf_file, debug_root) 264 | self._sup = None 265 | sup_filename = self._pri.get_alt_filename() 266 | if sup_filename: 267 | assert(debug_root) 268 | self._sup = DWARFData(ELFFile(open(os.path.join(debug_root, sup_filename), 'rb'))) 269 | self._external_decl_files = dict() 270 | self._index_dwarf_data() 271 | 272 | @property 273 | def default_address_size(self) -> int: 274 | return self._pri._dwarf_info.config.default_address_size 275 | 276 | def set_external_decl_file(self, name, filename): 277 | self._external_decl_files[name] = filename 278 | 279 | def has_external_decl_file(self, name): 280 | return name in self._external_decl_files 281 | 282 | def get_external_decl_file(self, name): 283 | return self._external_decl_files[name] 284 | 285 | def _index_dwarf_data(self): 286 | for top_die in self._pri.iter_partial_units(): 287 | for die in top_die.iter_children(): 288 | if die.tag == 'DW_TAG_subprogram': 289 | name = self.get_name(die) 290 | if 'DW_AT_decl_file' in die.attributes: 291 | self.set_external_decl_file(name, self.get_decl_file(die)) 292 | 293 | def process_imported_units(self, unit_die: DIE): 294 | self._logger.debug(f'Importing unit at {unit_die.cu.cu_offset}') 295 | for child in unit_die.iter_children(): 296 | if child.tag == 'DW_TAG_imported_unit': 297 | imported_unit_die = self.get_attr_as_die(child, 'DW_AT_import') 298 | if imported_unit_die is not None: 299 | self.process_imported_units(imported_unit_die) 300 | 301 | def iter_compile_units(self) -> Iterator[DIE]: 302 | return self._pri.iter_compile_units() 303 | 304 | def get_attribute(self, die: DIE, attr_name: str, inheritable: bool = True) -> Optional[Tuple[DIE, AttributeValue]]: 305 | attr_value = die.attributes.get(attr_name, None) 306 | if attr_value: 307 | return (die, attr_value) 308 | if not inheritable: 309 | return None 310 | if 'DW_AT_specification' in die.attributes: 311 | specification = self.get_attr_as_die(die, 'DW_AT_specification') 312 | assert(specification) 313 | result = self.get_attribute(specification, attr_name) 314 | if result: 315 | return result 316 | if 'DW_AT_abstract_origin' in die.attributes: 317 | abstract_origin = self.get_attr_as_die(die, 'DW_AT_abstract_origin') 318 | assert(abstract_origin) 319 | return self.get_attribute(abstract_origin, attr_name) 320 | 321 | def get_attr_as_die(self, die: DIE, attr_name: str) -> Optional[DIE]: 322 | result = self.get_attribute(die, attr_name) 323 | if result is None: 324 | return None 325 | attr_die, attr_value = result 326 | 327 | if attr_value.form in ['DW_FORM_ref1', 'DW_FORM_ref2', 'DW_FORM_ref4', 'DW_FORM_ref8', 'DW_FORM_ref_udata']: 328 | if attr_die.cu.dwarfinfo == self._pri._dwarf_info: 329 | return self._pri.get_die_at_offset(attr_value.value + attr_die.cu.cu_offset) 330 | elif self._sup is not None: 331 | return self._sup.get_die_at_offset(attr_value.value + attr_die.cu.cu_offset) 332 | elif attr_value.form == 'DW_FORM_ref_addr': 333 | if attr_die.cu.dwarfinfo == self._pri._dwarf_info: 334 | return self._pri.get_die_at_offset(attr_value.value) 335 | elif self._sup is not None: 336 | return self._sup.get_die_at_offset(attr_value.value) 337 | elif attr_value.form == 'DW_FORM_GNU_ref_alt': 338 | if self._sup is not None: 339 | return self._sup.get_die_at_offset(attr_value.value) 340 | 341 | self._logger.warning(f'unsupported form ("{attr_value.form}") while getting attribute ("{attr_name}") as DIE') 342 | return None 343 | 344 | def get_attr_as_constant(self, die: DIE, attr_name: str) -> Optional[Any]: 345 | die_attr_pair = self.get_attribute(die, attr_name) 346 | if die_attr_pair is None: 347 | return None 348 | attr_die, attr_value = die_attr_pair 349 | return attr_value.value 350 | 351 | def get_attr_as_string(self, die: DIE, attr_name: str) -> Optional[str]: 352 | die_attr_pair = self.get_attribute(die, attr_name) 353 | if die_attr_pair is None: 354 | return None 355 | attr_die, attr_value = die_attr_pair 356 | 357 | if type(attr_value.value) == bytes: 358 | return attr_value.value.decode('utf-8') 359 | elif attr_value.form == 'DW_FORM_strp': 360 | return self._pri.get_debug_str_at_offset(attr_value.value) 361 | elif attr_value.form in ['DW_FORM_GNU_strp_alt', 'DW_FORM_strp_sup']: 362 | if self._sup is None: 363 | raise Exception('Reference to missing supplemental debug file') 364 | return self._sup.get_debug_str_at_offset(attr_value.value) 365 | return None 366 | 367 | def get_attr_as_int(self, die: DIE, attr_name: str) -> Optional[int]: 368 | die_attr_pair = self.get_attribute(die, attr_name) 369 | if die_attr_pair is None: 370 | return None 371 | attr_die, attr_value = die_attr_pair 372 | assert(isinstance(attr_value.value, int)) 373 | return attr_value.value 374 | 375 | def get_type_attr_as_die(self, die: DIE) -> Optional[DIE]: 376 | return self.get_attr_as_die(die, 'DW_AT_type') 377 | 378 | def get_qualified_name(self, die: DIE) -> QualifiedName: 379 | if ( 380 | die._parent is not None 381 | and die._parent.tag in { 382 | 'DW_TAG_structure_type', 383 | 'DW_TAG_class_type', 384 | 'DW_TAG_interface_type', 385 | 'DW_TAG_union_type', 386 | 'DW_TAG_namespace'} 387 | ): 388 | qname = self.get_qualified_name(die._parent) 389 | uname = self.get_name(die) 390 | if uname is None: 391 | uname = 'anonymous' 392 | return QualifiedName(*qname, uname) 393 | else: 394 | if die.tag in ['DW_TAG_subprogram', 'DW_TAG_inlined_subroutine']: 395 | if 'DW_AT_abstract_origin' in die.attributes: 396 | origin_die = self.get_attr_as_die(die, 'DW_AT_abstract_origin') 397 | assert(origin_die) 398 | return self.get_qualified_name(origin_die) 399 | if 'DW_AT_specification' in die.attributes: 400 | specification = self.get_attr_as_die(die, 'DW_AT_specification') 401 | assert(specification) 402 | return self.get_qualified_name(specification) 403 | n = self.get_name(die) 404 | if n is None: 405 | return QualifiedName() 406 | else: 407 | return QualifiedName(n) 408 | 409 | def get_name(self, die: DIE) -> Optional[str]: 410 | return self.get_attr_as_string(die, 'DW_AT_name') 411 | 412 | def get_start_address(self, die: DIE) -> Optional[int]: 413 | if 'DW_AT_entry_pc' in die.attributes: 414 | return die.attributes['DW_AT_entry_pc'].value 415 | if 'DW_AT_ranges' in die.attributes: 416 | if die.cu.dwarfinfo == self._pri._dwarf_info: 417 | ranges = self._pri.get_range_list_at_offset(die.attributes['DW_AT_ranges'].value) 418 | elif self._sup is not None: 419 | ranges = self._sup.get_range_list_at_offset(die.attributes['DW_AT_ranges'].value) 420 | else: 421 | ranges = [] 422 | for r in ranges: 423 | if type(r) == elftools.dwarf.ranges.BaseAddressEntry: 424 | return r.base_address 425 | else: 426 | return r.begin_offset 427 | if 'DW_AT_low_pc' in die.attributes: 428 | low_pc = die.attributes['DW_AT_low_pc'] 429 | if low_pc.form == 'DW_FORM_addr': 430 | addr = die.attributes['DW_AT_low_pc'].value 431 | return addr if addr != 0 else None 432 | else: 433 | raise NotImplementedError('Unsupported DW_FORM for AT_low_pc') 434 | return None 435 | 436 | def get_ranges(self, die: DIE) -> Optional[List[Tuple[int, int]]]: 437 | ranges = [] 438 | if 'DW_AT_low_pc' in die.attributes: 439 | if 'DW_AT_high_pc' in die.attributes: 440 | high_pc = die.attributes['DW_AT_high_pc'] 441 | if high_pc.form == 'DW_FORM_addr': 442 | ranges.append((die.attributes['DW_AT_low_pc'].value, die.attributes['DW_AT_high_pc'].value)) 443 | else: 444 | base = die.attributes['DW_AT_low_pc'].value 445 | offset = die.attributes['DW_AT_high_pc'].value 446 | ranges.append((base, base + offset)) 447 | else: 448 | ranges.append((die.attributes['DW_AT_low_pc'].value, die.attributes['DW_AT_low_pc'].value)) 449 | elif 'DW_AT_ranges' in die.attributes: 450 | if die.cu.dwarfinfo == self._pri._dwarf_info: 451 | range_list = self._pri.get_range_list_at_offset(die.attributes['DW_AT_ranges'].value) 452 | elif self._sup is not None: 453 | range_list = self._sup.get_range_list_at_offset(die.attributes['DW_AT_ranges'].value) 454 | else: 455 | range_list = [] 456 | base_addr = self.get_cu_base_address(die.cu) 457 | if base_addr is None: 458 | raise Exception('Unable to obtain the compilation unit base address') 459 | for entry in range_list: 460 | if isinstance(entry, elftools.dwarf.ranges.RangeEntry): 461 | ranges.append((base_addr + entry.begin_offset, base_addr + entry.end_offset)) 462 | elif isinstance(entry, elftools.dwarf.ranges.BaseAddressEntry): 463 | base_addr = entry.base_address 464 | else: 465 | assert(False) 466 | return ranges if ranges else None 467 | 468 | def min_mapped_address(self) -> int: 469 | min_addr = sys.maxsize 470 | for start in self._pri.iter_section_starts(): 471 | if start < min_addr: 472 | min_addr = start 473 | if self._sup: 474 | for start in self._sup.iter_section_starts(): 475 | if start < min_addr: 476 | min_addr = start 477 | return min_addr 478 | 479 | def is_concrete_subprogram(self, die): 480 | return any(map(lambda attr_name: attr_name in CONCRETE_SUBPROGRAM_ATTRIBUTES, die.attributes.keys())) 481 | 482 | def is_subprogram_declared_external(self, die): 483 | return ( 484 | 'DW_AT_external' in die.attributes 485 | and 'DW_AT_declaration' in die.attributes 486 | and 'DW_AT_decl_file' in die.attributes 487 | ) 488 | 489 | def is_concrete_variable(self, die): 490 | return any(map(lambda attr_name: attr_name in CONCRETE_SUBPROGRAM_ATTRIBUTES, die.attributes.keys())) 491 | 492 | def get_decl_file(self, die: DIE) -> Optional[str]: 493 | file_id = self.get_attr_as_int(die, 'DW_AT_decl_file') 494 | if file_id is not None: 495 | file_index = file_id - 1 496 | if file_index >= 0: 497 | line_program = self._pri.get_line_program(die) 498 | filename = line_program['file_entry'][file_index]['name'].decode('utf-8') 499 | return os.path.normpath(filename) 500 | return None 501 | 502 | def get_attr_as_location_list(self, die: DIE, attr_name: str) -> Optional[List[LocationEntry]]: 503 | result = self.get_attribute(die, attr_name) 504 | if result is None: 505 | return None 506 | 507 | loc_die, loc_attr = result 508 | if loc_attr.form in ['DW_FORM_exprloc', 'DW_FORM_block1']: 509 | return [LocationEntry(entry_offset=0, begin_offset=0, end_offset=0, loc_expr=loc_attr.value)] 510 | elif loc_attr.form == 'DW_FORM_sec_offset': 511 | locations: List[LocationEntry] = [] 512 | assert(loc_die.cu.dwarfinfo == self._pri._dwarf_info) 513 | base = None 514 | for entry in self._pri.get_location_list(loc_attr.value): 515 | if isinstance(entry, LocationEntry): 516 | if base is None: 517 | cu_base = self.get_cu_base_address(loc_die.cu) 518 | if cu_base is not None: 519 | cu_end = cu_base + loc_die.cu.size 520 | if (cu_base <= entry.begin_offset and entry.begin_offset < cu_end): 521 | base = 0 522 | else: 523 | base = cu_base 524 | else: 525 | base = 0 526 | locations.append( 527 | LocationEntry( 528 | entry_offset=entry.entry_offset, 529 | begin_offset=base + entry.begin_offset, 530 | end_offset=base + entry.end_offset, 531 | loc_expr=entry.loc_expr)) 532 | elif isinstance(entry, BaseAddressEntry): 533 | b: int = entry.base_address 534 | base = b 535 | else: 536 | assert(False) 537 | return locations 538 | 539 | elif loc_attr.form == 'DW_FORM_data4': 540 | base = self.get_cu_base_address(loc_die.cu) 541 | locations: List[LocationEntry] = [] 542 | assert(loc_die.cu.dwarfinfo == self._pri._dwarf_info) 543 | for entry in self._pri.get_location_list(loc_attr.value): 544 | if isinstance(entry, LocationEntry): 545 | if base is None: 546 | locations.append( 547 | LocationEntry( 548 | entry_offset=entry.entry_offset, 549 | begin_offset=entry.begin_offset, 550 | end_offset=entry.end_offset, 551 | loc_expr=entry.loc_expr)) 552 | else: 553 | locations.append( 554 | LocationEntry( 555 | entry_offset=entry.entry_offset, 556 | begin_offset=base + entry.begin_offset, 557 | end_offset=base + entry.end_offset, 558 | loc_expr=entry.loc_expr)) 559 | elif isinstance(entry, BaseAddressEntry): 560 | base = entry.base_address 561 | else: 562 | assert(False) 563 | return locations 564 | 565 | else: 566 | self._logger.error(f'unhandled location form {loc_attr.form}') 567 | assert(False) 568 | 569 | def get_location_list(self, die: DIE) -> List[LocationEntry]: 570 | result = self.get_attr_as_location_list(die, 'DW_AT_location') 571 | return result if result is not None else [] 572 | 573 | def get_cu_base_address(self, cu: CompileUnit) -> Optional[int]: 574 | base = self._base_addresses.get(cu, None) 575 | if base is None: 576 | top_die = cu.get_top_DIE() 577 | assert top_die.tag == 'DW_TAG_compile_unit' 578 | base = self._base_addresses.setdefault(cu, self.get_start_address(top_die)) 579 | return base 580 | 581 | def get_array_count(self, die: DIE) -> Optional[int]: 582 | for d in die.iter_children(): 583 | if d.tag == 'DW_TAG_subrange_type': 584 | if 'DW_AT_count' in d.attributes: 585 | return d.attributes['DW_AT_count'].value 586 | if 'DW_AT_upper_bound' in d.attributes: 587 | upper_bound = d.attributes['DW_AT_upper_bound'] 588 | if upper_bound.form == 'DW_FORM_exprloc': 589 | return None 590 | if upper_bound.form in DWARFDB.REFERENCE_FORMS: 591 | return None 592 | ub = upper_bound.value 593 | if 'DW_AT_lower_bound' in d.attributes: 594 | lb = d.attributes['DW_AT_lower_bound'].value 595 | return ub - lb 596 | assert(isinstance(ub, int)) 597 | return ub + 1 598 | return None 599 | 600 | 601 | def qualified_name_to_str(qname: Union[Tuple[str], str]): 602 | return '::'.join(qname) if isinstance(qname, tuple) else qname 603 | 604 | 605 | def split_all_path_parts(path: str): 606 | import os.path 607 | allparts = [] 608 | path = os.path.normpath(path) 609 | while 1: 610 | parts = os.path.split(path) 611 | if parts[0] == path: 612 | allparts.insert(0, parts[0]) 613 | break 614 | elif parts[1] == path: 615 | allparts.insert(0, parts[1]) 616 | break 617 | else: 618 | path = parts[0] 619 | allparts.insert(0, parts[1]) 620 | return allparts 621 | 622 | 623 | class DWARFImporter(object): 624 | def __init__(self, dwarf_db: DWARFDB, query, model: AnalysisModel, logger: Optional[logging.Logger] = None): 625 | self._dwarf_db = dwarf_db 626 | self._model = model 627 | self._query = query 628 | self._type_factory = TypeFactory(dwarf_db) 629 | self._location_factory = LocationFactory(dwarf_db) 630 | self._min_section_addr = self._dwarf_db.min_mapped_address() 631 | self._globals_filter = query.get('globals_filter', lambda qname: True) 632 | self._include_imports = query.get('include_imports', True) 633 | self._include_variables = query.get('include_variables', True) 634 | self._include_subprograms = query.get('include_subprograms', True) 635 | self._only_concrete_subprograms = query.get('only_concrete_subprograms', True) 636 | self._include_parameters = query.get('include_parameters', True) 637 | self._include_local_variables = query.get('include_local_variables', True) 638 | self._include_inlined_functions = query.get('include_inlined_functions', True) 639 | self._include_global_variable_references = query.get('include_global_variable_references', True) 640 | self._include_subprogram_decls = query.get('include_subprogram_decls', True) 641 | self._compile_unit_filter = query.get('cu_filter', None) 642 | self._die_map = dict() 643 | self._imported_subprograms: MutableMapping[str, Set[str]] = defaultdict(set) 644 | self._imported_start_addrs: Set[int] = set() 645 | if logger is not None: 646 | self._logger = logger.getChild('DWARFImporter') 647 | else: 648 | self._logger = logging.getLogger('sidekick.DWARFImporter') 649 | self._component: Optional[Component] = None 650 | 651 | def import_debug_info(self): 652 | for cu_die in self._dwarf_db.iter_compile_units(): 653 | name = self._dwarf_db.get_name(cu_die) 654 | if self._compile_unit_filter and name not in self._compile_unit_filter: 655 | continue 656 | if name is None: 657 | name = 'anonymous' 658 | qname = QualifiedName(*split_all_path_parts(name)) 659 | language = self._dwarf_db.get_attr_as_int(cu_die, 'DW_AT_language') 660 | if language is not None: 661 | language = Language(language).name 662 | producer = self._dwarf_db.get_attr_as_string(cu_die, 'DW_AT_producer') 663 | 664 | self._component = self._model.make_leaf_component(qname) 665 | if language is not None: 666 | self._component.attributes['language'] = language 667 | if producer is not None: 668 | self._component.attributes['producer'] = producer 669 | 670 | self._type_factory._component = self._component 671 | self.import_compilation_unit(cu_die) 672 | self._component = None 673 | 674 | for ty in self._type_factory._definitions.values(): 675 | self._model.add_type(ty) 676 | 677 | def import_compilation_unit(self, die: DIE): 678 | if die.tag == 'DW_TAG_compile_unit': 679 | cu_name = self._dwarf_db.get_name(die) 680 | self._logger.debug('--' + (cu_name.ljust(78, '-') if cu_name else '')) 681 | self._base_address = self._dwarf_db.get_start_address(die) 682 | for child in die.iter_children(): 683 | if child.tag == 'DW_TAG_subprogram': 684 | if self._include_subprograms: 685 | self.import_subprogram(child) 686 | elif child.tag == 'DW_TAG_variable': 687 | if self._include_variables: 688 | if self.is_global_variable(child): 689 | self.import_global_variable(child) 690 | elif self.is_global_constant(child): 691 | self.import_global_constant(child) 692 | elif child.tag == 'DW_TAG_imported_declaration': 693 | if self._include_imports: 694 | self.import_imported_declaration(child) 695 | elif child.tag in [ 696 | 'DW_TAG_namespace', 697 | 'DW_TAG_class_type', 698 | 'DW_TAG_interface_type', 699 | 'DW_TAG_structure_type', 700 | 'DW_TAG_union_type' 701 | ]: 702 | self.import_compilation_unit(child) 703 | elif child.tag == 'DW_TAG_module' and 'DW_AT_declaration' in child.attributes: 704 | pass 705 | elif child.tag in [ 706 | 'DW_TAG_imported_unit', 707 | 'DW_TAG_typedef', 708 | 'DW_TAG_unspecified_type', 709 | 'DW_TAG_base_type', 710 | 'DW_TAG_const_type', 711 | 'DW_TAG_volatile_type', 712 | 'DW_TAG_restrict_type', 713 | 'DW_TAG_array_type', 714 | 'DW_TAG_enumeration_type', 715 | 'DW_TAG_pointer_type', 716 | 'DW_TAG_reference_type', 717 | 'DW_TAG_ptr_to_member_type', 718 | 'DW_TAG_subroutine_type', 719 | 'DW_TAG_rvalue_reference_type', 720 | 'DW_TAG_subrange_type', 721 | 'DW_TAG_string_type', 722 | 'DW_TAG_member', 723 | 'DW_TAG_inheritance', 724 | 'DW_TAG_template_type_param', 725 | 'DW_TAG_template_value_param', 726 | 'DW_TAG_GNU_template_template_param', 727 | 'DW_TAG_GNU_template_parameter_pack', 728 | 'DW_TAG_imported_module', 729 | 'DW_TAG_module', 730 | 'DW_TAG_imported_declaration', 731 | 'DW_TAG_dwarf_procedure', 732 | 'DW_TAG_constant' 733 | ]: 734 | pass 735 | else: 736 | print(child.tag, '(child tag)') 737 | 738 | def is_global_variable(self, die: DIE) -> bool: 739 | attr = self._dwarf_db.get_attribute(die, 'DW_AT_declaration', False) 740 | if attr is not None: 741 | return False 742 | locations = self._dwarf_db.get_location_list(die) 743 | return len(locations) != 0 744 | 745 | def is_global_constant(self, die: DIE) -> bool: 746 | if 'DW_AT_declaration' in die.attributes: 747 | return False 748 | locations = self._dwarf_db.get_location_list(die) 749 | if len(locations) > 0: 750 | return False 751 | constant_value = self._dwarf_db.get_attr_as_constant(die, 'DW_AT_const_value') 752 | return constant_value is not None 753 | 754 | def import_global_variable(self, die: DIE): 755 | locations = self._dwarf_db.get_location_list(die) 756 | if len(locations) == 0: 757 | return 758 | qname = self._dwarf_db.get_qualified_name(die) 759 | if not self._globals_filter(qname): 760 | return 761 | name = qualified_name_to_str(qname) 762 | assert(name is not None) 763 | self._logger.debug(f'Importing global variable {name}') 764 | type_die = self._dwarf_db.get_type_attr_as_die(die) 765 | assert(type_die) 766 | var_type = self._type_factory.make_user_type(type_die) 767 | 768 | address = None 769 | for _, begin, end, loc_expr in locations: 770 | loc = self._location_factory.make_location(begin, end, loc_expr) 771 | if loc and loc.type == LocationType.STATIC_GLOBAL: 772 | address = loc.expr[0] 773 | assert(isinstance(address, int)) 774 | break 775 | if address is None: 776 | v = Variable(name=qname, addr=None, type=var_type) 777 | elif address >= self._min_section_addr: 778 | v = Variable(name=qname, addr=address, type=var_type) 779 | else: 780 | self._logger.debug(f'Variable address is greater than the minimum section address ({self._min_section_addr:x})') 781 | return 782 | 783 | self._model.add_variable(v) 784 | 785 | def import_global_constant(self, die: DIE): 786 | constant_value = self._dwarf_db.get_attr_as_constant(die, 'DW_AT_const_value') 787 | if constant_value is not None: 788 | qname = self._dwarf_db.get_qualified_name(die) 789 | type_die = self._dwarf_db.get_type_attr_as_die(die) 790 | assert(type_die) 791 | value_type = self._type_factory.make_user_type(type_die) 792 | c = Constant(name=qname, type=value_type, value=constant_value) 793 | self._model.add_constant(c) 794 | 795 | def import_imported_declaration(self, die: DIE): 796 | import_die = self._dwarf_db.get_attr_as_die(die, 'DW_AT_import') 797 | if import_die is None: 798 | return 799 | qname = self._dwarf_db.get_qualified_name(import_die) 800 | name = qualified_name_to_str(qname) 801 | decl_file = self._dwarf_db.get_decl_file(die) 802 | if decl_file: 803 | self._imported_subprograms[decl_file].add(name) 804 | 805 | def import_location_list(self, location_list): 806 | if location_list is None: 807 | return None 808 | locations = [] 809 | for loc in location_list: 810 | r = self._location_factory.make_location(loc.begin_offset, loc.end_offset, loc.loc_expr) 811 | if r is not None: 812 | locations.append(r) 813 | if len(locations) == 1: 814 | return locations[0] 815 | elif len(locations) == 0: 816 | return None 817 | else: 818 | return locations 819 | 820 | def import_subprogram(self, die: DIE): 821 | is_concrete = self._dwarf_db.is_concrete_subprogram(die) 822 | if not is_concrete: 823 | if self._include_subprogram_decls and self._dwarf_db.is_subprogram_declared_external(die): 824 | qname = self._dwarf_db.get_qualified_name(die) 825 | name = qualified_name_to_str(qname) 826 | self._dwarf_db.set_external_decl_file(name, self._dwarf_db.get_decl_file(die)) 827 | if self._only_concrete_subprograms: 828 | return 829 | qname = self._dwarf_db.get_qualified_name(die) 830 | if not self._globals_filter(qname): 831 | return 832 | start_addr = self._dwarf_db.get_start_address(die) 833 | if start_addr is not None and self._model.get_function_at(start_addr): 834 | return 835 | function = Function(name=qname, entry_addr=start_addr) 836 | return_type_die = self._dwarf_db.get_type_attr_as_die(die) 837 | if return_type_die is None: 838 | return_type = VOID 839 | else: 840 | return_type = self._type_factory.make_user_type(return_type_die) 841 | function.set_return_type(return_type) 842 | function.no_return = True if self._dwarf_db.get_attribute(die, 'DW_AT_noreturn') else False 843 | function.frame_base = self.import_location_list(self._dwarf_db.get_attr_as_location_list(die, 'DW_AT_frame_base')) 844 | function.ranges = self._dwarf_db.get_ranges(die) 845 | if self._include_subprogram_decls: 846 | if self._dwarf_db.has_external_decl_file(str(qname)): 847 | decl_file = self._dwarf_db.get_external_decl_file(str(qname)) 848 | if decl_file is not None: 849 | function.set_attribute('decl_file', decl_file) 850 | function.arch = self.get_function_arch(function) 851 | location_map = defaultdict(list) 852 | abstract_map = dict() 853 | self.import_local_elements(die, function, None, location_map=location_map, abstract_map=abstract_map) 854 | self.apply_variable_hiding(location_map) 855 | global_vars = self.extract_global_variables(function) 856 | self._model.add_function(function) 857 | assert(self._component is not None) 858 | self._component.add_elements(function) 859 | self._component.add_elements(global_vars) 860 | 861 | def get_function_arch(self, function: Function): 862 | arch = self._dwarf_db._pri._arch 863 | if arch == 'ARM': 864 | for section in self._dwarf_db._pri._elf_file.iter_sections(): 865 | if not isinstance(section, SymbolTableSection): 866 | continue 867 | for symbol in section.iter_symbols(): 868 | if symbol.entry['st_info']['type'] != 'STT_FUNC': 869 | continue 870 | addr = symbol.entry['st_value'] 871 | if ((addr & ~0x1) != function.entry_addr): 872 | continue 873 | return 'Thumb' if (addr & 0x1) else 'ARM' 874 | return arch 875 | 876 | def extract_global_variables(self, function: Function): 877 | global_vars = [] 878 | for v in function.iter_variables(): 879 | global_locations = list(loc for loc in v.locations if loc.type == LocationType.STATIC_GLOBAL) 880 | if global_locations: 881 | for g in global_locations: 882 | if isinstance(g.expr[0], int): 883 | global_vars.append(Variable(name=v.name, addr=g.expr[0], type=v.type)) 884 | v.locations.remove(g) 885 | return global_vars 886 | 887 | def apply_variable_hiding(self, location_map): 888 | for loc, elements in location_map.items(): 889 | if len(elements) > 1: 890 | j = None 891 | for i in range(len(elements) - 1, 0, -1): 892 | if elements[i].type != elements[i - 1].type: 893 | j = i 894 | break 895 | if j is None: 896 | continue 897 | while j < len(elements) - 1 and elements[j].name in ['this', '__artificial__']: 898 | j += 1 899 | for i in range(0, j): 900 | elements[i].locations.remove(loc) 901 | 902 | def import_inlined_subroutine(self, die: DIE, parent_function: Function, location_map, abstract_map): 903 | qname = self._dwarf_db.get_qualified_name(die) 904 | name = qualified_name_to_str(qname) 905 | self._logger.debug(f'inlined subroutine {name}') 906 | start = self._dwarf_db.get_start_address(die) 907 | function = Function(name=qname, entry_addr=start) 908 | function.is_inlined = True 909 | type_die = self._dwarf_db.get_type_attr_as_die(die) 910 | function.ranges = self._dwarf_db.get_ranges(die) 911 | if type_die is not None: 912 | function.set_return_type(self._type_factory.make_user_type(type_die)) 913 | function.frame_base = self.import_location_list(self._dwarf_db.get_attr_as_location_list(die, 'DW_AT_frame_base')) 914 | self.import_local_elements(die, function, parent_function, location_map=location_map, abstract_map=abstract_map) 915 | return function 916 | 917 | def import_local_elements( 918 | self, 919 | die: DIE, function: Function, 920 | parent_function: Optional[Function], 921 | inside_lexical_block: bool = False, 922 | location_map=None, abstract_map=None 923 | ): 924 | for child in die.iter_children(): 925 | if child.tag == 'DW_TAG_variable': 926 | if self._include_local_variables: 927 | if self._dwarf_db.is_concrete_variable(child): 928 | local_var = self.import_local_variable(child, function, abstract_map) 929 | if local_var: 930 | self.import_locations( 931 | local_var, child, location_map, 932 | [LocationType.STATIC_LOCAL, LocationType.STATIC_GLOBAL]) 933 | elif child.tag == 'DW_TAG_formal_parameter': 934 | if self._include_parameters: 935 | param = self.import_parameter(child, function, parent_function, abstract_map) 936 | if param: 937 | self.import_locations(param, child, location_map, [LocationType.STATIC_LOCAL]) 938 | elif child.tag == 'DW_TAG_unspecified_parameters': 939 | function.append_parameter(None, VARIADIC) 940 | elif child.tag == 'DW_TAG_lexical_block': 941 | self.import_local_elements( 942 | child, function, parent_function, 943 | inside_lexical_block=True, location_map=location_map, abstract_map=abstract_map) 944 | elif child.tag == 'DW_TAG_inlined_subroutine': 945 | if self._include_inlined_functions: 946 | f = self.import_inlined_subroutine( 947 | child, function if parent_function is None else parent_function, 948 | location_map, abstract_map) 949 | function.add_inlined_function(f) 950 | elif child.tag == 'DW_TAG_subprogram': 951 | if self._dwarf_db.is_concrete_subprogram(child): 952 | self.import_subprogram(child) 953 | else: 954 | pass 955 | elif child.tag in [ 956 | 'DW_TAG_template_type_parameter', 957 | 'DW_TAG_template_type_param', 958 | 'DW_TAG_template_value_param', 959 | 'DW_TAG_GNU_formal_parameter_pack', 960 | 'DW_TAG_GNU_template_parameter_pack', 961 | 'DW_TAG_GNU_template_template_param', 962 | 'DW_TAG_GNU_call_site', 963 | 'DW_TAG_label', 964 | 'DW_TAG_imported_declaration', 965 | 'DW_TAG_constant', 966 | 'DW_TAG_imported_module', 967 | 'DW_TAG_common_block', 968 | 'DW_TAG_namelist', 969 | 'DW_TAG_subrange_type', 970 | 'DW_TAG_typedef', 971 | 'DW_TAG_array_type', 972 | 'DW_TAG_structure_type', 973 | 'DW_TAG_union_type', 974 | 'DW_TAG_class_type', 975 | 'DW_TAG_interface_type', 976 | 'DW_TAG_const_type', 977 | 'DW_TAG_pointer_type', 978 | 'DW_TAG_enumeration_type', 979 | 'DW_TAG_reference_type', 980 | 'DW_TAG_restrict_type' 981 | ]: 982 | pass 983 | else: 984 | self._logger.warning(f'unhandled tag {child.tag} inside {die.tag}\n\tParent DIE = {die}\n\tChild DIE = {child}') 985 | 986 | def import_parameter(self, die: DIE, function: Function, parent_function: Optional[Function], abstract_map): 987 | if 'DW_AT_abstract_origin' in die.attributes: 988 | abstract_origin = self._dwarf_db.get_attr_as_die(die, 'DW_AT_abstract_origin') 989 | if abstract_origin in abstract_map: 990 | return abstract_map[abstract_origin] 991 | else: 992 | abstract_origin = None 993 | 994 | name = self._get_parameter_name(die) 995 | if name is None: 996 | name = '' 997 | self._logger.debug(f'in {function.name}: unnamed parameter\n{die})') 998 | if name is not None: 999 | type_die = self._dwarf_db.get_type_attr_as_die(die) 1000 | if type_die is not None: 1001 | parameter_type = self._type_factory.make_user_type(type_die) 1002 | p = function.append_parameter(name, parameter_type) 1003 | 1004 | if 'DW_AT_abstract_origin' in die.attributes: 1005 | abstract_map[abstract_origin] = p 1006 | 1007 | return p 1008 | else: 1009 | self._logger.warning(f'In "{function.name}", parameter "{name}" has no type {die}') 1010 | else: 1011 | self._logger.warning(f'parameter has no name {die}') 1012 | self._logger.warning(f'parameter type {self._type_factory.type_of(die)}') 1013 | name = 'anonymous' 1014 | 1015 | def import_locations( 1016 | self, 1017 | element: Union[Parameter, LocalVariable], 1018 | die: DIE, 1019 | location_map: MutableMapping[Location, List[Element]], 1020 | location_filter 1021 | ): 1022 | for _, begin, end, loc_expr in self._dwarf_db.get_location_list(die): 1023 | loc = self._location_factory.make_location(begin, end, loc_expr) 1024 | if loc and loc.type in location_filter: 1025 | element.add_location(loc) 1026 | location_map[loc].append(element) 1027 | 1028 | def import_local_variable(self, die: DIE, fn: Function, abstract_map): 1029 | if 'DW_AT_abstract_origin' in die.attributes: 1030 | abstract_origin = self._dwarf_db.get_attr_as_die(die, 'DW_AT_abstract_origin') 1031 | if abstract_origin in abstract_map: 1032 | return abstract_map[abstract_origin] 1033 | else: 1034 | abstract_origin = None 1035 | 1036 | name = self._get_local_variable_name(die) 1037 | if name is None: 1038 | return None 1039 | 1040 | try: 1041 | type_die = self._dwarf_db.get_type_attr_as_die(die) 1042 | if type_die is None: 1043 | self._logger.error(f'Local variable ({name}) has no type {die}') 1044 | raise Exception(f'Local variable ({name}) has no type {die}') 1045 | assert(type_die is not None) 1046 | var_type = self._type_factory.make_user_type(type_die) 1047 | 1048 | if 'DW_AT_const_value' in die.attributes and 'DW_AT_location' not in die.attributes: 1049 | fn.add_constant(name, var_type, die.attributes['DW_AT_const_value'].value) 1050 | else: 1051 | v = fn.add_variable(name, var_type) 1052 | if 'DW_AT_abstract_origin' in die.attributes: 1053 | abstract_map[abstract_origin] = v 1054 | return v 1055 | except Exception as e: 1056 | self._logger.exception("importing local variable", exc_info=sys.exc_info()) 1057 | raise e 1058 | 1059 | def _get_parameter_name(self, die): 1060 | if 'DW_AT_artificial' in die.attributes: 1061 | return 'this' 1062 | else: 1063 | return self._dwarf_db.get_name(die) 1064 | 1065 | def _get_local_variable_name(self, die) -> Optional[str]: 1066 | if 'DW_AT_artificial' in die.attributes: 1067 | return '__artificial__' 1068 | else: 1069 | return self._dwarf_db.get_name(die) 1070 | 1071 | 1072 | class TypeFactory(object): 1073 | def __init__(self, dwarf_db: DWARFDB): 1074 | self._dwarf_db = dwarf_db 1075 | self._addr_size = dwarf_db._pri._dwarf_info.config.default_address_size 1076 | self._defined_types: MutableMapping[DIE, Type] = dict() 1077 | self._base_types: MutableMapping[str, Type] = dict() 1078 | self._named_composites: MutableMapping[str, Type] = dict() 1079 | self._logger = logging.getLogger('sidekick.TypeFactory') 1080 | self._definitions = dict() 1081 | self._component: Optional[Component] = None 1082 | structs = self._dwarf_db._pri._dwarf_info.structs 1083 | arch = self._dwarf_db._pri._arch 1084 | self._expr_parser = LocExprParser(structs, arch) 1085 | 1086 | def clear(self): 1087 | self._defined_types = dict() 1088 | 1089 | def iter_types(self) -> ValuesView[Type]: 1090 | return self._defined_types.values() 1091 | 1092 | def name_of(self, die: DIE) -> Optional[str]: 1093 | return self._dwarf_db.get_name(die) 1094 | 1095 | def type_of(self, die: DIE) -> DIE: 1096 | type_die = self._dwarf_db.get_type_attr_as_die(die) 1097 | assert(type_die) 1098 | return type_die 1099 | 1100 | def make_user_type(self, type_die: DIE, alias: Optional[QualifiedName] = None) -> Type: 1101 | visited = [] 1102 | return self._make_user_type_helper(type_die, alias, visited) 1103 | 1104 | def _make_user_type_helper(self, type_die: DIE, alias: Optional[QualifiedName] = None, visited=None) -> Type: 1105 | if type_die in self._defined_types: 1106 | return self._defined_types[type_die] 1107 | 1108 | assert(type_die) 1109 | 1110 | if type_die in visited: 1111 | if type_die.tag == 'DW_TAG_pointer_type': 1112 | return VOID_PTR 1113 | else: 1114 | return VOID 1115 | 1116 | if type_die.tag == 'DW_TAG_base_type': 1117 | n = self.name_of(type_die) 1118 | if n is None: 1119 | raise Exception('Base type must have a name') 1120 | base_type = BaseType(n, byte_size=size_of(type_die)) 1121 | self._base_types[str(base_type.name)] = base_type 1122 | return self._defined_types.setdefault(type_die, base_type) 1123 | 1124 | elif type_die.tag == 'DW_TAG_unspecified_type': 1125 | n = self.name_of(type_die) 1126 | if n is None: 1127 | raise Exception('Unspecified type must have a name') 1128 | unspecified_type = BaseType(n, None) 1129 | return self._defined_types.setdefault(type_die, unspecified_type) 1130 | 1131 | elif type_die.tag == 'DW_TAG_typedef': 1132 | qualified_alias = self._dwarf_db.get_qualified_name(type_die) 1133 | aliased_type_die = self._dwarf_db.get_type_attr_as_die(type_die) 1134 | if aliased_type_die: 1135 | visited.append(type_die) 1136 | element_type = self._make_user_type_helper(aliased_type_die, alias=qualified_alias, visited=visited) 1137 | visited.pop() 1138 | else: 1139 | element_type = VOID 1140 | if element_type.name == qualified_alias: 1141 | return element_type 1142 | else: 1143 | alias_type = AliasType(name=qualified_alias, type=element_type) 1144 | if alias_type.name in self._definitions: 1145 | return self._definitions[alias_type.name] 1146 | else: 1147 | self._definitions[alias_type.name] = alias_type 1148 | return self._defined_types.setdefault(type_die, alias_type) 1149 | 1150 | elif type_die.tag in ['DW_TAG_const_type', 'DW_TAG_volatile_type']: 1151 | if has_type(type_die): 1152 | try: 1153 | visited.append(type_die) 1154 | element_type = self._make_user_type_helper(self.type_of(type_die), visited=visited) 1155 | visited.pop() 1156 | except Exception as e: 1157 | self._logger.error(str(e)) 1158 | raise e 1159 | else: 1160 | element_type = VOID 1161 | if type_die.tag == 'DW_TAG_const_type': 1162 | cv_type = ConstType(type=element_type) 1163 | else: 1164 | cv_type = VolatileType(type=element_type) 1165 | return self._defined_types.setdefault(type_die, cv_type) 1166 | 1167 | elif type_die.tag in ['DW_TAG_pointer_type', 'DW_TAG_reference_type', 'DW_TAG_rvalue_reference_type']: 1168 | if has_type(type_die): 1169 | visited.append(type_die) 1170 | element_type = self._make_user_type_helper(self.type_of(type_die), visited=visited) 1171 | visited.pop() 1172 | else: 1173 | element_type = VOID 1174 | pointer_size = size_of(type_die) if has_size(type_die) else self._addr_size 1175 | pointer_type = PointerType(target_type=element_type, byte_size=pointer_size) 1176 | if type_die.tag == 'DW_TAG_reference_type': 1177 | pointer_type.nullable = False 1178 | elif type_die.tag == 'DW_TAG_rvalue_reference_type': 1179 | pointer_type.nullable = False 1180 | if has_name(type_die): 1181 | n = self.name_of(type_die) 1182 | assert(n) 1183 | alias_type = AliasType(name=QualifiedName(n), type=pointer_type) 1184 | return self._defined_types.setdefault(type_die, alias_type) 1185 | else: 1186 | return self._defined_types.setdefault(type_die, pointer_type) 1187 | 1188 | elif type_die.tag in ['DW_TAG_structure_type', 'DW_TAG_class_type', 'DW_TAG_union_type', 'DW_TAG_interface_type']: 1189 | composite_name = self.get_type_name(type_die, alias) 1190 | composite_size = size_of(type_die) if has_size(type_die) else None 1191 | if type_die.tag == 'DW_TAG_structure_type': 1192 | composite_type = StructType(name=composite_name, byte_size=composite_size) 1193 | elif type_die.tag == 'DW_TAG_union_type': 1194 | composite_type = UnionType(name=composite_name, byte_size=composite_size) 1195 | elif type_die.tag == 'DW_TAG_class_type': 1196 | composite_type = ClassType(name=composite_name, byte_size=composite_size) 1197 | elif type_die.tag == 'DW_TAG_interface_type': 1198 | composite_type = ClassType(name=composite_name, byte_size=composite_size) 1199 | else: 1200 | assert(False) 1201 | self._defined_types[type_die] = composite_type 1202 | for member_die in type_die.iter_children(): 1203 | if member_die.tag == 'DW_TAG_member': 1204 | if not has_type(member_die): 1205 | raise Exception('Member has no type') 1206 | 1207 | visited.append(type_die) 1208 | member_type = self._make_user_type_helper(self.type_of(member_die), visited=visited) 1209 | visited.pop() 1210 | 1211 | if member_type.name is None: 1212 | assert(member_type.name is not None) 1213 | member_offset = self.member_offset_of(member_die) 1214 | if member_offset is None and isinstance(composite_type, UnionType) and 'DW_AT_declaration' not in member_die.attributes: 1215 | member_offset = 0 1216 | if member_offset is not None: 1217 | n = self.name_of(member_die) 1218 | if n is not None: 1219 | composite_type.add_field(Field(name=n, offset=member_offset, type=member_type)) 1220 | else: 1221 | composite_type.add_unnamed_field(Field(name=None, offset=member_offset, type=member_type)) 1222 | else: 1223 | if 'DW_AT_const_value' in member_die.attributes: 1224 | pass 1225 | elif 'DW_AT_declaration' in member_die.attributes: 1226 | pass 1227 | else: 1228 | raise NotImplementedError('member with no offset') 1229 | elif member_die.tag == 'DW_TAG_inheritance': 1230 | member_type_die = self.type_of(member_die) 1231 | visited.append(type_die) 1232 | member_type = self._make_user_type_helper(member_type_die, visited=visited) 1233 | visited.pop() 1234 | member_offset = self.member_offset_of(member_die) 1235 | if member_offset is not None: 1236 | if member_type.byte_size is None: 1237 | self._logger.debug(f'Member type size is None {member_type}') 1238 | composite_type.add_unnamed_field(Field(name=None, offset=member_offset, type=member_type)) 1239 | else: 1240 | self._logger.warning(f'Unsupported: inherited composite ({member_type.name}) at a computed offset') 1241 | elif member_die.tag == 'DW_TAG_variant_part': 1242 | pass 1243 | self._defined_types[type_die] = composite_type 1244 | if composite_type.name.is_anonymous is False: 1245 | if composite_type.name in self._definitions: 1246 | existing_ty = self._definitions[composite_type.name] 1247 | if isinstance(existing_ty, CompositeType): 1248 | if len(existing_ty) >= 0 and len(composite_type) == 0: 1249 | return existing_ty 1250 | elif len(composite_type) > 0 and len(existing_ty) == 0: 1251 | existing_ty.merge_from(composite_type) 1252 | return existing_ty 1253 | elif existing_ty.byte_size is None and composite_type.byte_size is not None: 1254 | existing_ty.merge_from(composite_type) 1255 | return existing_ty 1256 | elif existing_ty.is_equivalent(composite_type): 1257 | return existing_ty 1258 | 1259 | assert(self._component is not None) 1260 | composite_type.name = QualifiedName(self._component.name, *composite_type.name) 1261 | self._logger.debug(f'Conflicting type name. Adding qualifier. ({composite_type.name})') 1262 | self._logger.debug(f'Existing type: ({existing_ty.__repr__()})') 1263 | if isinstance(existing_ty, CompositeType): 1264 | self._logger.debug(f'{existing_ty.byte_size=}, {existing_ty.policy.name=}, {existing_ty.name=}') 1265 | self._logger.debug(f'Current type: ({composite_type.__repr__()})') 1266 | self._logger.debug(f'{composite_type.byte_size=}, {composite_type.policy.name=}, {composite_type.name=}') 1267 | self._definitions[composite_type.name] = composite_type 1268 | return self._definitions[composite_type.name] 1269 | else: 1270 | self._definitions[composite_type.name] = composite_type 1271 | return composite_type 1272 | 1273 | elif type_die.tag in ['DW_TAG_enumeration_type']: 1274 | enum_name = self.get_type_name(type_die, alias) 1275 | enum_size = size_of(type_die) if has_size(type_die) else self._addr_size 1276 | enum_type = EnumType(name=enum_name, byte_size=enum_size) 1277 | for member_die in type_die.iter_children(): 1278 | if member_die.tag == 'DW_TAG_enumerator': 1279 | value = member_die.attributes['DW_AT_const_value'].value 1280 | label = self.name_of(member_die) 1281 | if label is None: 1282 | raise Exception('DW_TAG_enumeration_type has no label') 1283 | enum_type.add_enumerator(Enumerator(label=label, value=value)) 1284 | if enum_type.name: 1285 | if enum_type.name in self._definitions: 1286 | return self._definitions[enum_type.name] 1287 | else: 1288 | self._definitions[enum_type.name] = enum_type 1289 | return self._defined_types.setdefault(type_die, enum_type) 1290 | 1291 | elif type_die.tag == 'DW_TAG_subroutine_type': 1292 | function_name = self.get_type_name(type_die, alias) 1293 | if function_name is None: 1294 | function_name = QualifiedName() 1295 | if has_type(type_die): 1296 | visited.append(type_die) 1297 | return_type = self._make_user_type_helper(self.type_of(type_die), visited=visited) 1298 | visited.pop() 1299 | else: 1300 | return_type = VOID 1301 | function_type = FunctionType(name=function_name, return_type=return_type) 1302 | for param_die in type_die.iter_children(): 1303 | if param_die.tag == 'DW_TAG_formal_parameter': 1304 | visited.append(type_die) 1305 | param_type = self._make_user_type_helper(self.type_of(param_die), visited=visited) 1306 | visited.pop() 1307 | function_type.parameters.append(param_type) 1308 | elif param_die.tag == 'DW_TAG_unspecified_parameters': 1309 | function_type.parameters.append(VARIADIC) 1310 | else: 1311 | self._logger.warning(( 1312 | f'While defining subroutine type "{function_name}", ' 1313 | f'encountered an unhandled tag "{param_die.tag}"')) 1314 | if function_type.name: 1315 | if function_type.name in self._definitions: 1316 | return self._definitions[function_type.name] 1317 | else: 1318 | self._definitions[function_type.name] = function_type 1319 | return self._defined_types.setdefault(type_die, function_type) 1320 | 1321 | elif type_die.tag == 'DW_TAG_array_type': 1322 | array_name = self.get_type_name(type_die, alias) 1323 | element_die = self._dwarf_db.get_type_attr_as_die(type_die) 1324 | if element_die is None: 1325 | raise Exception('DW_TAG_array_type has no type DIE') 1326 | visited.append(type_die) 1327 | element_type = self._make_user_type_helper(element_die, visited=visited) 1328 | visited.pop() 1329 | array_count = self._dwarf_db.get_array_count(type_die) 1330 | if array_count is None: 1331 | array_count = 0 1332 | array_type = ArrayType(element_type=element_type, count=array_count, name=array_name) 1333 | if array_type.name: 1334 | if array_type.name in self._definitions: 1335 | return self._definitions[array_type.name] 1336 | else: 1337 | self._definitions[array_type.name] = array_type 1338 | return self._defined_types.setdefault(type_die, array_type) 1339 | 1340 | elif type_die.tag in ['DW_TAG_restrict_type']: 1341 | restricted_type_die = self._dwarf_db.get_type_attr_as_die(type_die) 1342 | if restricted_type_die is None: 1343 | raise Exception('DW_TAG_restrict_type has no type DIE') 1344 | visited.append(type_die) 1345 | ty = self._make_user_type_helper(restricted_type_die, visited=visited) 1346 | visited.pop() 1347 | return ty 1348 | 1349 | elif type_die.tag in ['DW_TAG_ptr_to_member_type']: 1350 | p2m_name = self.get_type_name(type_die, alias) 1351 | if has_type(type_die): 1352 | member_type_die = self._dwarf_db.get_type_attr_as_die(type_die) 1353 | assert(member_type_die) 1354 | visited.append(type_die) 1355 | member_type = self._make_user_type_helper(member_type_die, visited=visited) 1356 | visited.pop() 1357 | else: 1358 | member_type = VOID 1359 | if 'DW_AT_containing_type' in type_die.attributes: 1360 | containing_die = self._dwarf_db.get_attr_as_die(type_die, 'DW_AT_containing_type') 1361 | assert(containing_die) 1362 | visited.append(type_die) 1363 | containing_type = self._make_user_type_helper(containing_die, visited=visited) 1364 | visited.pop() 1365 | assert(isinstance(containing_type, CompositeType)) 1366 | else: 1367 | containing_type = StructType() 1368 | p2m_type = PointerToMemberType(name=p2m_name, container=containing_type, target=member_type) 1369 | return p2m_type 1370 | 1371 | elif type_die.tag == 'DW_TAG_string_type': 1372 | assert('DW_AT_type' not in type_die.attributes) 1373 | if 'char' not in self._base_types: 1374 | char_type = BaseType('char', 1) 1375 | self._base_types[str(char_type.name)] = char_type 1376 | else: 1377 | char_type = self._base_types['char'] 1378 | 1379 | if 'DW_AT_byte_size' in type_die.attributes: 1380 | size = type_die.attributes['DW_AT_byte_size'].value 1381 | assert(char_type.byte_size) 1382 | array_type = StringType(char_size=char_type.byte_size, is_null_terminated=False, byte_size=size) 1383 | return self._defined_types.setdefault(type_die, array_type) 1384 | else: 1385 | cptr_type = PointerType(self._addr_size, target_type=char_type) 1386 | return self._defined_types.setdefault(type_die, cptr_type) 1387 | 1388 | elif type_die.tag == 'DW_TAG_subrange_type': 1389 | assert('DW_AT_type' in type_die.attributes) 1390 | subrange_type = self._dwarf_db.get_type_attr_as_die(type_die) 1391 | assert(subrange_type) 1392 | visited.append(type_die) 1393 | ty = self._make_user_type_helper(subrange_type, visited=visited) 1394 | visited.pop() 1395 | return ty 1396 | 1397 | elif type_die.tag == 'DW_TAG_variant_part': 1398 | return VOID 1399 | 1400 | else: 1401 | print(type_die) 1402 | assert(type_die and False) 1403 | 1404 | def get_type_name(self, type_die: DIE, alias: Optional[QualifiedName] = None): 1405 | if 'DW_AT_name' in type_die.attributes: 1406 | return self._dwarf_db.get_qualified_name(type_die) 1407 | else: 1408 | if alias is None: 1409 | return QualifiedName() 1410 | else: 1411 | return alias 1412 | 1413 | def member_offset_of(self, die: DIE) -> Optional[int]: 1414 | if 'DW_AT_data_member_location' in die.attributes: 1415 | attr = die.attributes['DW_AT_data_member_location'] 1416 | if attr.form in ['DW_FORM_data1', 'DW_FORM_data2', 'DW_FORM_data4', 'DW_FORM_data8', 'DW_FORM_udata', 'DW_FORM_sdata']: 1417 | return attr.value 1418 | elif attr.form in ['DW_FORM_block1']: 1419 | self._expr_parser.clear() 1420 | self._expr_parser.parse(attr.value) 1421 | s = self._expr_parser.stack 1422 | if len(s) == 2 and s[0] == ExprOp.PLUS_IMM: 1423 | if isinstance(s[1], int): 1424 | return s[1] 1425 | return None 1426 | elif attr.form in ['DW_FORM_exprloc']: 1427 | self._expr_parser.clear() 1428 | self._expr_parser.parse(attr.value) 1429 | s = self._expr_parser.stack 1430 | if len(s) == 2 and s[0] == ExprOp.PLUS_IMM: 1431 | if isinstance(s[1], int): 1432 | return s[1] 1433 | return None 1434 | else: 1435 | print(attr) 1436 | raise NotImplementedError(f'unsupported attr form ({attr.form}) for DW_AT_data_member_location') 1437 | else: 1438 | return None 1439 | 1440 | 1441 | class LocationFactory(object): 1442 | def __init__(self, dwarf_db: DWARFDB): 1443 | self._dwarf_db = dwarf_db 1444 | structs = self._dwarf_db._pri._dwarf_info.structs 1445 | arch = self._dwarf_db._pri._arch 1446 | self._expr_parser = LocExprParser(structs, arch) 1447 | self._memo = dict() 1448 | 1449 | def make_location(self, begin: int, end: int, loc_expr) -> Optional[Location]: 1450 | if not isinstance(loc_expr, tuple): 1451 | loc_expr = tuple(loc_expr) 1452 | if loc_expr in self._memo: 1453 | ty, expr = self._memo[loc_expr] 1454 | else: 1455 | self._expr_parser.clear() 1456 | self._expr_parser.parse(loc_expr) 1457 | if self._expr_parser.location_type is None: 1458 | return None 1459 | expr = tuple(self._expr_parser.stack) 1460 | ty = self._expr_parser.location_type 1461 | self._memo[loc_expr] = (ty, expr) 1462 | return Location(begin=begin, end=end, type=ty, expr=expr) 1463 | 1464 | 1465 | def import_ELF_DWARF_into_model(elf_file: ELFFile, model: AnalysisModel, query=dict(), debug_root: Optional[str] = None, logger=None): 1466 | dwarf_db = DWARFDB(elf_file, debug_root) 1467 | importer = DWARFImporter(dwarf_db, query, model, logger) 1468 | importer.import_debug_info() 1469 | 1470 | 1471 | def import_ELF_DWARF(file: Union[str, bytes, int], query=dict()) -> AnalysisModel: 1472 | elf_file = ELFFile(open(file, 'rb')) 1473 | if elf_file.has_dwarf_info(): 1474 | model = AnalysisModel('') 1475 | import_ELF_DWARF_into_model(elf_file, model) 1476 | return model 1477 | raise Exception('ELF has no DWARF info') 1478 | 1479 | 1480 | def ELF_has_debug_info(elf_file) -> bool: 1481 | if elf_file.get_section_by_name('.debug_info') or elf_file.get_section_by_name('.zdebug_info'): 1482 | return True 1483 | else: 1484 | return False 1485 | -------------------------------------------------------------------------------- /io/elftools_extras.py: -------------------------------------------------------------------------------- 1 | # Copyright(c) 2021-2023 Vector 35 Inc 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files(the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and / or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | from elftools.dwarf.descriptions import describe_reg_name as elftools_describe_reg_name 22 | 23 | # https://developer.arm.com/documentation/ihi0040/c/?lang=en#dwarf-register-names 24 | _REG_NAMES_ARM = [ 25 | # 0-15 26 | 'r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 27 | 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15', 28 | # 16-63 29 | '', '', '', '', '', '', '', '', 30 | '', '', '', '', '', '', '', '', 31 | '', '', '', '', '', '', '', '', 32 | '', '', '', '', '', '', '', '', 33 | '', '', '', '', '', '', '', '', 34 | '', '', '', '', '', '', '', '', 35 | # 64-95 36 | 's0', 's1', 's2', 's3', 's4', 's5', 's6', 's7', 37 | 's8', 's9', 's10', 's11', 's12', 's13', 's14', 's15', 38 | 's16', 's17', 's18', 's19', 's20', 's21', 's22', 's23', 39 | 's24', 's25', 's26', 's27', 's28', 's29', 's30', 's31', 40 | # 96-103 41 | 'f0', 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 42 | # 104-11 43 | 'ACC0', 'ACC1', 'ACC2', 'ACC3', 'ACC4', 'ACC5', 'ACC6', 'ACC7', 44 | # 112-127 45 | 'wR0', 'wR1', 'wR2', 'wR3', 'wR4', 'wR5', 'wR6', 'wR7', 46 | 'wR8', 'wR9', 'wR10', 'wR11', 'wR12', 'wR13', 'wR14', 'wR15', 47 | # 128 48 | 'SPSR', 49 | # 129 50 | 'SPSR_FIQ', 51 | # 130 52 | 'SPSR_IRQ', 53 | # 131 54 | 'SPSR_ABT', 55 | # 132 56 | 'SPSR_UND', 57 | # 133 58 | 'SPSR_SVC', 59 | # 134-143 60 | '', '', '', '', '', '', '', '', 61 | '', '', 62 | # 144-150 63 | 'R8_USR', 'R9_USR', 'R10_USR', 'R11_USR', 'R12_USR', 'R13_USR', 'R14_USR', 64 | # 151-157 65 | 'R8_FIQ', 'R9_FIQ', 'R10_FIQ', 'R11_FIQ', 'R12_FIQ', 'R13_FIQ', 'R14_FIQ', 66 | # 158-159 67 | 'R13_IRQ', 'R14_IRQ', 68 | # 160-161 69 | 'R13_ABT', 'R14_ABT', 70 | # 162-163 71 | 'R13_UND', 'R14_UND', 72 | # 164-165 73 | 'R13_SVC', 'R14_SVC', 74 | # 166-191 75 | '', '', '', '', '', '', '', '', 76 | '', '', '', '', '', '', '', '', 77 | '', '', '', '', '', '', '', '', 78 | '', '', 79 | # 192-199 80 | 'wC0', 'wC1', 'wC2', 'wC3', 'wC4', 'wC5', 'wC6', 'wC7', 81 | # 200-255 82 | '', '', '', '', '', '', '', '', 83 | '', '', '', '', '', '', '', '', 84 | '', '', '', '', '', '', '', '', 85 | '', '', '', '', '', '', '', '', 86 | '', '', '', '', '', '', '', '', 87 | '', '', '', '', '', '', '', '', 88 | '', '', '', '', '', '', '', '', 89 | # 256-287 90 | 'd0', 'd1', 'd2', 'd3', 'd4', 'd5', 'd6', 'd7', 91 | 'd8', 'd9', 'd10', 'd11', 'd12', 'd13', 'd14', 'd15', 92 | 'd16', 'd17', 'd18', 'd19', 'd20', 'd21', 'd22', 'd23', 93 | 'd24', 'd25', 'd26', 'd27', 'd28', 'd29', 'd30', 'd31', 94 | # 288-319 95 | '', '', '', '', '', '', '', '', 96 | '', '', '', '', '', '', '', '', 97 | '', '', '', '', '', '', '', '', 98 | '', '', '', '', '', '', '', '' 99 | # 320-8191 None 100 | # 8192-16383 Vendor co-processor 101 | ] 102 | 103 | # https://github.com/NationalSecurityAgency/ghidra/blob/master/Ghidra/Processors/MIPS/data/languages/mips.dwarf 104 | _REG_NAMES_MIPS = [ 105 | # 0-32 106 | '$zero', '$at', '$v0', '$v1', 107 | '$a0', '$a1', '$a2', '$a3', 108 | '$t0', '$t1', '$t2', '$t3', '$t4', '$t5', '$t6', '$t7', 109 | '$s0', '$s1', '$s2', '$s3', '$s4', '$s5', '$s6', '$s7', 110 | '$t8', '$t9', 111 | '$k0', '$k1', 112 | '$gp', '$sp', 113 | '$fp', 114 | '$ra', 115 | # 32-64 116 | '$f0', '$f1', '$f2', '$f3', '$f4', '$f5', '$f6', '$f7', 117 | '$f8', '$f9', '$f10', '$f11', '$f12', '$f13', '$f14', '$f15', 118 | '$f16', '$f17', '$f18', '$f19', '$f20', '$f21', '$f22', '$f23', 119 | '$f24', '$f25', '$f26', '$f27', '$f28', '$f29', '$f30', '$f31' 120 | # 121 | ' (64)', ' (65)', ' (66)', ' (67)', 122 | ' (68)', ' (69)', ' (70)', ' (71)', 123 | ] 124 | 125 | # http://refspecs.linux-foundation.org/elf/elfspec_ppc.pdf 126 | # https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html#DW-REG 127 | _REG_NAMES_POWERPC = [ 128 | # 0-31 129 | 'r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 130 | 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15', 131 | 'r16', 'r17', 'r18', 'r19', 'r20', 'r21', 'r22', 'r23', 132 | 'r24', 'r25', 'r26', 'r27', 'r28', 'r29', 'r30', 'r31', 133 | # 32-63 134 | 'f0', 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 135 | 'f8', 'f9', 'f10', 'f11', 'f12', 'f13', 'f14', 'f15', 136 | 'f16', 'f17', 'f18', 'f19', 'f20', 'f21', 'f22', 'f23', 137 | 'f24', 'f25', 'f26', 'f27', 'f28', 'f29', 'f30', 'f31', 138 | # 64, 65 139 | 'CR', 'FPSCR', 140 | # 66-99 141 | 'MSR', ' (67)', ' (68)', ' (69)', 142 | 'SR0', 'SR1', 'SR2', 'SR3', 'SR4', 'SR5', 'SR6', 'SR7', 143 | 'SR8', 'SR9', 'SR10', 'SR11', 'SR12', 'SR13', 'SR14', 'SR15', 144 | ' (86)', ' (87)', ' (88)', ' (89)', 145 | ' (90)', ' (91)', ' (92)', ' (93)', 146 | ' (94)', ' (95)', ' (96)', ' (97)', 147 | ' (98)', ' (99)', 148 | # 100, 101 149 | 'MQ', 'XER', 150 | # 102, 103 151 | '', '', 152 | # 104, 105 153 | 'RTCU', 'RTCL', 154 | # 106, 107 155 | '', '', 156 | # 108, 109 157 | 'LR', 'CTR' 158 | ] 159 | 160 | # https://developer.arm.com/documentation/ihi0057/e/?lang=en#dwarf-register-names 161 | _REG_NAMES_AArch64 = [ 162 | # 0-31 163 | 'x0', 'x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'x7', 164 | 'x8', 'x9', 'x10', 'x11', 'x12', 'x13', 'x14', 'x15', 165 | 'x16', 'x17', 'x18', 'x19', 'x20', 'x21', 'x22', 'x23', 166 | 'x24', 'x25', 'x26', 'x27', 'x28', 'x29', 'x30', 'sp', 167 | 168 | # 32, 33, 34 169 | '', 'elr_mode', 'ra_sign_state', 170 | 171 | # 35, 36 172 | ' (35)', ' (36)', 173 | 174 | # 37-45 175 | ' (37)', ' (38)', ' (39)', ' (40)', 176 | ' (41)', ' (42)', ' (43)', ' (44)', 177 | ' (45)', 178 | 179 | # 46, 47 180 | 'vg', 'ffr', 181 | 182 | # 48-63 183 | 'p0', 'p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 184 | 'p8', 'p9', 'p10', 'p11', 'p12', 'p13', 'p14', 'p15', 185 | 186 | # 64-95 187 | 'v0', 'v1', 'v2', 'v3', 'v4', 'v5', 'v6', 'v7', 188 | 'v8', 'v9', 'v10', 'v11', 'v12', 'v13', 'v14', 'v15', 189 | 'v16', 'v17', 'v18', 'v19', 'v20', 'v21', 'v22', 'v23', 190 | 'v24', 'v25', 'v26', 'v27', 'v28', 'v29', 'v30', 'v31', 191 | 192 | # 96-127 193 | 'z0', 'z1', 'z2', 'z3', 'z4', 'z5', 'z6', 'z7', 194 | 'z8', 'z9', 'z10', 'z11', 'z12', 'z13', 'z14', 'z15', 195 | 'z16', 'z17', 'z18', 'z19', 'z20', 'z21', 'z22', 'z23', 196 | 'z24', 'z25', 'z26', 'z27', 'z28', 'z29', 'z30', 'z31' 197 | ] 198 | 199 | _REG_NAMES_x64 = [ 200 | 'rax', 'rdx', 'rcx', 'rbx', 'rsi', 'rdi', 'rbp', 'rsp', 201 | 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15', 202 | 'rip', 'xmm0', 'xmm1', 'xmm2', 'xmm3', 'xmm4', 'xmm5', 'xmm6', 203 | 'xmm7', 'xmm8', 'xmm9', 'xmm10', 'xmm11', 'xmm12', 'xmm13', 'xmm14', 204 | 'xmm15', 'st0', 'st1', 'st2', 'st3', 'st4', 'st5', 'st6', 205 | 'st7', 'mm0', 'mm1', 'mm2', 'mm3', 'mm4', 'mm5', 'mm6', 206 | 'mm7', 'rflags', 'es', 'cs', 'ss', 'ds', 'fs', 'gs', 207 | '', '', 208 | 'fs.base', 'gs.base', 209 | '', '', 210 | 'tr', 211 | 'ldtr', 212 | 'mxcsr', 213 | 'fcw', 214 | 'fsw', 215 | 'xmm16', 'xmm17', 'xmm18', 'xmm19', 'xmm20', 'xmm21', 'xmm22', 'xmm23', 'xmm24', 'xmm25', 'xmm26', 'xmm27', 'xmm28', 'xmm29', 'xmm30', 'xmm31', 216 | ] 217 | 218 | 219 | def describe_reg_name(regnum, machine_arch=None, default=True): 220 | assert machine_arch in ['AArch64', 'ARM', 'x86', 'x64', 'MIPS', 'PowerPC'], 'unrecognized: %s' % machine_arch 221 | 222 | if machine_arch == 'x64': 223 | return _REG_NAMES_x64[regnum] 224 | if machine_arch == 'ARM': 225 | return _REG_NAMES_ARM[regnum] 226 | if machine_arch == 'MIPS': 227 | return _REG_NAMES_MIPS[regnum] 228 | if machine_arch == 'PowerPC': 229 | return _REG_NAMES_POWERPC[regnum] 230 | if machine_arch == 'AArch64': 231 | return _REG_NAMES_AArch64[regnum] 232 | return elftools_describe_reg_name(regnum, machine_arch, default) 233 | -------------------------------------------------------------------------------- /location_index.py: -------------------------------------------------------------------------------- 1 | # Copyright(c) 2020-2023 Vector 35 Inc 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files(the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and / or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | from .model.locations import Location, ExprOp 22 | from itertools import chain 23 | from collections import defaultdict 24 | from typing import MutableMapping, Union, List, Optional, Any, ValuesView, Tuple 25 | from enum import Enum, auto 26 | from dataclasses import dataclass 27 | import binaryninja as bn 28 | 29 | 30 | class VariableUpdateStatus(Enum): 31 | UPDATED = auto() 32 | ALREADY_UPDATED = auto() 33 | LOCATION_NOT_FOUND = auto() 34 | LOCATION_EXPRESSION_NOT_SUPPORTED = auto() 35 | INVALID_REGISTER = auto() 36 | CONFLICT = auto() 37 | 38 | 39 | @dataclass 40 | class VariableUpdate: 41 | status: VariableUpdateStatus 42 | storage_type: Optional[int] = None 43 | storage_id: Optional[int] = None 44 | 45 | 46 | @dataclass(eq=True, frozen=True) 47 | class StackLocation: 48 | offset: int 49 | 50 | 51 | class LocalVariableFrame(object): 52 | def __init__(self, function: bn.Function): 53 | self._function = function 54 | self._declarations: MutableMapping[bn.CoreVariable, Tuple[str, bn.Type]] = dict() 55 | 56 | def __contains__(self, v: bn.CoreVariable): 57 | return v in self._declarations 58 | 59 | def declare_variable(self, v: bn.CoreVariable, name: str, ty: bn.Type): 60 | if v not in self._declarations: 61 | assert(v.__class__ == bn.CoreVariable) 62 | self._declarations[v] = (name, ty) 63 | tc = ty._to_core_struct() 64 | ignore_disjoint_uses = False 65 | bn._binaryninjacore.BNCreateUserVariable(self._function.handle, v.to_BNVariable(), tc, name, ignore_disjoint_uses) 66 | return True 67 | 68 | prior_name, prior_type = self._declarations[v] 69 | return prior_name == name and prior_type == ty 70 | 71 | def items(self): 72 | return self._declarations.items() 73 | 74 | 75 | class LocationIndex(object): 76 | def __init__(self, function: bn.Function, frame_base: Optional[Union[Location, List[Location]]], log): 77 | self._function = function 78 | self._frame_base = frame_base 79 | self._log = log 80 | self._binary_view = self._function.view 81 | if self._binary_view.arch is None: 82 | raise Exception('unable to create a variable index for an undefined architecture (bv.arch is None)') 83 | self._arch = self._binary_view.arch 84 | self._arch_name = self._arch.name if self._arch.name else '' 85 | self._reg_aliases = {'armv7': {'r13': 'sp', 'r14': 'lr', 'r15': 'pc'}, 86 | 'thumb2': {'r13': 'sp', 'r14': 'lr', 'r15': 'pc'}, 87 | 'ppc': {'MSR': 'msr'} 88 | } 89 | self._stack_frame_regs = { 90 | 'x86_64': ['rbp', 'ebp'], 91 | 'armv7': ['sp', 'r13'], 92 | 'thyumb2': ['sp', 'r13']}.get(self._arch_name, list()) 93 | self._map: MutableMapping[Any, MutableMapping[int, bn.CoreVariable]] = defaultdict(dict) 94 | self._local_variables = LocalVariableFrame(self._function) 95 | self._calc_frame_base() 96 | self._build_index() 97 | 98 | def _calc_frame_base(self): 99 | self._stack_offset_adjustment = 0 100 | if isinstance(self._frame_base, Location): 101 | expr = self._frame_base.expr 102 | if self._frame_base.begin == 0: 103 | if expr == (ExprOp.CFA,): 104 | self._stack_offset_adjustment = -self._arch.address_size 105 | elif len(expr) == 1 and isinstance(expr[0], str): 106 | fbreg_value = self._get_reg_value_at_first_insn(bn.RegisterName(expr[0])) 107 | if fbreg_value is not None: 108 | self._stack_offset_adjustment = -fbreg_value 109 | elif expr[0] in self._stack_frame_regs: 110 | self._stack_offset_adjustment = self._arch.address_size 111 | 112 | def _get_reg_value_at_first_insn(self, reg_name: bn.RegisterName) -> Optional[int]: 113 | mlil = self._function.mlil 114 | if len(mlil) > 0: 115 | first_addr = mlil[0].address 116 | value = self._function.get_reg_value_at(first_addr, reg_name) 117 | if isinstance(value, bn.StackFrameOffsetRegisterValue): 118 | return value.value 119 | return None 120 | 121 | def _build_index(self): 122 | mlil = self._function.mlil 123 | assert mlil is not None 124 | for insn in self._function.mlil.instructions: 125 | if ( 126 | insn.operation == bn.MediumLevelILOperation.MLIL_TAILCALL 127 | or insn.operation == bn.MediumLevelILOperation.MLIL_TAILCALL_UNTYPED 128 | ): 129 | vars_accessed = insn.vars_read 130 | else: 131 | vars_accessed = chain(insn.vars_written, insn.vars_read) 132 | 133 | for v in vars_accessed: # type: ignore 134 | assert(isinstance(v, bn.Variable)) 135 | assert(isinstance(v._source_type, int)) 136 | cv = bn.CoreVariable(_source_type=v._source_type, index=v.index, storage=v.storage) 137 | if v.source_type == bn.VariableSourceType.RegisterVariableSourceType: 138 | self._map[self._arch.get_reg_name(bn.RegisterIndex(v.storage))][insn.address] = cv 139 | elif v.source_type == bn.VariableSourceType.StackVariableSourceType: 140 | offset = v.storage + self._stack_offset_adjustment 141 | self._map[StackLocation(offset=offset)][insn.address] = cv 142 | elif v.source_type == bn.VariableSourceType.FlagVariableSourceType: 143 | pass 144 | 145 | for v in self._function.vars: 146 | cv = bn.CoreVariable(_source_type=v._source_type, index=v.index, storage=v.storage) 147 | if v.source_type == bn.VariableSourceType.RegisterVariableSourceType: 148 | loc = self._arch.get_reg_name(bn.RegisterIndex(v.storage)) 149 | if loc not in self._map: 150 | self._map[loc][-1] = cv 151 | elif v.source_type == bn.VariableSourceType.StackVariableSourceType: 152 | offset = v.storage + self._stack_offset_adjustment 153 | loc = StackLocation(offset=offset) 154 | if loc not in self._map: 155 | self._map[loc][-1] = cv 156 | else: 157 | pass 158 | 159 | def update_variable(self, loc: Location, new_type: bn.Type, new_name: str) -> VariableUpdate: 160 | """Only update a variable if it has not already been updated. 161 | """ 162 | # Find the location. 163 | expr = loc.expr 164 | if len(expr) == 1: 165 | expr = expr[0] 166 | if expr == ExprOp.CFA: 167 | expr = StackLocation(offset=0) 168 | else: 169 | if self._arch.name in self._reg_aliases: 170 | assert(isinstance(expr, str)) 171 | alias = self._reg_aliases[self._arch_name].get(expr, None) 172 | if alias is not None: 173 | expr = alias 174 | r: bn.RegisterInfo = self._arch.regs.get(expr) # type: ignore 175 | if r is None: 176 | arch = self._function.arch 177 | assert(arch is not None) 178 | print(f'NO SUCH REG {expr} - {arch.name}') 179 | return VariableUpdate(status=VariableUpdateStatus.INVALID_REGISTER) 180 | expr = r.full_width_reg 181 | elif len(expr) == 3: 182 | if expr[2] == ExprOp.ADD and expr[0] == ExprOp.CFA: 183 | assert(isinstance(expr[1], int)) 184 | expr = StackLocation(offset=expr[1]) 185 | elif expr[2] == ExprOp.VAR_FIELD: 186 | expr = expr[0] 187 | else: 188 | self._log.debug(f'Unhandled location expression for "{new_name}": {loc}') 189 | return VariableUpdate(status=VariableUpdateStatus.LOCATION_EXPRESSION_NOT_SUPPORTED) 190 | else: 191 | self._log.debug(f'Unhandled location expression for "{new_name}": {loc}') 192 | return VariableUpdate(status=VariableUpdateStatus.LOCATION_EXPRESSION_NOT_SUPPORTED) 193 | 194 | if expr not in self._map: 195 | return VariableUpdate(status=VariableUpdateStatus.LOCATION_NOT_FOUND) 196 | 197 | var_list: Union[ValuesView[bn.CoreVariable], List[bn.CoreVariable]] 198 | 199 | addr_map = self._map[expr] 200 | if loc.begin == 0: 201 | var_list = addr_map.values() 202 | else: 203 | var_list = [] 204 | for addr in addr_map.keys(): 205 | end_addr = addr + self._binary_view.get_instruction_length(addr) - 1 206 | if loc.begin <= addr and end_addr <= loc.end: 207 | v = addr_map[addr] 208 | var_list.append(v) 209 | 210 | if len(var_list) == 0: 211 | return VariableUpdate(status=VariableUpdateStatus.LOCATION_NOT_FOUND) 212 | 213 | # Finally, update the variable name and type. 214 | result: List[VariableUpdate] = [] 215 | for cv in var_list: 216 | if self._local_variables.declare_variable(cv, new_name, new_type): 217 | result.append(VariableUpdate(status=VariableUpdateStatus.UPDATED, storage_type=cv._source_type, storage_id=cv.storage)) 218 | else: 219 | result.append(VariableUpdate(status=VariableUpdateStatus.CONFLICT)) 220 | 221 | for res in result: 222 | if res.status == VariableUpdateStatus.UPDATED: 223 | return res 224 | return result[-1] 225 | 226 | def propagate_names(self): 227 | """Using BFS, propagate along SET_VAR operations. 228 | """ 229 | queue = list(self._local_variables.items()) 230 | while queue: 231 | cv, (name, ty) = queue.pop(0) 232 | 233 | if ( 234 | cv.source_type == bn.VariableSourceType.StackVariableSourceType 235 | and ty.type_class == bn.TypeClass.ArrayTypeClass 236 | ): 237 | for insn in self._function.mlil.instructions: 238 | if isinstance(insn, bn.MediumLevelILSetVar): 239 | if isinstance(insn.src, bn.MediumLevelILAddressOf): 240 | if cv == insn.src.src: 241 | assert(insn.dest.type) 242 | dest_cv = self.declare_variable(insn.dest, name, insn.dest.type) 243 | if dest_cv is not None: 244 | queue.append((dest_cv, (name, insn.dest.type))) 245 | else: 246 | v = bn.Variable(self._function, cv.source_type, cv.index, cv.storage) 247 | for use_insn in self._function.mlil.get_var_uses(v): 248 | if isinstance(use_insn, bn.MediumLevelILSetVar): 249 | if isinstance(use_insn.src, bn.MediumLevelILVar): 250 | dest_cv = self.declare_variable(use_insn.dest, name, ty) 251 | if dest_cv is not None: 252 | queue.append((dest_cv, (name, ty))) 253 | 254 | for def_insn in self._function.mlil.get_var_definitions(v): 255 | if isinstance(def_insn, bn.MediumLevelILSetVar): 256 | if isinstance(def_insn.src, bn.MediumLevelILVar): 257 | src_cv = self.declare_variable(def_insn.src.src, name, ty) 258 | if src_cv is not None: 259 | queue.append((src_cv, (name, ty))) 260 | elif ( 261 | isinstance(def_insn.src, bn.MediumLevelILVarField) 262 | and def_insn.src.src.source_type == bn.VariableSourceType.RegisterVariableSourceType 263 | ): 264 | src_type = def_insn.src.src.type 265 | assert(src_type) 266 | src_cv = self.declare_variable(def_insn.src.src, name, src_type) 267 | if src_cv is not None: 268 | queue.append((src_cv, (name, src_type))) 269 | 270 | def declare_variable(self, v: bn.Variable, name: str, ty: bn.Type): 271 | cv = bn.CoreVariable(v._source_type, v.index, v.storage) 272 | if cv not in self._local_variables: 273 | if self._local_variables.declare_variable(cv, name, ty): 274 | return cv 275 | return None 276 | -------------------------------------------------------------------------------- /mapped_model.py: -------------------------------------------------------------------------------- 1 | # Copyright(c) 2021-2023 Vector 35 Inc 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files(the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and / or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | from typing import Optional 22 | import os 23 | from binaryninja import BinaryView, BinaryViewType 24 | from .model.analysis_model import AnalysisModel 25 | from .mapping import BinjaMap 26 | 27 | 28 | class AnalysisSession(object): 29 | def __init__( 30 | self, 31 | filename: Optional[str] = None, 32 | debug_root: Optional[str] = None, 33 | debug_file: Optional[str] = None, 34 | binary_view: Optional[BinaryView] = None, 35 | logger=None 36 | ): 37 | if filename is None and binary_view is None: 38 | raise ValueError('Must specify either a filename or binary view.') 39 | 40 | self.model: AnalysisModel 41 | self.binary_view: BinaryView 42 | self.debug_root = debug_root 43 | self.debug_file = debug_file 44 | 45 | if binary_view is None: 46 | if filename is None: 47 | raise ValueError('A filename must be specified when binary_view is None.') 48 | bv = BinaryViewType.get_view_of_file(filename, update_analysis=False) 49 | if bv is None: 50 | raise Exception(f'Unable to get binary view for file: {filename}') 51 | self.binary_view = bv 52 | else: 53 | self.binary_view = binary_view 54 | 55 | if self.binary_view.arch is None: 56 | raise Exception('The binary view has no valid architecture.') 57 | 58 | # Create the root module with the binary name as the module name. 59 | if filename is None: 60 | filename = self.binary_view.file.original_filename 61 | assert(isinstance(filename, str)) 62 | binary_name = os.path.basename(filename) 63 | 64 | debug_source = filename 65 | if self.debug_file is not None: 66 | debug_source = self.debug_file 67 | m = AnalysisModel.from_dwarf(debug_source, self.debug_root, name=binary_name, logger=logger) 68 | self.model = m if m else AnalysisModel(binary_name) 69 | 70 | assert(self.binary_view.arch) 71 | self.arch = self.binary_view.arch 72 | assert(self.binary_view.platform) 73 | self.platform = self.binary_view.platform 74 | self.mapping = BinjaMap(self.binary_view) 75 | 76 | def __del__(self): 77 | if hasattr(self, 'binary_view') and self.binary_view is not None: 78 | self.binary_view.abort_analysis() 79 | self.binary_view.file.close() # type: ignore 80 | del self.binary_view 81 | 82 | # @property 83 | # def name(self) -> str: 84 | # return os.path.basename(self.binary_view.file.original_filename) 85 | -------------------------------------------------------------------------------- /mapping.py: -------------------------------------------------------------------------------- 1 | # Copyright(c) 2020-2023 Vector 35 Inc 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files(the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and / or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | from uuid import UUID 22 | from typing import MutableMapping, Optional 23 | from .model.concrete_elements import Element 24 | 25 | 26 | class BinjaMap(object): 27 | def __init__(self, binary_view): 28 | self._binary_view = binary_view 29 | self._version_table: MutableMapping[UUID, int] = dict() 30 | 31 | def is_newer(self, element: Element): 32 | if element.uuid not in self._version_table: 33 | return True 34 | return element.version > self._version_table[element.uuid] 35 | 36 | def commit(self, element: Element) -> Optional[Element]: 37 | if element.uuid in self._version_table: 38 | if element.version <= self._version_table[element.uuid]: 39 | return None 40 | self._version_table[element.uuid] = element.version 41 | return element 42 | -------------------------------------------------------------------------------- /model/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright(c) 2020-2023 Vector 35 Inc 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files(the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and / or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | from .analysis_model import AnalysisModel 22 | from .component import Component, AnalysisRealm 23 | from .qualified_name import QualifiedName 24 | 25 | 26 | __all__ = ['AnalysisModel', 'AnalysisRealm', 'Component', 'QualifiedName'] 27 | -------------------------------------------------------------------------------- /model/abstract_elements.py: -------------------------------------------------------------------------------- 1 | # Copyright(c) 2021-2023 Vector 35 Inc 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files(the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and / or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | from typing import List, Tuple, Union, Optional, NewType 22 | from uuid import UUID, uuid4 23 | from .observer import Observable 24 | from .attributes import AttributeSet 25 | from .qualified_name import QualifiedName 26 | from .concrete_elements import Element 27 | 28 | 29 | class AbstractElement(object): 30 | def __init__(self, model: Optional[Observable] = None, name: Optional[QualifiedName] = None): 31 | self.uuid: UUID = uuid4() 32 | self.model = model 33 | self._name: QualifiedName = name if name else QualifiedName() 34 | self.attributes: Optional[AttributeSet] = None 35 | 36 | def __eq__(self, rhs: "AbstractElement"): 37 | return self.uuid == rhs.uuid 38 | 39 | def __hash__(self): 40 | return hash(self.uuid) 41 | 42 | @property 43 | def name(self): 44 | return self._name 45 | 46 | @name.setter 47 | def name(self, value: QualifiedName): 48 | if value == self._name: 49 | return 50 | old_name = self._name 51 | self._name = value 52 | if self.model is not None: 53 | self.model.notify('entity_renamed', **{'entity': self, 'old_name': old_name}) 54 | 55 | def has_attribute(self, key): 56 | if self.attributes is not None: 57 | return key in self.attributes 58 | else: 59 | return False 60 | 61 | def get_attribute(self, key): 62 | if self.attributes is not None: 63 | if key in self.attributes: 64 | return self.attributes[key] 65 | return None 66 | 67 | def set_attribute(self, key, value): 68 | if self.attributes is None: 69 | self.attributes = AttributeSet() 70 | self.attributes[key] = value 71 | 72 | def unset_attribute(self, key): 73 | if self.attributes is not None: 74 | del self.attributes[key] 75 | 76 | def append_attribute(self, key, value) -> None: 77 | if self.attributes is None: 78 | self.attributes = AttributeSet() 79 | self.attributes.append(key, value) 80 | 81 | 82 | Scheme = NewType('Scheme', str) 83 | 84 | SCHEME_CONSOLE = Scheme('console') 85 | SCHEME_FILESYSTEM = Scheme('file') 86 | SCHEME_NETWORK = Scheme('net') 87 | SCHEME_NETWORK_TCP = Scheme('tcp') 88 | SCHEME_NETWORK_UDP = Scheme('udp') 89 | SCHEME_NETWORK_HTTP = Scheme('http') 90 | SCHEME_PROCESS_HEAP = Scheme('mem') 91 | 92 | 93 | AbstractType = NewType('AbstractType', str) 94 | 95 | ABSTRACT_TYPE_NONE = AbstractType('None') 96 | ABSTRACT_TYPE_HEAP_MEMORY = AbstractType('HeapMemory') 97 | ABSTRACT_TYPE_FILE_OBJECT = AbstractType('FileObject') 98 | ABSTRACT_TYPE_FILE_HANDLE = AbstractType('FileHandle') 99 | ABSTRACT_TYPE_NETWORK_SOCKET = AbstractType('Socket') 100 | ABSTRACT_TYPE_DATASTRUCTURE_LIST = AbstractType('List') 101 | ABSTRACT_TYPE_DATASTRUCTURE_STACK = AbstractType('Stack') 102 | ABSTRACT_TYPE_DATASTRUCTURE_QUEUE = AbstractType('Queue') 103 | ABSTRACT_TYPE_DATASTRUCTURE_TREE = AbstractType('Tree') 104 | ABSTRACT_TYPE_DATASTRUCTURE_GRAPH = AbstractType('Graph') 105 | ABSTRACT_TYPE_DATASTRUCTURE_HASHTABLE = AbstractType('HashTable') 106 | ABSTRACT_TYPE_LINKED = AbstractType('LinkedDataStructure') 107 | 108 | 109 | class AbstractObject(AbstractElement): 110 | def __init__( 111 | self, 112 | name: QualifiedName, 113 | abstract_type: AbstractType, 114 | concrete_element: Optional[Element], 115 | concrete_allocator: Optional[Element], 116 | addr: Optional[int] = None 117 | ): 118 | super().__init__(name=name) 119 | self.abstract_type = abstract_type 120 | self.concrete_element = concrete_element 121 | self.concrete_allocator = concrete_allocator 122 | self.addr = addr 123 | self.context: List[Tuple[Element, Union["AbstractObject", int, str]]] = list() 124 | 125 | def inherit_properties(self, objects: List["AbstractObject"]): 126 | for attrs in filter(None, map(lambda x: x.attributes, objects)): 127 | for name, value in attrs.items(): 128 | if not self.has_attribute(name): 129 | self.set_attribute(name, value) 130 | else: 131 | if self.get_attribute(name) != value: 132 | self.unset_attribute(name) 133 | 134 | 135 | class AbstractFlow(AbstractElement): 136 | def __init__(self, source: AbstractObject, sink: AbstractObject): 137 | self.source = source 138 | self.sink = sink 139 | self.capacity: float = 0.0 # Number of bits (ratio of co-domain / domain) 140 | self.identity: float = 0.0 # Degree of transformation (e.g., edit distance) 141 | -------------------------------------------------------------------------------- /model/analysis_model.py: -------------------------------------------------------------------------------- 1 | # Copyright(c) 2021-2023 Vector 35 Inc 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files(the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and / or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | from typing import Iterable, MutableMapping, Optional, Union, Generator, Tuple, Any 22 | from uuid import UUID 23 | from itertools import chain 24 | 25 | from .qualified_name import QualifiedName 26 | from .abstract_elements import AbstractObject, AbstractFlow 27 | from .concrete_elements import Element, Type, Variable, Constant, Function 28 | from .component import Component, Selector 29 | from .attributes import AttributeSet, Attribute 30 | from .observer import Observable 31 | 32 | 33 | class AnalysisModel(Observable): 34 | VERSION = 1 35 | 36 | def __init__(self, name: str) -> None: 37 | super().__init__(None) 38 | self.name = name 39 | self._elements: MutableMapping[UUID, Element] = dict() 40 | self._types: MutableMapping[QualifiedName, Type] = dict() 41 | self._constants: MutableMapping[QualifiedName, Constant] = dict() 42 | self._variables: MutableMapping[QualifiedName, Variable] = dict() 43 | self._functions: MutableMapping[int, Function] = dict() 44 | self._component = Component(self, 'Components', selector=Selector.UNION) 45 | self._abstract_objects: List[AbstractObject] = list() 46 | self._abstract_flows: MutableMapping[QualifiedName, AbstractFlow] = dict() 47 | self._metadata = AttributeSet() 48 | 49 | @property 50 | def is_empty(self) -> bool: 51 | if ( 52 | len(self._types) > 0 53 | or len(self._constants) > 0 54 | or len(self._variables) > 0 55 | or len(self._functions) > 0 56 | or self._component.component_count > 0 57 | ): 58 | return False 59 | else: 60 | return True 61 | 62 | @property 63 | def metadata(self): 64 | return self._metadata 65 | 66 | def elements(self) -> Iterable[Union[Type, Constant, Variable, Function]]: 67 | return chain( 68 | self._types.values(), 69 | self._constants.values(), 70 | self._variables.values(), 71 | self._functions.values()) 72 | 73 | @property 74 | def element_count(self) -> int: 75 | return len(self._types) + len(self._constants) + len(self._variables) + len(self._functions) 76 | 77 | def abstract_elements(self) -> Iterable[Union[AbstractObject, AbstractFlow]]: 78 | return chain( 79 | self._abstract_objects, 80 | self._abstract_flows.values() 81 | ) 82 | 83 | @property 84 | def types(self) -> Iterable[Type]: 85 | return self._types.values() 86 | 87 | @property 88 | def constants(self) -> Iterable[Constant]: 89 | return self._constants.values() 90 | 91 | @property 92 | def variables(self) -> Iterable[Variable]: 93 | return self._variables.values() 94 | 95 | @property 96 | def functions(self) -> Iterable[Function]: 97 | return self._functions.values() 98 | 99 | @property 100 | def root_component(self) -> Component: 101 | return self._component 102 | 103 | def iter_components(self) -> Generator[Component, None, None]: 104 | def get_components(c: Component): 105 | for opnd in c.operands: 106 | if isinstance(opnd, Component): 107 | yield opnd 108 | for x in get_components(opnd): 109 | yield x 110 | 111 | for c in get_components(self._component): 112 | yield c 113 | 114 | def iter_leaf_components(self) -> Generator[Component, None, None]: 115 | def get_leaf_components(c: Component): 116 | if c.selector == Selector.ELEMENT_SET: 117 | yield c 118 | else: 119 | for opnd in c.operands: 120 | assert(isinstance(opnd, Component)) 121 | for x in get_leaf_components(opnd): 122 | yield x 123 | 124 | for leaf in get_leaf_components(self._component): 125 | yield leaf 126 | 127 | def attributes(self) -> Generator[Tuple[Any, Attribute], None, None]: 128 | for e in chain(self.elements(), self.abstract_elements()): 129 | if e.attributes is not None: 130 | for attr in e.attributes: 131 | yield (e, attr) 132 | 133 | def get_element_by_name(self, qname: QualifiedName) -> Optional[Element]: 134 | raise NotImplementedError('get_element_by_name') 135 | 136 | def get_element_by_uuid(self, uuid: UUID) -> Optional[Element]: 137 | return self._elements.get(uuid, None) 138 | 139 | def get_component_by_uuid(self, uuid: UUID) -> Optional[Component]: 140 | for c in self.iter_components(): 141 | if c.uuid == uuid: 142 | return c 143 | return None 144 | 145 | def get_function_at(self, addr: int): 146 | return self._functions.get(addr, None) 147 | 148 | def get_function_by_name(self, name: Union[QualifiedName, str]): 149 | if isinstance(name, str): 150 | name = QualifiedName(name) 151 | for fn in self._functions.values(): 152 | if fn.name == name: 153 | return fn 154 | return None 155 | 156 | def get_type_with_name(self, typename: QualifiedName): 157 | return self._types.get(typename, None) 158 | 159 | def add_variable(self, v: Variable): 160 | v.model = self 161 | self._variables[v.name] = v 162 | self._elements[v.uuid] = v 163 | self.notify('elements_added', **{'elements': [v]}) 164 | 165 | def add_constant(self, c: Constant): 166 | c.model = self 167 | self._constants[c.name] = c 168 | self._elements[c.uuid] = c 169 | self.notify('elements_added', **{'elements': [c]}) 170 | 171 | def add_function(self, f: Function): 172 | f.model = self 173 | if isinstance(f.entry_addr, int): 174 | self._functions[f.entry_addr] = f 175 | self._elements[f.uuid] = f 176 | self.notify('elements_added', **{'elements': [f]}) 177 | 178 | def add_type(self, ty: Type): 179 | ty.model = self 180 | self._types[ty.name] = ty 181 | self._elements[ty.uuid] = ty 182 | self.notify('elements_added', **{'elements': [ty]}) 183 | 184 | def get_components_containing(self, el: Element): 185 | for c in self.root_component.components(): 186 | if el in c: 187 | yield c 188 | 189 | def get_component(self, qname: QualifiedName) -> Optional[Component]: 190 | def get_component_helper(parent: Component, index: int): 191 | for c in parent.operands: 192 | if isinstance(c, Component): 193 | if c.name == qname[index]: 194 | if len(qname) == index - 1: 195 | return c 196 | else: 197 | return get_component_helper(c, index + 1) 198 | return None 199 | 200 | if qname.is_empty: 201 | return self._component 202 | 203 | # Find an existing component. 204 | for c in self._component.operands: 205 | assert(isinstance(c, Component)) 206 | if c.name == qname[0]: 207 | if len(qname) == 1: 208 | return c 209 | else: 210 | return get_component_helper(c, 1) 211 | 212 | return None 213 | 214 | def make_leaf_component(self, qname: QualifiedName) -> Component: 215 | def make_component_helper(parent: Component, index: int): 216 | for c in parent.operands: 217 | if isinstance(c, Component): 218 | if c.name == qname[index]: 219 | if len(qname) - 1 == index: 220 | return c 221 | else: 222 | return make_component_helper(c, index + 1) 223 | 224 | # Create components according to the qname. 225 | for child_name in qname[index:]: 226 | parent.selector = Selector.UNION 227 | c = Component(None, child_name) 228 | parent.add_components(c) 229 | parent = c 230 | 231 | return parent 232 | 233 | if qname.is_empty: 234 | return self._component 235 | 236 | # Find an existing component. 237 | for c in self._component.operands: 238 | assert(isinstance(c, Component)) 239 | if c.name == qname[0]: 240 | if len(qname) == 1: 241 | return c 242 | else: 243 | return make_component_helper(c, 1) 244 | 245 | # Create components according to the qname. 246 | top = Component(None, qname[0]) 247 | parent = top 248 | for child_name in qname[1:]: 249 | parent.selector = Selector.UNION 250 | c = Component(None, child_name) 251 | parent.add_components(c) 252 | parent = c 253 | self._component.add_components(top) 254 | return parent 255 | 256 | def add_abstract_object(self, obj: AbstractObject): 257 | obj.model = self 258 | self._abstract_objects.append(obj) 259 | self.notify('abstract_elements_added', **{'elements': [obj]}) 260 | 261 | def add_abstract_flow(self, flow: AbstractFlow): 262 | flow.model = self 263 | self._abstract_flows[flow.name] = flow 264 | self.notify('abstract_elements_added', **{'elements': [flow]}) 265 | 266 | def select_elements(self, attr_name, attr_value: Any = None) -> Generator[Union[Element, Component], None, None]: 267 | for el in self.elements(): 268 | if el.has_attribute(attr_name): 269 | if attr_value is None or el.get_attribute(attr_name) == attr_value: 270 | yield el 271 | if isinstance(el, Function): 272 | for param in el.parameters: 273 | if param.has_attribute(attr_name): 274 | if attr_value is None or param.get_attribute(attr_name) == attr_value: 275 | yield param 276 | if el.return_value: 277 | if el.return_value.has_attribute(attr_name): 278 | if attr_value is None or el.return_value.get_attribute(attr_name) == attr_value: 279 | yield el.return_value 280 | 281 | @staticmethod 282 | def from_dwarf(filename: str, debug_root: Optional[str] = None, name: str = '', logger=None) -> Optional["AnalysisModel"]: 283 | from elftools.elf.elffile import ELFFile 284 | from ..io.dwarf_import import import_ELF_DWARF_into_model 285 | 286 | def ELF_has_debug_info(elf_file: ELFFile) -> bool: 287 | return ( 288 | elf_file.get_section_by_name('.debug_info') is not None 289 | or elf_file.get_section_by_name('.zdebug_info') is not None 290 | ) 291 | 292 | elf_file = ELFFile(open(filename, 'rb')) 293 | if ELF_has_debug_info(elf_file) is False: 294 | return None 295 | 296 | model = AnalysisModel(name=name) 297 | import_ELF_DWARF_into_model(elf_file, model, debug_root=debug_root, logger=logger) 298 | return model 299 | -------------------------------------------------------------------------------- /model/attributes.py: -------------------------------------------------------------------------------- 1 | # Copyright(c) 2021-2023 Vector 35 Inc 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files(the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and / or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | from typing import Any, Optional, Union 22 | from dataclasses import dataclass 23 | 24 | 25 | @dataclass 26 | class Attribute: 27 | name: str 28 | type: Any 29 | 30 | def __hash__(self): 31 | return self.name.__hash__() 32 | 33 | def __eq__(self, other): 34 | if isinstance(other, Attribute): 35 | return self.name == other.name and self.type == other.type 36 | elif isinstance(other, str): 37 | return self.name == other 38 | return NotImplemented 39 | 40 | 41 | class AttributeSet(dict): 42 | def __getitem__(self, key: Union[str, Attribute]) -> Optional[Any]: 43 | if isinstance(key, Attribute): 44 | key = key.name 45 | if super().__contains__(key): 46 | return super().__getitem__(key) 47 | else: 48 | return None 49 | 50 | def __setitem__(self, key: Union[str, Attribute], value: Any) -> None: 51 | if isinstance(key, Attribute): 52 | key = key.name 53 | return super().__setitem__(key, value) 54 | 55 | def __contains__(self, key: Union[str, Attribute]) -> bool: 56 | if isinstance(key, Attribute): 57 | key = key.name 58 | return super().__contains__(key) 59 | 60 | def append(self, key: Union[str, Attribute], value) -> None: 61 | v = self[key] 62 | if isinstance(v, list): 63 | if value not in v: 64 | v.append(value) 65 | elif v is None: 66 | self[key] = [value] 67 | -------------------------------------------------------------------------------- /model/component.py: -------------------------------------------------------------------------------- 1 | # Copyright(c) 2021-2023 Vector 35 Inc 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files(the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and / or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | import sys 22 | from dataclasses import dataclass, field 23 | from typing import Optional, List, Union, Iterable, Generator 24 | from uuid import UUID, uuid4 25 | from enum import Enum 26 | from .observer import Observable 27 | from .concrete_elements import Element, Function, Type, Variable 28 | from .attributes import AttributeSet 29 | 30 | 31 | class AnalysisRealm(Enum): 32 | INTERNAL = 0 33 | EXTERNAL = 1 34 | RUNTIME = 2 35 | 36 | 37 | class Visibility(Enum): 38 | PUBLIC = 0 39 | PRIVATE = 1 40 | 41 | 42 | class Selector(Enum): 43 | ELEMENT_SET = 0 44 | UNION = 1 45 | INTERSECTION = 2 46 | SYMMETRIC_DIFFERENCE = 3 47 | 48 | 49 | @dataclass 50 | class ComponentMember: 51 | element: Element 52 | visibility: Visibility = Visibility.PRIVATE 53 | 54 | 55 | ComponentOperand = Union[ComponentMember, "Component"] 56 | 57 | 58 | @dataclass 59 | class Component: 60 | model: Optional[Observable] 61 | name: str 62 | uuid: UUID = field(init=False, default_factory=uuid4) 63 | parent: Optional["Component"] = None 64 | realm: AnalysisRealm = AnalysisRealm.INTERNAL 65 | selector: Selector = Selector.ELEMENT_SET 66 | operands: List[ComponentOperand] = field(default_factory=list) 67 | attributes: AttributeSet = field(default_factory=AttributeSet) 68 | 69 | def __hash__(self): 70 | return hash(self.uuid) 71 | 72 | def __len__(self): 73 | c = 0 74 | for opnd in self.operands: 75 | if isinstance(opnd, Component): 76 | c += len(opnd) 77 | else: 78 | c += 1 79 | return c 80 | 81 | def __contains__(self, el: Element): 82 | assert(el is not None) 83 | if self.is_leaf(): 84 | for opnd in self.operands: 85 | assert(isinstance(opnd, ComponentMember)) 86 | if el == opnd.element: 87 | return True 88 | return False 89 | 90 | def __repr__(self): 91 | return f'' 92 | 93 | @property 94 | def pathname(self) -> str: 95 | tmp = [] 96 | p = self 97 | while p is not None and p.parent is not None: 98 | tmp.insert(0, p.name) 99 | p = p.parent 100 | return '/'.join(tmp) 101 | 102 | def rename(self, value: str): 103 | if value == self.name: 104 | return 105 | old_name = self.name 106 | self.name = value 107 | if self.model is not None: 108 | self.model.notify('component_renamed', **{'component': self, 'old_name': old_name}) 109 | 110 | def set_model(self, m): 111 | self.model = m 112 | if self.selector != Selector.ELEMENT_SET: 113 | for opnd in self.operands: 114 | if isinstance(opnd, Component): 115 | opnd.set_model(m) 116 | 117 | @property 118 | def component_count(self) -> int: 119 | if self.is_leaf(): 120 | return 0 121 | else: 122 | return len(self.operands) 123 | 124 | @property 125 | def lowerbound(self) -> int: 126 | low = sys.maxsize 127 | for opnd in self.operands: 128 | if isinstance(opnd, Component): 129 | low = min(opnd.lowerbound, low) 130 | elif isinstance(opnd, ComponentMember): 131 | el = opnd.element 132 | if isinstance(el, Function): 133 | low = min(el.lowerbound, low) 134 | else: 135 | assert False, f'{type(opnd)} is an invalid component operand' 136 | return low 137 | 138 | @property 139 | def upperbound(self) -> int: 140 | high = 0 141 | for opnd in self.operands: 142 | if isinstance(opnd, Component): 143 | high = max(opnd.lowerbound, high) 144 | elif isinstance(opnd, ComponentMember): 145 | el = opnd.element 146 | if isinstance(el, Function): 147 | high = max(el.upperbound, high) 148 | return high 149 | 150 | def is_internal(self) -> bool: 151 | return self.realm == AnalysisRealm.INTERNAL 152 | 153 | def is_leaf(self) -> bool: 154 | return self.selector == Selector.ELEMENT_SET 155 | 156 | def elements(self) -> Generator[Element, None, None]: 157 | for opnd in self.operands: 158 | if isinstance(opnd, Component): 159 | for x in opnd.elements(): 160 | yield x 161 | else: 162 | assert(isinstance(opnd, ComponentMember)) 163 | yield opnd.element 164 | 165 | def functions(self) -> Generator[Function, None, None]: 166 | for opnd in self.operands: 167 | if isinstance(opnd, Component): 168 | for x in opnd.functions(): 169 | yield x 170 | else: 171 | assert(isinstance(opnd, ComponentMember)) 172 | if isinstance(opnd.element, Function): 173 | yield opnd.element 174 | 175 | def variables(self) -> Generator[Variable, None, None]: 176 | for opnd in self.operands: 177 | if isinstance(opnd, Component): 178 | for x in opnd.variables(): 179 | yield x 180 | else: 181 | assert(isinstance(opnd, ComponentMember)) 182 | if isinstance(opnd.element, Variable): 183 | yield opnd.element 184 | 185 | def types(self) -> Generator[Type, None, None]: 186 | for opnd in self.operands: 187 | if isinstance(opnd, Component): 188 | for x in opnd.types(): 189 | yield x 190 | else: 191 | assert(isinstance(opnd, ComponentMember)) 192 | if isinstance(opnd.element, Type): 193 | yield opnd.element 194 | 195 | def components(self) -> Generator["Component", None, None]: 196 | if self.selector != Selector.ELEMENT_SET: 197 | for opnd in self.operands: 198 | if isinstance(opnd, Component): 199 | yield opnd 200 | for x in opnd.components(): 201 | yield x 202 | 203 | def add_components(self, components: Union["Component", Iterable["Component"]]): 204 | if self.selector != Selector.ELEMENT_SET: 205 | if isinstance(components, Component): 206 | components = [components] 207 | pos = len(self.operands) 208 | for c in components: 209 | assert(c.uuid != self.uuid) 210 | c.parent = self 211 | c.set_model(self.model) 212 | self.operands.append(c) 213 | if self.model and pos < len(self.operands): 214 | self.model.notify('component_operands_added', **{'component': self, 'operands': list(self.operands[pos:])}) 215 | else: 216 | raise Exception('Cannot add components to ELEMENT_SET') 217 | 218 | def add_elements(self, elements: Union[Element, Iterable[Element]]): 219 | if self.is_leaf(): 220 | if isinstance(elements, Element): 221 | elements = [elements] 222 | pos = len(self.operands) 223 | self.operands.extend(map(lambda el: ComponentMember(el), elements)) 224 | if self.model and pos < len(self.operands): 225 | self.model.notify('component_operands_added', **{'component': self, 'operands': list(self.operands[pos:])}) 226 | 227 | def get_visibility(self, el: Element) -> Optional[Visibility]: 228 | if self.is_leaf(): 229 | for opnd in self.operands: 230 | assert(isinstance(opnd, ComponentMember)) 231 | if el == opnd.element: 232 | return opnd.visibility 233 | return None 234 | -------------------------------------------------------------------------------- /model/concrete_elements.py: -------------------------------------------------------------------------------- 1 | # Copyright(c) 2021-2023 Vector 35 Inc 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files(the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and / or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | import sys 22 | from dataclasses import dataclass 23 | from typing import List, Optional, Any, Iterable, Union, Tuple, MutableMapping 24 | from itertools import chain 25 | from uuid import uuid4, UUID 26 | from enum import Enum 27 | from .qualified_name import QualifiedName 28 | from .locations import Location 29 | from .attributes import AttributeSet 30 | from .observer import Observable 31 | 32 | 33 | class Element(object): 34 | def __init__(self, model: Optional[Observable] = None, name: Optional[QualifiedName] = None): 35 | self.uuid: UUID = uuid4() 36 | self.version: int = 0 37 | self.model: Optional[Observable] = model 38 | self._name: QualifiedName = name if name else QualifiedName() 39 | self.attributes: Optional[AttributeSet] = None 40 | 41 | def __eq__(self, rhs: "Element"): 42 | return self.uuid == rhs.uuid 43 | 44 | def __hash__(self): 45 | return hash(self.uuid) 46 | 47 | def increment_version(self): 48 | self.version += 1 49 | 50 | @property 51 | def name(self): 52 | return self._name 53 | 54 | @name.setter 55 | def name(self, value: QualifiedName): 56 | if value == self._name: 57 | return 58 | old_name = self._name 59 | self._name = value 60 | if self.model is not None: 61 | self.model.notify('element_renamed', **{'element': self, 'old_name': old_name}) 62 | 63 | def has_attribute(self, key): 64 | if self.attributes is not None: 65 | return key in self.attributes 66 | else: 67 | return False 68 | 69 | def get_attribute(self, key): 70 | if self.attributes is not None: 71 | if key in self.attributes: 72 | return self.attributes[key] 73 | return None 74 | 75 | def set_attribute(self, key, value): 76 | if self.attributes is None: 77 | self.attributes = AttributeSet() 78 | self.attributes[key] = value 79 | 80 | def append_attribute(self, key, value) -> None: 81 | if self.attributes is None: 82 | self.attributes = AttributeSet() 83 | self.attributes.append(key, value) 84 | 85 | def get_child_by_uuid(self, uuid: UUID) -> Optional["Element"]: 86 | return None 87 | 88 | 89 | class Type(Element): 90 | def __init__(self, name: Optional[QualifiedName] = None, byte_size: Optional[int] = 0): 91 | super().__init__(name=name) 92 | self.byte_size: Optional[int] = byte_size 93 | 94 | def is_equivalent(self, ty: "Type") -> bool: 95 | if type(self) != type(ty): 96 | return False 97 | return self.byte_size == ty.byte_size 98 | 99 | def has_ancestor_class(self, typeclass) -> bool: 100 | return isinstance(self, typeclass) 101 | 102 | def strip_modifiers(self) -> "Type": 103 | return self 104 | 105 | 106 | class BaseType(Type): 107 | def __init__(self, name: str, byte_size: Optional[int]): 108 | super().__init__(name=QualifiedName(name), byte_size=byte_size) 109 | assert(self.byte_size is None or isinstance(self.byte_size, int)) 110 | 111 | def __repr__(self): 112 | return self.name[0] 113 | 114 | def __str__(self): 115 | return self.name[0] 116 | 117 | def is_equivalent(self, ty: "Type") -> bool: 118 | if type(self) != type(ty): 119 | return False 120 | return self.byte_size == ty.byte_size and self.name == ty.name 121 | 122 | @staticmethod 123 | def int(byte_size: int) -> "Type": 124 | return BaseType(name=f'int{byte_size*8}_t', byte_size=byte_size) 125 | 126 | 127 | class VoidType(BaseType): 128 | def __init__(self): 129 | super().__init__('void', 0) 130 | self.uuid = UUID('00000000000000000000000000000000') 131 | 132 | 133 | VOID = VoidType() 134 | 135 | 136 | class VariadicType(Type): 137 | def __init__(self): 138 | super().__init__() 139 | self.uuid = UUID('00000000000000000000000000000001') 140 | 141 | def __repr__(self): 142 | return '...' 143 | 144 | def __str__(self): 145 | return '...' 146 | 147 | 148 | VARIADIC = VariadicType() 149 | 150 | 151 | class AliasType(Type): 152 | def __init__(self, name: QualifiedName, type: Type): 153 | super().__init__(name=name) 154 | assert(isinstance(type, Type)) 155 | self.type = type 156 | 157 | def __repr__(self): 158 | if isinstance(self.type, AliasType): 159 | return f'typedef {str(self.type)} {str(self.name)}' 160 | else: 161 | return f'typedef {self.type.__repr__()} {str(self.name)}' 162 | 163 | def __str__(self): 164 | return str(self.name) 165 | 166 | def is_equivalent(self, ty: "Type") -> bool: 167 | if type(self) != type(ty): 168 | return False 169 | assert(isinstance(ty, AliasType)) 170 | return self.byte_size == ty.byte_size and self.type.is_equivalent(ty.type) 171 | 172 | def is_alias_for_type(self, type_class) -> bool: 173 | if isinstance(self.type, type_class): 174 | return True 175 | if isinstance(self.type, AliasType): 176 | return self.type.is_alias_for_type(type_class) 177 | return False 178 | 179 | def resolve_alias(self) -> Type: 180 | if not isinstance(self.type, AliasType): 181 | return self.type 182 | return self.type.resolve_alias() 183 | 184 | def has_ancestor_class(self, typeclass) -> bool: 185 | if typeclass == AliasType: 186 | return True 187 | return self.type.has_ancestor_class(typeclass) 188 | 189 | def strip_modifiers(self) -> Type: 190 | return self.type.strip_modifiers() 191 | 192 | 193 | class PointerType(Type): 194 | def __init__(self, byte_size: Optional[int], target_type: Type = VOID, nullable: bool = True): 195 | super().__init__(byte_size=byte_size) 196 | self.target_type = target_type 197 | self.nullable = nullable 198 | 199 | def __repr__(self): 200 | if self.nullable is True: 201 | return f'{str(self.target_type)} *' 202 | else: 203 | return f'{str(self.target_type)} &' 204 | 205 | def __str__(self): 206 | if self.nullable is True: 207 | return f'{str(self.target_type)} *' 208 | else: 209 | return f'{str(self.target_type)} &' 210 | 211 | def is_equivalent(self, ty: Type) -> bool: 212 | if type(self) != type(ty): 213 | return False 214 | assert(isinstance(ty, PointerType)) 215 | return ( 216 | self.byte_size == ty.byte_size 217 | and self.nullable == ty.nullable 218 | and self.target_type.is_equivalent(ty.target_type) 219 | ) 220 | 221 | def has_ancestor_class(self, typeclass) -> bool: 222 | if typeclass == PointerType: 223 | return True 224 | return self.target_type.has_ancestor_class(typeclass) 225 | 226 | def strip_modifiers(self) -> Type: 227 | return self.target_type.strip_modifiers() 228 | 229 | 230 | VOID_PTR = PointerType(None, target_type=VOID) 231 | 232 | 233 | class ConstType(Type): 234 | def __init__(self, type: Type): 235 | super().__init__() 236 | self.type = type 237 | assert(self.name.is_empty) 238 | 239 | def __repr__(self) -> str: 240 | return f'const {self.type.__repr__()}' 241 | 242 | def __str__(self): 243 | return f'{self.type} const' 244 | 245 | def has_ancestor_class(self, typeclass) -> bool: 246 | if typeclass == ConstType: 247 | return True 248 | return self.type.has_ancestor_class(typeclass) 249 | 250 | def is_equivalent(self, ty: "Type") -> bool: 251 | if type(self) != type(ty): 252 | return False 253 | assert(isinstance(ty, ConstType)) 254 | return (self.byte_size == ty.byte_size and self.type.is_equivalent(ty.type)) 255 | 256 | def strip_modifiers(self) -> Type: 257 | return self.type.strip_modifiers() 258 | 259 | 260 | class VolatileType(Type): 261 | def __init__(self, type: Type): 262 | super().__init__() 263 | self.type = type 264 | assert(self.name.is_empty) 265 | 266 | def __repr__(self) -> str: 267 | return f'volatile {self.type.__repr__()}' 268 | 269 | def __str__(self): 270 | return f'{self.type} volatile' 271 | 272 | def has_ancestor_class(self, typeclass) -> bool: 273 | if typeclass == VolatileType: 274 | return True 275 | return self.type.has_ancestor_class(typeclass) 276 | 277 | def is_equivalent(self, ty: "Type") -> bool: 278 | if type(self) != type(ty): 279 | return False 280 | assert(isinstance(ty, VolatileType)) 281 | return (self.byte_size == ty.byte_size and self.type.is_equivalent(ty.type)) 282 | 283 | def strip_modifiers(self) -> Type: 284 | return self.type.strip_modifiers() 285 | 286 | 287 | class ArrayType(Type): 288 | def __init__(self, element_type: Type, count: int, name: Optional[QualifiedName] = None): 289 | super().__init__(name=name) 290 | self.element_type = element_type 291 | self.count = count 292 | 293 | def __str__(self): 294 | if self.count == 0: 295 | return f'{str(self.element_type)} []' 296 | else: 297 | return f'{str(self.element_type)} [{self.count}]' 298 | 299 | def has_ancestor_class(self, typeclass) -> bool: 300 | if typeclass == ArrayType: 301 | return True 302 | return self.element_type.has_ancestor_class(typeclass) 303 | 304 | def is_equivalent(self, ty: "Type") -> bool: 305 | if type(self) != type(ty): 306 | return False 307 | assert(isinstance(ty, ArrayType)) 308 | return ( 309 | self.byte_size == ty.byte_size 310 | and self.count == ty.count 311 | and self.element_type.is_equivalent(ty.element_type) 312 | ) 313 | 314 | 315 | class StringType(Type): 316 | def __init__(self, char_size: int, is_null_terminated: bool, byte_size: int): 317 | super().__init__(byte_size=byte_size) 318 | self.char_size = char_size 319 | self.is_null_terminated = is_null_terminated 320 | 321 | def is_equivalent(self, ty: "Type") -> bool: 322 | if type(self) != type(ty): 323 | return False 324 | assert(isinstance(ty, StringType)) 325 | return ( 326 | self.byte_size == ty.byte_size 327 | and self.char_size == ty.char_size 328 | and self.is_null_terminated == ty.is_null_terminated 329 | ) 330 | 331 | 332 | class Constant(Element): 333 | def __init__(self, name: QualifiedName, type: Optional[Type], value: Any): 334 | super().__init__(name=name) 335 | self.type = type 336 | self.value: Any = None 337 | 338 | 339 | class Variable(Element): 340 | def __init__(self, name: QualifiedName, addr: Optional[int] = None, type: Optional[Type] = VOID, value: Any = None): 341 | super().__init__(name=name) 342 | self.addr = addr 343 | self.type = type 344 | self.initial_value = value 345 | 346 | 347 | class AccessSpecifier(Enum): 348 | UNDEFINED = 0 349 | PUBLIC = 1 350 | PRIVATE = 2 351 | PROTECTED = 2 352 | 353 | 354 | class MemberPermissions(Enum): 355 | READ_ONLY = 0 356 | READ_WRITE = 1 357 | 358 | 359 | @dataclass 360 | class Member: 361 | accessibility: AccessSpecifier = AccessSpecifier.PUBLIC 362 | permissions: MemberPermissions = MemberPermissions.READ_ONLY 363 | 364 | 365 | @dataclass 366 | class MemberType(Member): 367 | type: Type = VOID 368 | 369 | def __repr__(self): 370 | return self.type.__repr__() 371 | 372 | def __str__(self) -> str: 373 | return str(self.type) 374 | 375 | 376 | @dataclass 377 | class MemberConstant(Member): 378 | constant: Constant = None # type: ignore 379 | 380 | def __repr__(self): 381 | return self.constant.__repr__() 382 | 383 | def __str__(self) -> str: 384 | return str(self.constant) 385 | 386 | 387 | @dataclass 388 | class MemberFunction(Member): 389 | function: "Function" = None # type: ignore 390 | read_only: bool = False 391 | 392 | def __repr__(self): 393 | if self.read_only: 394 | return f'{self.function.__repr__()} const' 395 | else: 396 | return self.function.__repr__() 397 | 398 | def __str__(self) -> str: 399 | return self.function.__str__() 400 | 401 | 402 | class Field(Element): 403 | def __init__(self, name: Optional[str], offset: int, type: Type, composite: Optional[Element] = None): 404 | super().__init__(name=QualifiedName(name) if name else QualifiedName()) 405 | self.owner = composite 406 | self.offset = offset 407 | self.type = type 408 | 409 | @property 410 | def local_name(self) -> str: 411 | if len(self.name) == 0: 412 | return '' 413 | else: 414 | return self.name[-1] 415 | 416 | @local_name.setter 417 | def local_name(self, n: str): 418 | old_name = self.name 419 | self.name = self.name.parent.concat(n) 420 | if self.owner is not None and self.owner.model: 421 | self.owner.model.notify('element_renamed', **{'element': self, 'old_name': old_name}) 422 | 423 | @property 424 | def size(self) -> Optional[int]: 425 | # if self.type.byte_size is None: 426 | # raise Exception('Field has no size') 427 | return self.type.byte_size 428 | 429 | @property 430 | def storage(self) -> Tuple[int, int]: 431 | sz = self.size 432 | return (self.offset, sz if sz else 0) 433 | 434 | 435 | @dataclass 436 | class MemberField(Member): 437 | field: Field = None # type: ignore 438 | 439 | def __repr__(self): 440 | if self.field.local_name == '': 441 | if isinstance(self.field.type, CompositeType): 442 | return self.field.type.__repr__() 443 | assert(isinstance(self.field.type, AliasType)) 444 | cty = self.field.type.resolve_alias() 445 | return cty.__repr__() 446 | else: 447 | return f'{str(self.field.type)} {self.field.local_name}' 448 | 449 | def __str__(self) -> str: 450 | assert(isinstance(self.field.local_name, str)) 451 | return self.field.local_name 452 | 453 | 454 | class CompositeLayout(Enum): 455 | DISJOINT = 0 456 | UNION = 1 457 | 458 | 459 | class CompositePolicy(object): 460 | def __init__(self, name: str, default_access: AccessSpecifier, layout_type: CompositeLayout = CompositeLayout.DISJOINT): 461 | self.name = name 462 | self.default_access = default_access 463 | self.layout_type = layout_type 464 | 465 | def add_field(self, composite: "CompositeType", field): 466 | field.name = composite.name.concat(field.name) 467 | composite.members.append(MemberField(accessibility=self.default_access, field=field)) 468 | 469 | def add_unnamed_field(self, composite: "CompositeType", field: Field): 470 | def is_composite_like(ty: Type) -> bool: 471 | if not isinstance(ty, CompositeType): 472 | if not (isinstance(ty, AliasType) and ty.is_alias_for_type(CompositeType)): 473 | return False 474 | return True 475 | 476 | if not is_composite_like(field.type): 477 | raise Exception('Invalid type for field') 478 | if field.type == composite: 479 | raise Exception('Invalid circular definition') 480 | 481 | field.name = composite.name.concat('') 482 | composite.members.append(MemberField(accessibility=self.default_access, field=field)) 483 | 484 | 485 | CLASS_COMPOSITE_POLICY = CompositePolicy('class', AccessSpecifier.PRIVATE) 486 | STRUCT_COMPOSITE_POLICY = CompositePolicy('struct', AccessSpecifier.PUBLIC) 487 | UNION_COMPOSITE_POLICY = CompositePolicy('union', AccessSpecifier.PUBLIC, layout_type=CompositeLayout.UNION) 488 | 489 | 490 | class CompositeType(Type): 491 | def __init__(self, policy: CompositePolicy, name: Optional[QualifiedName], byte_size: Optional[int] = None): 492 | super().__init__(name=name, byte_size=byte_size) 493 | self.policy = policy 494 | self.members: List[Member] = list() 495 | self.storage_index: MutableMapping[Tuple[int, int], MemberField] = dict() 496 | 497 | def __repr__(self) -> str: 498 | # TODO: fields vs. non-fields 499 | # TODO: sort fields by offset 500 | # TODO: check for non-overlapping fields 501 | member_types = (m.__repr__() for m in self.types()) 502 | member_constants = (m.__repr__() for m in self.constants()) 503 | member_functions = (f.__repr__() for f in self.functions()) 504 | member_fields = (f.__repr__() for f in sorted(self.fields(), key=lambda mf: mf.field.offset)) 505 | decls = ';\n'.join(chain(member_types, member_constants, member_functions, member_fields, '')) 506 | body = '\n ' + decls.replace('\n', '\n ') + ';' if decls else '' 507 | return f'{self.policy.name} {str(self.name)}\n{{{body}\n}}' 508 | 509 | def __str__(self) -> str: 510 | return f'{self.policy.name} {str(self.name)}' 511 | 512 | def __len__(self) -> int: 513 | return len(self.members) 514 | 515 | def types(self) -> Iterable[MemberType]: 516 | return filter(lambda m: isinstance(m, MemberType), self.members) # type: ignore 517 | 518 | def constants(self) -> Iterable[MemberConstant]: 519 | return filter(lambda m: isinstance(m, MemberConstant), self.members) # type: ignore 520 | 521 | def functions(self) -> Iterable[MemberFunction]: 522 | return filter(lambda m: isinstance(m, MemberFunction), self.members) # type: ignore 523 | 524 | def fields(self) -> Iterable[MemberField]: 525 | return filter(lambda m: isinstance(m, MemberField), self.members) # type: ignore 526 | 527 | def get_child_by_uuid(self, uuid: UUID) -> Optional[Element]: 528 | for member in self.fields(): 529 | if member.field.uuid == uuid: 530 | return member.field 531 | # TODO: recurse into anonymous composites 532 | return None 533 | 534 | @property 535 | def field_count(self) -> int: 536 | c = 0 537 | for m in filter(lambda m: isinstance(m, MemberField), self.members): 538 | c += 1 539 | return c 540 | 541 | def add_field(self, field: Field): 542 | field.owner = self 543 | self.policy.add_field(self, field) 544 | 545 | def add_unnamed_field(self, field: Field): 546 | field.owner = self 547 | self.policy.add_unnamed_field(self, field) 548 | 549 | def add_constant(self, local_name: str, c: Constant): 550 | c.name = self.name.concat(local_name) 551 | self.members.append(MemberConstant(self.policy.default_access, constant=c)) 552 | 553 | def add_function(self, fn: "Function", perms: MemberPermissions = MemberPermissions.READ_WRITE): 554 | fn.name = self.name.concat(fn.name[-1]) 555 | self.members.append(MemberFunction(self.policy.default_access, perms, fn)) 556 | 557 | def merge_from(self, rhs: "CompositeType") -> bool: 558 | if self.policy != rhs.policy: 559 | return False 560 | for m in rhs.members: 561 | if not self._contains_congruent_member(m): 562 | self._add_member(m) 563 | return True 564 | 565 | def _add_member(self, m: Member): 566 | if isinstance(m, MemberFunction): 567 | self.add_function(m.function, m.permissions) 568 | elif isinstance(m, MemberConstant): 569 | self.add_constant(m.constant.name[-1], m.constant) 570 | elif isinstance(m, MemberField): 571 | if self.is_storage_available(m.field.storage): 572 | self.add_field(m.field) 573 | elif isinstance(m, MemberType): 574 | self.members.append(m) 575 | 576 | def is_storage_available(self, storage: Tuple[int, int]) -> bool: 577 | if storage in self.storage_index: 578 | return False 579 | return True 580 | 581 | def _contains_congruent_member(self, m: Member): 582 | for existing_member in self.members: 583 | if type(existing_member) != type(m): 584 | continue 585 | if isinstance(m, MemberFunction): 586 | assert(isinstance(existing_member, MemberFunction)) 587 | if ( 588 | m.function.name == existing_member.function.name 589 | and m.read_only == existing_member.read_only 590 | ): 591 | return True 592 | elif isinstance(m, MemberConstant): 593 | assert(isinstance(existing_member, MemberConstant)) 594 | raise NotImplementedError 595 | elif isinstance(m, MemberField): 596 | assert(isinstance(existing_member, MemberField)) 597 | if ( 598 | m.field.offset == existing_member.field.offset 599 | and m.field.local_name == existing_member.field.local_name 600 | ): 601 | if m.field.type.is_equivalent(existing_member.field.type): 602 | return True 603 | elif isinstance(m, MemberType): 604 | assert(isinstance(existing_member, MemberType)) 605 | raise NotImplementedError 606 | 607 | return False 608 | 609 | def is_equivalent(self, ty: "Type") -> bool: 610 | if type(self) != type(ty): 611 | return False 612 | assert isinstance(ty, CompositeType) 613 | if self.name != ty.name or self.policy.name != ty.policy.name: 614 | return False 615 | if self.byte_size == ty.byte_size and self.field_count == ty.field_count: 616 | for lhs, rhs in zip(self.fields(), ty.fields()): 617 | if lhs.field.local_name != rhs.field.local_name: 618 | return False 619 | return True 620 | return False 621 | 622 | 623 | class ClassType(CompositeType): 624 | def __init__(self, name: Optional[QualifiedName] = None, byte_size: Optional[int] = None): 625 | super().__init__(CLASS_COMPOSITE_POLICY, name, byte_size) 626 | 627 | def __repr__(self) -> str: 628 | return super().__repr__() 629 | 630 | 631 | class StructType(CompositeType): 632 | def __init__(self, name: Optional[QualifiedName] = None, byte_size: Optional[int] = None): 633 | super().__init__(STRUCT_COMPOSITE_POLICY, name, byte_size) 634 | 635 | def __repr__(self) -> str: 636 | return super().__repr__() 637 | 638 | 639 | class UnionType(CompositeType): 640 | def __init__(self, name: Optional[QualifiedName] = None, byte_size: Optional[int] = None): 641 | super().__init__(UNION_COMPOSITE_POLICY, name, byte_size) 642 | 643 | def __repr__(self) -> str: 644 | return super().__repr__() 645 | 646 | def is_storage_available(self, storage: Tuple[int, int]) -> bool: 647 | return True 648 | 649 | 650 | @dataclass 651 | class Enumerator: 652 | label: str 653 | value: Any 654 | 655 | def __repr__(self): 656 | return f'{self.label} = {str(self.value)}' 657 | 658 | def __str__(self) -> str: 659 | return self.label 660 | 661 | 662 | class EnumType(Type): 663 | def __init__(self, name: Optional[QualifiedName], byte_size: int): 664 | super().__init__(name=name, byte_size=byte_size) 665 | self.enumerators: List[Enumerator] = list() 666 | 667 | def __repr__(self): 668 | items = ';\n'.join((e.__repr__() for e in self.enumerators)) 669 | body = '\n ' + items.replace('\n', '\n ') + ';' if items else '' 670 | return f'enum {str(self.name)}\n{{{body}\n}}' 671 | 672 | def __str__(self) -> str: 673 | return f'enum {str(self.name)}' 674 | 675 | def add_enumerator(self, e: Enumerator): 676 | self.enumerators.append(e) 677 | 678 | def is_equivalent(self, ty: Type) -> bool: 679 | if type(self) != type(ty): 680 | return False 681 | assert(isinstance(ty, EnumType)) 682 | return (self.byte_size == ty.byte_size and self.enumerators == ty.enumerators) 683 | 684 | 685 | class FunctionType(Type): 686 | def __init__(self, name: QualifiedName, return_type: Optional[Type]): 687 | super().__init__() 688 | self.parameters: List[Type] = [] 689 | self.return_type = return_type 690 | self.no_return: bool = False 691 | 692 | def __str__(self) -> str: 693 | params = ', '.join([str(p) for p in self.parameters]) 694 | return f'{self.return_type} {str(self.name)}({params})' 695 | 696 | def is_equivalent(self, ty: Type) -> bool: 697 | if type(self) != type(ty): 698 | return False 699 | assert(isinstance(ty, FunctionType)) 700 | if self.return_type is None or ty.return_type is None: 701 | return False 702 | for pi, qi in zip(self.parameters, ty.parameters): 703 | if not pi.is_equivalent(qi): 704 | return False 705 | return (self.byte_size == ty.byte_size and self.return_type.is_equivalent(ty.return_type)) 706 | 707 | 708 | class PointerToMemberType(Type): 709 | def __init__(self, name: QualifiedName, container: CompositeType, target: Union[Type, MemberField]): 710 | super().__init__(name=name) 711 | self.container = container 712 | self.target = target 713 | 714 | @property 715 | def target_type(self) -> Type: 716 | if isinstance(self.target, Type): 717 | return self.target 718 | else: 719 | return self.target.field.type 720 | 721 | 722 | def type_refers_to(ty: Type, target: Type): 723 | if isinstance(ty, ConstType): 724 | return type_refers_to(ty.type, target) 725 | elif isinstance(ty, VolatileType): 726 | return type_refers_to(ty.type, target) 727 | elif isinstance(ty, AliasType): 728 | return type_refers_to(ty.type, target) 729 | elif isinstance(ty, PointerType): 730 | return ty.target_type is target 731 | else: 732 | return False 733 | 734 | 735 | def get_referenced_type(ty: Type): 736 | if isinstance(ty, ConstType): 737 | return get_referenced_type(ty.type) 738 | elif isinstance(ty, VolatileType): 739 | return get_referenced_type(ty.type) 740 | elif isinstance(ty, AliasType): 741 | return get_referenced_type(ty.type) 742 | elif isinstance(ty, PointerType): 743 | return get_referenced_type(ty.target_type) 744 | elif isinstance(ty, CompositeType): 745 | return ty 746 | else: 747 | return None 748 | 749 | 750 | class Parameter(Element): 751 | def __init__(self, function: "Function", name: Optional[str], type: Type): 752 | super().__init__(name=QualifiedName(*function.name, name) if name else None) 753 | self._function = function 754 | self.model = function.model 755 | self.type = type 756 | self.locations: List[Location] = [] 757 | 758 | def __repr__(self) -> str: 759 | return f'{self.local_name}: {str(self.type)}' 760 | 761 | @property 762 | def local_name(self) -> str: 763 | if self.name: 764 | return self.name[-1] 765 | else: 766 | return '' 767 | 768 | @property 769 | def function(self) -> "Function": 770 | return self._function 771 | 772 | @function.setter 773 | def function(self, fn: "Function"): 774 | self._function = fn 775 | self.model = fn.model 776 | 777 | def add_location(self, loc: Location): 778 | self.locations.append(loc) 779 | 780 | 781 | class ReturnValue(Element): 782 | def __init__(self, function: "Function", type: Type): 783 | super().__init__() 784 | self._function = function 785 | self.model = function.model 786 | self.type = type 787 | 788 | @property 789 | def function(self) -> "Function": 790 | return self._function 791 | 792 | @function.setter 793 | def function(self, fn: "Function"): 794 | self._function = fn 795 | self.model = fn.model 796 | 797 | 798 | @dataclass 799 | class VariableStorage: 800 | storage_type: int 801 | storage_id: int 802 | 803 | 804 | class LocalVariable(Element): 805 | def __init__(self, function: "Function", name: str, type: Type, storage=None): 806 | super().__init__(name=QualifiedName(*function.name, name)) 807 | self._function = function 808 | self.model = function.model 809 | self.type = type 810 | self.locations: List[Location] = [] 811 | self.storage: List[VariableStorage] = storage if storage else [] 812 | 813 | @property 814 | def local_name(self) -> str: 815 | if self.name: 816 | return self.name[-1] 817 | else: 818 | return '' 819 | 820 | @property 821 | def function(self) -> "Function": 822 | return self._function 823 | 824 | @function.setter 825 | def function(self, fn: "Function"): 826 | self._function = fn 827 | self.model = fn.model 828 | 829 | def add_location(self, loc: Location): 830 | self.locations.append(loc) 831 | 832 | 833 | class LocalConstant(Element): 834 | def __init__(self, function: "Function", name: str, type: Type, value: Any = None): 835 | super().__init__(name=QualifiedName(*function.name, name)) 836 | self._function = function 837 | self.model = function.model 838 | self.type = type 839 | self.value = value 840 | 841 | @property 842 | def local_name(self) -> str: 843 | if self.name: 844 | return self.name[-1] 845 | else: 846 | return '' 847 | 848 | @property 849 | def function(self) -> "Function": 850 | return self._function 851 | 852 | @function.setter 853 | def function(self, fn: "Function"): 854 | self._function = fn 855 | self.model = fn.model 856 | 857 | 858 | class Function(Element): 859 | def __init__(self, name: Optional[QualifiedName], entry_addr: Optional[int] = None): 860 | super().__init__(name=name) 861 | self.entry_addr = entry_addr 862 | self._parameters: List[Parameter] = list() 863 | self._return_value: Optional[ReturnValue] = None 864 | self.variables: Optional[List[LocalVariable]] = None 865 | self.constants: Optional[List[LocalConstant]] = None 866 | self.inlined_functions: Optional[List["Function"]] = None 867 | self.no_return: bool = False 868 | self.frame_base: Any = None 869 | self.has_definition: bool = True 870 | self.is_inlined: bool = False 871 | self.ranges: Optional[List[Tuple[int, int]]] = None 872 | self.arch: Optional[str] = None 873 | 874 | def __repr__(self) -> str: 875 | if self._return_value is None: 876 | return f'void {str(self.name)}({self._parameters})' 877 | else: 878 | return f'{str(self._return_value.type)} {str(self.name)}({self._parameters})' 879 | 880 | def __str__(self) -> str: 881 | return str(self.name) 882 | 883 | @property 884 | def return_value(self): 885 | return self._return_value 886 | 887 | def set_return_type(self, ty: Type): 888 | if self._return_value is None: 889 | self._return_value = ReturnValue(self, ty) 890 | else: 891 | self._return_value.type = ty 892 | 893 | @property 894 | def variadic(self) -> bool: 895 | return any(map(lambda p: isinstance(p.type, VariadicType), self._parameters)) 896 | 897 | @property 898 | def is_constructor(self) -> bool: 899 | return len(self.name) >= 2 and self.name[-1] == self.name[-2] 900 | 901 | @property 902 | def is_destructor(self) -> bool: 903 | return not self.name.is_anonymous and self.name[-1][0] == '~' 904 | 905 | @property 906 | def lowerbound(self) -> int: 907 | if self.ranges: 908 | return min(map(lambda r: r[0], self.ranges)) 909 | return sys.maxsize 910 | 911 | @property 912 | def upperbound(self) -> int: 913 | if self.ranges: 914 | return max(map(lambda r: r[1], self.ranges)) 915 | return 0 916 | 917 | @property 918 | def parameters(self): 919 | return tuple(self._parameters) 920 | 921 | def append_parameter(self, name: Optional[str], type: Type): 922 | p = Parameter(self, name, type) 923 | self._parameters.append(p) 924 | return p 925 | 926 | def add_variable(self, name: str, ty: Type): 927 | v = LocalVariable(self, name, ty) 928 | if self.variables is None: 929 | self.variables = list() 930 | self.variables.append(v) 931 | return v 932 | 933 | def add_constant(self, name: str, ty: Type, value: Any): 934 | c = LocalConstant(self, name, ty, value) 935 | if self.constants is None: 936 | self.constants = list() 937 | self.constants.append(c) 938 | 939 | def add_inlined_function(self, inlined_fn: "Function"): 940 | if self.inlined_functions is None: 941 | self.inlined_functions = list() 942 | self.inlined_functions.append(inlined_fn) 943 | 944 | def iter_variables(self): 945 | if self.variables is not None: 946 | for v in self.variables: 947 | yield v 948 | 949 | def iter_inlined_function_tree(self): 950 | for child in self.inlined_functions or []: 951 | yield child 952 | yield from child.iter_inlined_function_tree() 953 | -------------------------------------------------------------------------------- /model/locations.py: -------------------------------------------------------------------------------- 1 | # Copyright(c) 2021-2023 Vector 35 Inc 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files(the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and / or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | from enum import Enum 22 | from dataclasses import dataclass 23 | from typing import Union, Tuple 24 | 25 | 26 | class ExprOp(Enum): 27 | CFA = 0 # Canonical frame address 28 | ADD = 1 29 | VAR_FIELD = 2 30 | NE = 3 31 | GE = 4 32 | GT = 5 33 | LE = 6 34 | LT = 7 35 | EQ = 8 36 | AND = 9 37 | OR = 10 38 | MINUS = 11 39 | ASHR = 12 40 | MUL = 13 41 | MOD = 14 42 | SHR = 15 43 | PLUS_IMM = 16 44 | OVER = 17 45 | DIV = 18 46 | NOT = 19 47 | NEG = 20 48 | XOR = 21 49 | DYNAMIC = 0xfffe 50 | UNSUPPORTED = 0xffff 51 | 52 | 53 | class LocationType(Enum): 54 | STATIC_GLOBAL = 0 55 | STATIC_LOCAL = 1 56 | DYNAMIC = 2 57 | UNSUPPORTED = 3 58 | 59 | 60 | LocationExpression = Tuple[Union[int, str, ExprOp], ...] 61 | 62 | 63 | @dataclass(eq=True, frozen=True) 64 | class Location: 65 | begin: int 66 | end: int 67 | type: LocationType 68 | expr: LocationExpression 69 | -------------------------------------------------------------------------------- /model/observer.py: -------------------------------------------------------------------------------- 1 | # Copyright(c) 2020-2023 Vector 35 Inc 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files(the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and / or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | 22 | from typing import List 23 | 24 | 25 | class Observer(object): 26 | pass 27 | 28 | 29 | class Observable(object): 30 | def __init__(self, parent): 31 | self._observable_parent = parent 32 | self._observers: List[Observer] = list() 33 | 34 | def add_observer(self, observer): 35 | self._observers.append(observer) 36 | 37 | def notify(self, event_name, **kwargs): 38 | for observer in self._observers: 39 | method = getattr(observer, f'on_{event_name}', None) 40 | if method: 41 | method(**kwargs) 42 | if self._observable_parent: 43 | self._observable_parent.notify(event_name, **kwargs) 44 | -------------------------------------------------------------------------------- /model/qualified_name.py: -------------------------------------------------------------------------------- 1 | # Copyright(c) 2021-2023 Vector 35 Inc 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files(the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and / or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | 22 | from typing import Union 23 | 24 | 25 | class QualifiedName(tuple): 26 | def __new__(cls, *args): 27 | if len(args) == 1: 28 | if isinstance(args[0], str): 29 | return tuple.__new__(QualifiedName, args[0:1]) 30 | else: 31 | return tuple.__new__(QualifiedName, args[0]) 32 | else: 33 | return tuple.__new__(QualifiedName, args) 34 | 35 | def __bool__(self) -> bool: 36 | return len(self) > 0 37 | 38 | def __repr__(self) -> str: 39 | return str(self) 40 | 41 | def __str__(self) -> str: 42 | return '::'.join(self) 43 | 44 | def concat(self, rhs: Union[str, "QualifiedName"]) -> "QualifiedName": 45 | if isinstance(rhs, str): 46 | return QualifiedName(*self, rhs) 47 | else: 48 | return QualifiedName(*self, *rhs) 49 | 50 | @property 51 | def is_anonymous(self) -> bool: 52 | return len(self) == 0 or self[-1] == '' 53 | 54 | @property 55 | def is_empty(self) -> bool: 56 | return len(self) == 0 57 | 58 | @property 59 | def parent(self) -> "QualifiedName": 60 | if len(self) > 1: 61 | return QualifiedName(*self[:-1]) 62 | else: 63 | return QualifiedName() 64 | 65 | @property 66 | def local_name(self) -> str: 67 | if len(self) > 0: 68 | return self[-1] 69 | else: 70 | return '' 71 | -------------------------------------------------------------------------------- /model/resource_schema.py: -------------------------------------------------------------------------------- 1 | # Copyright(c) 2021-2023 Vector 35 Inc 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files(the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and / or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | from typing import List 22 | from enum import Enum 23 | from .attributes import Attribute 24 | from .concrete_elements import Element 25 | 26 | 27 | class ResourceType(Enum): 28 | HEAP_MEMORY = 0 29 | FILE_OBJECT = 1 30 | FILE_HANDLE = 2 31 | NETWORK_SOCKET = 3 32 | 33 | 34 | class ResourceSchema(object): 35 | acquires = Attribute('acquires', ResourceType) 36 | releases = Attribute('releases', ResourceType) 37 | escapes = Attribute('escapes', bool) 38 | released_at = Attribute('released_at', List[Element]) 39 | acquired_at = Attribute('acquired_at', List[Element]) 40 | imported = Attribute('imported', bool) 41 | objects = Attribute('objects', List[Element]) 42 | -------------------------------------------------------------------------------- /model/touch_flags.py: -------------------------------------------------------------------------------- 1 | # Copyright(c) 2020-2023 Vector 35 Inc 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files(the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and / or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | from enum import Enum, auto 22 | 23 | 24 | class TouchFlags(Enum): 25 | TOUCHED_FUNCTION_SET = auto() 26 | TOUCHED_NAMES = auto() 27 | TOUCHED_IMPORTS = auto() 28 | TOUCHED_VARIABLES = auto() 29 | TOUCHED_CONSTANTS = auto() 30 | TOUCHED_TYPES = auto() 31 | TOUCHED_TAGS = auto() 32 | TOUCHED_FUNCTION_TYPE = auto() 33 | -------------------------------------------------------------------------------- /plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "pluginmetadataversion": 2, 3 | "name": "DWARF Import", 4 | "type": [ 5 | "core" 6 | ], 7 | "api": [ 8 | "python3" 9 | ], 10 | "description": "Imports DWARF Info from ELFs", 11 | "longdescription": "This plugin is deprecated. Please use our new DWARF Import plugin, which is shipped with the product by default: https://github.com/Vector35/binaryninja-api/issues/3206#issuecomment-1629342753", 12 | "license": { 13 | "name": "MIT", 14 | "text": "Copyright (c) 2020-2023 Vector 35 Inc\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." 15 | }, 16 | "platforms": [ 17 | "Darwin", 18 | "Linux", 19 | "Windows" 20 | ], 21 | "installinstructions": { 22 | "Darwin": null, 23 | "Linux": null, 24 | "Windows": null 25 | }, 26 | "dependencies": { 27 | "pip": [ 28 | "pyelftools==0.27" 29 | ] 30 | }, 31 | "version": "1.2.1", 32 | "author": "Vector 35 Inc", 33 | "minimumbinaryninjaversion": 3000 34 | } -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pyelftools==0.27 2 | --------------------------------------------------------------------------------