diff options
| author | Theofilos Augoustis <theofilos.augoustis@gmail.com> | 2024-07-12 11:52:00 +0200 |
|---|---|---|
| committer | Theofilos Augoustis <theofilos.augoustis@gmail.com> | 2024-07-12 11:52:00 +0200 |
| commit | 243aaa08afd66f1b409774693b716e30fa9ffacc (patch) | |
| tree | b81dc00bf3b2dd10ba8a239672cf30a9d3360507 /tools/_qemu_tool.py | |
| parent | ef31d11c7bb0ec6505622ea61f963c56ddf79672 (diff) | |
| download | focaccia-243aaa08afd66f1b409774693b716e30fa9ffacc.tar.gz focaccia-243aaa08afd66f1b409774693b716e30fa9ffacc.zip | |
Add support for aarch64
- Implement an architecture description for aarch64 - Add endianness information to the `Arch` class. - Move conversion from flags register to logical flag values from the calling code to the concrete targets (LLDB and GDB), which is the only point where we (have to) deal in flags registers. - Handle assembly/disassembly errors in serialization of SymbolicTransform - Move ProgramState's `arch` attribute into ReadableProgramState. Co-authored-by: Theofilos Augoustis <theofilos.augoustis@gmail.com> Co-authored-by: Nicola Crivellin <nicola.crivellin98@gmail.com>
Diffstat (limited to '')
| -rw-r--r-- | tools/_qemu_tool.py | 81 |
1 files changed, 58 insertions, 23 deletions
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 |