# Copyright (C) 1999-2023 Pavel Tomin. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. '''This module contains classes which provides decompiling program functionality. WARNING: This is a part of internal API. Do not import it directly to your scripts. ''' def reko_address(reko, addr): if isinstance(addr, tuple): addr = ":".join(map('0x{:X}'.format, addr)) return reko.Address(addr) def addr_from_str(s_addr): pos = s_addr.find(":") if pos >= 0: return (int(s_addr[:pos], 16), int(s_addr[pos + 1:], 16)) return int(s_addr, 16) def addr_list_from_str_list(s_addr_list): return (addr_from_str(s_addr) for s_addr in s_addr_list) def get_memory_length(start_addr, end_addr): if isinstance(start_addr, tuple) and isinstance(end_addr, tuple): start_seg, start_offset = start_addr end_seg, end_offset = end_addr if start_seg != end_seg: raise Exception("Can't work with different memory segments.") return end_offset - start_offset return end_addr - start_addr class RekoDictBase: __slots__ = [] def __getitem__(self, key): raise KeyError def get(self, key, default=None): 'D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None.' try: return self[key] except KeyError: return default def __contains__(self, key): try: self[key] except KeyError: return False else: return True def items(self): for key in self: yield (key, self[key]) class Comments: __slots__ = '_reko' def __init__(self, reko): self._reko = reko def __setitem__(self, addr, comment): addr = reko_address(self._reko, addr) if not isinstance(comment, str): raise TypeError( 'Unsupported type: {}. Expected type: str'.format( type(comment))) self._reko.SetUserComment(addr, comment) class MemorySlice: __slots__ = '_reko', '_start_addr', '_length' def __init__(self, reko, start_addr, length): self._reko = reko self._start_addr = start_addr self._length = length @property def byte(self): if self._length is None: return self._reko.ReadByte(self._start_addr) return self._reko.ReadBytes(self._start_addr, self._length) @property def int16(self): if self._length is None: return self._reko.ReadInt16(self._start_addr) return self._reko.ReadInts16(self._start_addr, self._length) @property def int32(self): if self._length is None: return self._reko.ReadInt32(self._start_addr) return self._reko.ReadInts32(self._start_addr, self._length) @property def int64(self): if self._length is None: return self._reko.ReadInt64(self._start_addr) return self._reko.ReadInts64(self._start_addr, self._length) @property def c_str(self): return self._reko.ReadCString(self._start_addr) class Memory: __slots__ = '_reko' def __init__(self, reko): self._reko = reko def __getitem__(self, key): if isinstance(key, slice): if key.step is not None: raise ValueError('Step is not supported') if key.start is None: raise ValueError('Start address is required') if key.stop is None: raise ValueError('End address is required') start_addr = key.start length = get_memory_length(key.start, key.stop) else: start_addr = key length = None start_addr = reko_address(self._reko, start_addr) return MemorySlice(self._reko, start_addr, length) class Globals: __slots__ = '_reko' def __init__(self, reko): self._reko = reko def __setitem__(self, addr, decl): addr = reko_address(self._reko, addr) if isinstance(decl, str): self._reko.SetUserGlobal(addr, decl) return raise TypeError('Unsupported type: {}'.format(type(decl))) class Procedure: __slots__ = '_reko', '_addr' def __init__(self, reko, addr): self._reko = reko self._addr = addr @property def decompile(self): return self._reko.GetProcedureDecompileFlag(self._addr) @decompile.setter def decompile(self, value): self._reko.SetUserProcedureDecompileFlag(self._addr, value) @property def name(self): return self._reko.GetProcedureName(self._addr) @property def file(self): return self._reko.GetProcedureOutputFile(self._addr) @file.setter def file(self, value): self._reko.SetUserProcedureOutputFile(self._addr, value) class Procedures(RekoDictBase): __slots__ = '_reko' def __init__(self, reko): self._reko = reko def __getitem__(self, addr): try: addr = reko_address(self._reko, addr) except Exception: raise KeyError(addr) if not self._reko.ContainsProcedureAddress(addr): raise KeyError(addr) return Procedure(self._reko, addr) def __setitem__(self, addr, decl): addr = reko_address(self._reko, addr) if isinstance(decl, str): self._reko.SetUserProcedure(addr, decl) return raise TypeError('Unsupported type: {}'.format(type(decl))) def __iter__(self): return addr_list_from_str_list(self._reko.GetProcedureAddresses()) class Program: __slots__ = '_comments', '_memory', '_globals', '_procedures' def __init__(self, reko): self._comments = Comments(reko) self._memory = Memory(reko) self._globals = Globals(reko) self._procedures = Procedures(reko) @property def comments(self): return self._comments @property def memory(self): return self._memory @property def globals(self): return self._globals @property def procedures(self): return self._procedures