diff options
| -rw-r--r-- | README.md | 21 | ||||
| -rw-r--r-- | focaccia/arch/__init__.py | 11 | ||||
| -rw-r--r-- | focaccia/arch/aarch64.py | 55 | ||||
| -rw-r--r-- | focaccia/arch/arch.py | 8 | ||||
| -rw-r--r-- | focaccia/lldb_target.py | 85 | ||||
| -rw-r--r-- | focaccia/miasm_util.py | 55 | ||||
| -rw-r--r-- | focaccia/snapshot.py | 6 | ||||
| -rw-r--r-- | focaccia/symbolic.py | 47 | ||||
| -rw-r--r-- | nix.shell | 12 | ||||
| -rw-r--r-- | tools/_qemu_tool.py | 81 |
10 files changed, 293 insertions, 88 deletions
diff --git a/README.md b/README.md index 5c14d91..67db62c 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ The `tools/` directory contains additional utility scripts to work with focaccia ## Project Overview (for developers) -### Snapshot-comparison framework +### Snapshots and comparison The following files belong to a rough framework for the snapshot comparison engine: @@ -43,8 +43,7 @@ representation of program snapshots. - `focaccia/compare.py`: The central algorithms that work on snapshots. - - `focaccia/arch/`: Abstractions over different processor architectures. Will be used to integrate support for more -architectures later. Currently, we only have X86. + - `focaccia/arch/`: Abstractions over different processor architectures. Currently we have x86 and aarch64. ### Concolic execution @@ -67,4 +66,18 @@ our own log format. - `focaccia/match.py`: Algorithms for trace matching. - - `miasm_test.py`: A test script that traces a program concolically. +### Supporting new architectures + +To add support for an architecture <arch>, do the following: + + - Add a file `focaccia/arch/<arch>.py`. This module declares the architecture's description, such as register names and +an architecture class. The convention is to declare state flags (e.g. flags in RFLAGS for x86) as separate registers. + + - Add the class to the `supported_architectures` dict in `focaccia/arch/__init__.py`. + + - Depending on Miasm's support for <arch>, add register name aliases to the `MiasmSymbolResolver.miasm_flag_aliases` +dict in `focaccia/miasm_util.py`. + + - Depending on the existence of a flags register in <arch>, implement conversion from the flags register's value to +values of single logical flags (e.g. implement the operation `RFLAGS['OF']`) in the respective concrete targets (LLDB, +GDB, ...). diff --git a/focaccia/arch/__init__.py b/focaccia/arch/__init__.py index 2926d20..1797176 100644 --- a/focaccia/arch/__init__.py +++ b/focaccia/arch/__init__.py @@ -1,11 +1,14 @@ from .arch import Arch -from . import x86 +from . import x86, aarch64 +supported_architectures: dict[str, Arch] = { + 'x86_64': x86.ArchX86(), + 'aarch64': aarch64.ArchAArch64('little'), + 'aarch64l': aarch64.ArchAArch64('little'), + 'aarch64b': aarch64.ArchAArch64('big'), +} """A dictionary containing all supported architectures at their names. The arch names (keys) should be compatible with the string returned from `platform.machine()`. """ -supported_architectures: dict[str, Arch] = { - "x86_64": x86.ArchX86(), -} diff --git a/focaccia/arch/aarch64.py b/focaccia/arch/aarch64.py new file mode 100644 index 0000000..c0efc1a --- /dev/null +++ b/focaccia/arch/aarch64.py @@ -0,0 +1,55 @@ +"""Description of 64-bit ARM.""" +from typing import Literal + +from .arch import Arch + +archname = 'aarch64' + +regnames = [ + 'PC', 'SP', 'LR', + 'CPSR', + + 'X0', 'X1', 'X2', 'X3', 'X4', 'X5', 'X6', 'X7', 'X8', 'X9', + 'X10', 'X11', 'X12', 'X13', 'X14', 'X15', 'X16', 'X17', 'X18', 'X19', + 'X20', 'X21', 'X22', 'X23', 'X24', 'X25', 'X26', 'X27', 'X28', 'X29', + 'X30', + + 'Q0', 'Q1', 'Q2', 'Q3', 'Q4', 'Q5', 'Q6', 'Q7', 'Q8', 'Q9', + 'Q10', 'Q11', 'Q12', 'Q13', 'Q14', 'Q15', + + 'V0', 'V1', 'V2', 'V3', 'V4', 'V5', 'V6', 'V7', 'V8', 'V9', + 'V10', 'V11', 'V12', 'V13', 'V14', 'V15', 'V16', 'V17', 'V18', 'V19', + 'V20', 'V21', 'V22', 'V23', 'V24', 'V25', 'V26', 'V27', 'V28', 'V29', + 'V30', 'V31', + + 'N', 'Z', 'C', 'V', 'Q', + 'SSBS', 'PAN', 'DIT', 'GE', + 'E', 'A', 'I', 'F', 'M', +] + +def decompose_cpsr(cpsr: int) -> dict[str, int]: + """Extract individual flag values from the CPSR register.""" + return { + 'N': (cpsr & (1 << 31)) != 0, + 'Z': (cpsr & (1 << 30)) != 0, + 'C': (cpsr & (1 << 29)) != 0, + 'V': (cpsr & (1 << 28)) != 0, + 'Q': (cpsr & (1 << 27)) != 0, + # Reserved: [26:24] + 'SSBS': (cpsr & (1 << 23)) != 0, + 'PAN': (cpsr & (1 << 22)) != 0, + 'DIT': (cpsr & (1 << 21)) != 0, + # Reserved: [20] + 'GE': (cpsr & (0b1111 << 16)) != 0, + # Reserved: [15:10] + 'E': (cpsr & (1 << 9)) != 0, + 'A': (cpsr & (1 << 8)) != 0, + 'I': (cpsr & (1 << 7)) != 0, + 'F': (cpsr & (1 << 6)) != 0, + # Reserved: [5:4] + 'M': (cpsr & 0b1111) != 0, + } + +class ArchAArch64(Arch): + def __init__(self, endianness: Arch.Endianness): + super().__init__(archname, regnames, 64, endianness) diff --git a/focaccia/arch/arch.py b/focaccia/arch/arch.py index e64f1fa..dee7288 100644 --- a/focaccia/arch/arch.py +++ b/focaccia/arch/arch.py @@ -1,13 +1,17 @@ -from typing import Iterable +from typing import Iterable, Literal class Arch(): + Endianness = Literal['little', 'big'] + def __init__(self, archname: str, regnames: Iterable[str], - ptr_size: int): + ptr_size: int, + endianness: Endianness = 'little'): self.archname = archname self.regnames = set(name.upper() for name in regnames) self.ptr_size = ptr_size + self.endianness: Literal['little', 'big'] = endianness def to_regname(self, name: str) -> str | None: """Transform a string into a standard register name. diff --git a/focaccia/lldb_target.py b/focaccia/lldb_target.py index a4e96a8..2ed0757 100644 --- a/focaccia/lldb_target.py +++ b/focaccia/lldb_target.py @@ -2,7 +2,7 @@ import os import lldb -from .arch import supported_architectures, x86 +from .arch import supported_architectures from .snapshot import ProgramState class MemoryMap: @@ -32,6 +32,18 @@ class ConcreteSectionError(Exception): pass class LLDBConcreteTarget: + from focaccia.arch import aarch64, x86 + + flag_register_names = { + aarch64.archname: 'cpsr', + x86.archname: 'rflags', + } + + flag_register_decompose = { + aarch64.archname: aarch64.decompose_cpsr, + x86.archname: x86.decompose_rflags, + } + def __init__(self, executable: str, argv: list[str] = [], @@ -67,7 +79,14 @@ class LLDBConcreteTarget: raise RuntimeError(f'[In LLDBConcreteTarget.__init__]: Failed to' f' launch process.') + # Determine current arch self.archname = self.target.GetPlatform().GetTriple().split('-')[0] + if self.archname not in supported_architectures: + err = f'LLDBConcreteTarget: Architecture {self.archname} is not' \ + f' supported by Focaccia.' + print(f'[ERROR] {err}') + raise NotImplementedError(err) + self.arch = supported_architectures[self.archname] def is_exited(self): """Signals whether the concrete process has exited. @@ -99,28 +118,17 @@ class LLDBConcreteTarget: def record_snapshot(self) -> ProgramState: """Record the concrete target's state in a ProgramState object.""" - # Determine current arch - if self.archname not in supported_architectures: - print(f'[ERROR] LLDBConcreteTarget: Recording snapshots is not' - f' supported for architecture {self.archname}!') - raise NotImplementedError() - arch = supported_architectures[self.archname] - - state = ProgramState(arch) + state = ProgramState(self.arch) # Query and store register state - for regname in arch.regnames: + for regname in self.arch.regnames: try: conc_val = self.read_register(regname) state.set_register(regname, conc_val) except KeyError: pass except ConcreteRegisterError: - # Special rule for flags on X86 - if arch.archname == x86.archname: - rflags = x86.decompose_rflags(self.read_register('rflags')) - if regname in rflags: - state.set_register(regname, rflags[regname]) + pass # Query and store memory state for mapping in self.get_mappings(): @@ -142,12 +150,35 @@ class LLDBConcreteTarget: """ frame = self.process.GetThreadAtIndex(0).GetFrameAtIndex(0) reg = frame.FindRegister(regname) - if reg is None: + if not reg.IsValid(): raise ConcreteRegisterError( f'[In LLDBConcreteTarget._get_register]: Register {regname}' f' not found.') return reg + def read_flags(self) -> dict[str, int | bool]: + """Read the current state flags. + + If the concrete target's architecture has state flags, read and return + their current values. + + This handles the conversion from implementation details like flags + registers to the logical flag values. For example: On X86, this reads + the RFLAGS register and extracts the flag bits from its value. + + :return: Dictionary mapping flag names to values. The values may be + booleans in the case of true binary flags or integers in the + case of multi-byte flags. Is empty if the current architecture + does not have state flags of the access is not implemented for + it. + """ + if self.archname not in self.flag_register_names: + return {} + + flags_reg = self.flag_register_names[self.archname] + flags_val = self._get_register(flags_reg).GetValueAsUnsigned() + return self.flag_register_decompose[self.archname](flags_val) + def read_register(self, regname: str) -> int: """Read the value of a register. @@ -155,14 +186,17 @@ class LLDBConcreteTarget: or the target is otherwise unable to read the register's value. """ - reg = self._get_register(regname) - val = reg.GetValue() - if val is None: + try: + reg = self._get_register(regname) + assert(reg.IsValid()) + return reg.GetValueAsUnsigned() + except ConcreteRegisterError as err: + flags = self.read_flags() + if regname in flags: + return flags[regname] raise ConcreteRegisterError( - f'[In LLDBConcreteTarget.read_register]: Register has an' - f' invalid value of {val}.') - - return int(val, 16) + f'[In LLDBConcreteTarget.read_register]: Unable to read' + f' register {regname}: {err}') def write_register(self, regname: str, value: int): """Read the value of a register. @@ -189,7 +223,10 @@ class LLDBConcreteTarget: if not err.success: raise ConcreteMemoryError(f'Error when reading {size} bytes at' f' address {hex(addr)}: {err}') - return bytes(reversed(content)) # Convert to big endian + if self.arch.endianness == 'little': + return content + else: + return bytes(reversed(content)) def write_memory(self, addr, value: bytes): """Write bytes to memory. diff --git a/focaccia/miasm_util.py b/focaccia/miasm_util.py index 83a8778..a2cd025 100644 --- a/focaccia/miasm_util.py +++ b/focaccia/miasm_util.py @@ -1,14 +1,26 @@ from typing import Callable +from miasm.analysis.machine import Machine from miasm.core.locationdb import LocationDB, LocKey from miasm.expression.expression import Expr, ExprOp, ExprId, ExprLoc, \ ExprInt, ExprMem, ExprCompose, \ ExprSlice, ExprCond from miasm.expression.simplifications import expr_simp_explicit +from . import arch +from .arch import Arch from .snapshot import ReadableProgramState, \ RegisterAccessError, MemoryAccessError +def make_machine(_arch: Arch) -> Machine: + """Create a Miasm `Machine` object corresponding to an `Arch`.""" + machines = { + arch.x86.archname: lambda _: Machine('x86_64'), + # Miasm only has ARM machine names with the l/b suffix: + arch.aarch64.archname: lambda a: Machine(f'aarch64{a.endianness[0]}'), + } + return machines[_arch.archname](_arch) + def simp_segm(expr_simp, expr: ExprOp): """Simplify a segmentation expression to an addition of the segment register's base value and the address argument. @@ -60,27 +72,50 @@ class MiasmSymbolResolver: """Resolves atomic symbols to some state.""" miasm_flag_aliases = { - 'NF': 'SF', - 'I_F': 'IF', - 'IOPL_F': 'IOPL', - 'I_D': 'ID', + arch.x86.archname: { + 'NF': 'SF', + 'I_F': 'IF', + 'IOPL_F': 'IOPL', + 'I_D': 'ID', + }, + arch.aarch64.archname: { + 'NF': 'N', + 'SF': 'N', + 'ZF': 'Z', + 'CF': 'C', + 'VF': 'V', + 'OF': 'V', + 'QF': 'Q', + + 'AF': 'A', + 'EF': 'E', + 'IF': 'I', + 'FF': 'F', + } } - def __init__(self, state: ReadableProgramState, loc_db: LocationDB): + def __init__(self, + state: ReadableProgramState, + loc_db: LocationDB): self._state = state self._loc_db = loc_db + self._arch = state.arch + self.endianness: Arch.Endianness = self._arch.endianness - @staticmethod - def _miasm_to_regname(regname: str) -> str: + def _miasm_to_regname(self, regname: str) -> str: """Convert a register name as used by Miasm to one that follows Focaccia's naming conventions.""" regname = regname.upper() - return MiasmSymbolResolver.miasm_flag_aliases.get(regname, regname) + if self._arch.archname in self.miasm_flag_aliases: + aliases = self.miasm_flag_aliases[self._arch.archname] + return aliases.get(regname, regname) + return regname def resolve_register(self, regname: str) -> int | None: try: return self._state.read_register(self._miasm_to_regname(regname)) - except RegisterAccessError: + except RegisterAccessError as err: + print(f'Not a register: {regname} ({err})') return None def resolve_memory(self, addr: int, size: int) -> bytes | None: @@ -164,7 +199,7 @@ def _eval_exprmem(expr: ExprMem, state: MiasmSymbolResolver): return expr assert(len(mem) * 8 == expr.size) - return ExprInt(int.from_bytes(mem, byteorder='big'), expr.size) + return ExprInt(int.from_bytes(mem, byteorder=state.endianness), expr.size) def _eval_exprcond(expr, state: MiasmSymbolResolver): """Evaluate an ExprCond using the current state""" diff --git a/focaccia/snapshot.py b/focaccia/snapshot.py index 104ca0a..506cf27 100644 --- a/focaccia/snapshot.py +++ b/focaccia/snapshot.py @@ -80,7 +80,9 @@ class SparseMemory: assert(len(data) == offset) # Exactly all data was written class ReadableProgramState: - """Interface for read-only program states. Used for typing purposes.""" + """Interface for read-only program states.""" + def __init__(self, arch: Arch): + self.arch = arch def read_register(self, reg: str) -> int: """Read a register's value. @@ -106,7 +108,7 @@ class ReadableProgramState: class ProgramState(ReadableProgramState): """A snapshot of the program's state.""" def __init__(self, arch: Arch): - self.arch = arch + super().__init__(arch=arch) self.regs: dict[str, int | None] = {reg: None for reg in arch.regnames} self.mem = SparseMemory() diff --git a/focaccia/symbolic.py b/focaccia/symbolic.py index dfc2966..d5475a6 100644 --- a/focaccia/symbolic.py +++ b/focaccia/symbolic.py @@ -17,7 +17,7 @@ from .arch import Arch, supported_architectures from .lldb_target import LLDBConcreteTarget, \ ConcreteRegisterError, \ ConcreteMemoryError -from .miasm_util import MiasmSymbolResolver, eval_expr +from .miasm_util import MiasmSymbolResolver, eval_expr, make_machine from .snapshot import ProgramState, ReadableProgramState, \ RegisterAccessError, MemoryAccessError from .trace import Trace, TraceEnvironment @@ -80,7 +80,7 @@ class Instruction: @staticmethod def from_bytecode(asm: bytes, arch: Arch) -> Instruction: """Disassemble an instruction.""" - machine = Machine(arch.archname) + machine = make_machine(arch) assert(machine.mn is not None) _instr = machine.mn.dis(asm, arch.ptr_size) return Instruction(_instr, machine, arch, None) @@ -244,14 +244,15 @@ class SymbolicTransform: accessed_regs = set[str]() class RegisterCollector(MiasmSymbolResolver): - def __init__(self): pass + def __init__(self, arch: Arch): + self._arch = arch # MiasmSymbolResolver needs this def resolve_register(self, regname: str) -> int | None: accessed_regs.add(self._miasm_to_regname(regname)) return None def resolve_memory(self, addr: int, size: int): pass def resolve_location(self, loc): assert(False) - resolver = RegisterCollector() + resolver = RegisterCollector(self.arch) for expr in self.changed_regs.values(): eval_expr(expr, resolver) for addr_expr, mem_expr in self.changed_mem.items(): @@ -340,7 +341,8 @@ class SymbolicTransform: for addr, expr in self.changed_mem.items(): addr = eval_symbol(addr, conc_state) length = int(expr.size / 8) - res[addr] = eval_symbol(expr, conc_state).to_bytes(length, byteorder='big') + res[addr] = eval_symbol(expr, conc_state) \ + .to_bytes(length, byteorder=self.arch.endianness) return res @classmethod @@ -352,8 +354,13 @@ class SymbolicTransform: from miasm.expression.parser import str_to_expr as parse def decode_inst(obj: Iterable[int], arch: Arch): - b = b''.join(i.to_bytes(1, byteorder='big') for i in obj) - return Instruction.from_bytecode(b, arch) + b = b''.join(i.to_bytes(1) for i in obj) + try: + return Instruction.from_bytecode(b, arch) + except Exception as err: + warn(f'[In SymbolicTransform.from_json] Unable to disassemble' + f' bytes {obj}: {err}.') + return None arch = supported_architectures[data['arch']] start_addr = int(data['from_addr']) @@ -362,7 +369,8 @@ class SymbolicTransform: t = SymbolicTransform({}, [], arch, start_addr, end_addr) t.changed_regs = { name: parse(val) for name, val in data['regs'].items() } t.changed_mem = { parse(addr): parse(val) for addr, val in data['mem'].items() } - t.instructions = [decode_inst(b, arch) for b in data['instructions']] + instrs = [decode_inst(b, arch) for b in data['instructions']] + t.instructions = [inst for inst in instrs if inst is not None] # Recover the instructions' address information addr = t.addr @@ -375,13 +383,21 @@ class SymbolicTransform: def to_json(self) -> dict: """Serialize a symbolic transformation as a JSON object.""" def encode_inst(inst: Instruction): - return [int(b) for b in inst.to_bytecode()] + try: + return [int(b) for b in inst.to_bytecode()] + except Exception as err: + warn(f'[In SymbolicTransform.to_json] Unable to assemble' + f' "{inst}" to bytecode: {err}. This instruction will not' + f' be serialized.') + return None + instrs = [encode_inst(inst) for inst in self.instructions] + instrs = [inst for inst in instrs if inst is not None] return { 'arch': self.arch.archname, 'from_addr': self.range[0], 'to_addr': self.range[1], - 'instructions': [encode_inst(inst) for inst in self.instructions], + 'instructions': instrs, 'regs': { name: repr(expr) for name, expr in self.changed_regs.items() }, 'mem': { repr(addr): repr(val) for addr, val in self.changed_mem.items() }, } @@ -554,24 +570,17 @@ class _LLDBConcreteState(ReadableProgramState): allows us instead to read values from LLDB on demand. """ def __init__(self, target: LLDBConcreteTarget, arch: Arch): + super().__init__(arch) self._target = target - self._arch = arch def read_register(self, reg: str) -> int: - from focaccia.arch import x86 - - regname = self._arch.to_regname(reg) + regname = self.arch.to_regname(reg) if regname is None: raise RegisterAccessError(reg, f'Not a register name: {reg}') try: return self._target.read_register(regname) except ConcreteRegisterError: - # Special case for X86 - if self._arch.archname == x86.archname: - rflags = x86.decompose_rflags(self._target.read_register('rflags')) - if regname in rflags: - return rflags[regname] raise RegisterAccessError(regname, '') def read_memory(self, addr: int, size: int) -> bytes: diff --git a/nix.shell b/nix.shell new file mode 100644 index 0000000..00fef51 --- /dev/null +++ b/nix.shell @@ -0,0 +1,12 @@ +{ pkgs ? import <nixpkgs> {} }: +pkgs.mkShell { + nativeBuildInputs = with pkgs; [ + python311 + python311Packages.pip + virtualenv + + gcc gnumake binutils cmake ninja pkg-config + musl qemu swig4 + gdb + ]; +} diff --git a/tools/_qemu_tool.py b/tools/_qemu_tool.py index 38715dc..e3341ad 100644 --- a/tools/_qemu_tool.py +++ b/tools/_qemu_tool.py @@ -10,51 +10,86 @@ import gdb from typing import Iterable import focaccia.parser as parser -from focaccia.arch import supported_architectures +from focaccia.arch import supported_architectures, Arch from focaccia.compare import compare_symbolic from focaccia.snapshot import ProgramState, ReadableProgramState, \ RegisterAccessError, MemoryAccessError from focaccia.symbolic import SymbolicTransform, eval_symbol, ExprMem from focaccia.trace import Trace, TraceEnvironment -from focaccia.utils import get_envp, print_result +from focaccia.utils import print_result from verify_qemu import make_argparser, verbosity class GDBProgramState(ReadableProgramState): - def __init__(self, process: gdb.Inferior, frame: gdb.Frame): + from focaccia.arch import aarch64, x86 + + flag_register_names = { + aarch64.archname: 'cpsr', + x86.archname: 'eflags', + } + + flag_register_decompose = { + aarch64.archname: aarch64.decompose_cpsr, + x86.archname: x86.decompose_rflags, + } + + def __init__(self, process: gdb.Inferior, frame: gdb.Frame, arch: Arch): + super().__init__(arch) self._proc = process self._frame = frame + @staticmethod + def _read_vector_reg_aarch64(val, size) -> int: + return int(str(val['u']), 10) + + @staticmethod + def _read_vector_reg_x86(val, size) -> int: + num_longs = size // 64 + vals = val[f'v{num_longs}_int64'] + res = 0 + for i in range(num_longs): + val = int(vals[i].cast(gdb.lookup_type('unsigned long'))) + res += val << i * 64 + return res + + read_vector_reg = { + aarch64.archname: _read_vector_reg_aarch64, + x86.archname: _read_vector_reg_x86, + } + def read_register(self, reg: str) -> int: try: val = self._frame.read_register(reg.lower()) size = val.type.sizeof * 8 - # For vector registers, we have to assemble Python's - # arbitrary-length integers from GDB's fixed-size integers - # ourselves: + # For vector registers, we need to apply architecture-specific + # logic because GDB's interface is not consistent. if size > 64: # Value is a vector - num_longs = size // 64 - vals = val[f'v{num_longs}_int64'] - res = 0 - for i in range(num_longs): - val = int(vals[i].cast(gdb.lookup_type('unsigned long'))) - res += val << i * 64 - return res + if self.arch.archname not in self.read_vector_reg: + raise NotImplementedError( + f'Reading vector registers is not implemented for' + f' architecture {self.arch.archname}.') + return self.read_vector_reg[self.arch.archname](val, size) # For non-vector values, just return the 64-bit value return int(val.cast(gdb.lookup_type('unsigned long'))) except ValueError as err: - from focaccia.arch import x86 - rflags = int(self._frame.read_register('eflags')) - rflags = x86.decompose_rflags(rflags) - if reg in rflags: - return rflags[reg] - raise RegisterAccessError(reg, str(err)) + # Try to access the flags register with `reg` as a logical flag name + if self.arch.archname in self.flag_register_names: + flags_reg = self.flag_register_names[self.arch.archname] + flags = int(self._frame.read_register(flags_reg)) + flags = self.flag_register_decompose[self.arch.archname](flags) + if reg in flags: + return flags[reg] + raise RegisterAccessError(reg, + f'[GDB] Unable to access {reg}: {err}') def read_memory(self, addr: int, size: int) -> bytes: try: mem = self._proc.read_memory(addr, size).tobytes() - return bytes(reversed(mem)) # Convert to big endian + if self.arch.endianness == 'little': + return mem + else: + return bytes(reversed(mem)) # Convert to big endian except gdb.MemoryError as err: raise MemoryAccessError(addr, size, str(err)) @@ -87,7 +122,7 @@ class GDBServerStateIterator: # i.e. before stepping the first time if self._first_next: self._first_next = False - return GDBProgramState(self._process, gdb.selected_frame()) + return GDBProgramState(self._process, gdb.selected_frame(), self.arch) # Step pc = gdb.selected_frame().read_register('pc') @@ -98,7 +133,7 @@ class GDBServerStateIterator: raise StopIteration new_pc = gdb.selected_frame().read_register('pc') - return GDBProgramState(self._process, gdb.selected_frame()) + return GDBProgramState(self._process, gdb.selected_frame(), self.arch) def record_minimal_snapshot(prev_state: ReadableProgramState, cur_state: ReadableProgramState, @@ -140,8 +175,8 @@ def record_minimal_snapshot(prev_state: ReadableProgramState, resolved relative to this state. """ for regname in regs: - regval = cur_state.read_register(regname) try: + regval = cur_state.read_register(regname) out_state.set_register(regname, regval) except RegisterAccessError: pass |