fxos: refactor AbstractMemory into <fxos/vspace.h>

This commit is contained in:
Lephenixnoir 2023-09-16 11:49:56 +02:00
parent 56a4800bbd
commit c0820b59f0
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
7 changed files with 170 additions and 214 deletions

View File

@ -35,7 +35,6 @@ flex_target(LoadAsm lib/load-asm.l
"${CMAKE_CURRENT_BINARY_DIR}/load-asm.yy.cpp" COMPILE_FLAGS -s)
set(fxos_core_SOURCES
lib/AbstractMemory.cpp
lib/disassembly.cpp
lib/lang.cpp
lib/memory.cpp

View File

@ -1,22 +1,18 @@
# Format: [01nmdi]{16}, followed by the mnemonic and the list of arguments.
# In each opcode, there should be at most one sequence of "m", "n", "d" and "i"
# each (representing the location of the argument).
# SuperH instruction table. Each line is of the form
#
# Possible argument strings are predefined and include:
# rn rm #imm
# jump8 jump12 disp pcdisp
# @rn @rm @rn+ @rm+ @-rn
# @(disp,rn) @(disp,rm) @(r0,rn) @(r0,rm) @(disp,gbr)
# [01nmdi]{16} [a-zA-Z0-9./]+ {arg|reg}* {tag}*
# (opcode) (mnemonic) (arguments) (tags)
#
# The disassembler substitutes some elements as follows:
# rn -> value of the "n"-sequence
# rm -> value of the "m"-sequence
# #imm -> value of the "i"-sequence
# disp -> value of the "d"-sequence
# jump8 -> value of the 8-bit "d"-sequence x2 plus value of PC
# jump12 -> value of the 12-bit "d"-sequence x2 plus value of PC
# @(disp,pc) -> value of the 8-bit "d"-sequence x2 or x4, plus value of PC
# TODO: This list does not exactly reflect the behavior of the parser
# where arguments are one of the following predefined strings (or an explicit
# register name like "vbr").
#
# #imm [i] rn [n] rm [m] jump8 [d]
# jump12 [d] pc+disp [d] @(disp,pc) [d] @rn [n]
# @rm [m] @rm+ [m] @rn+ [n] @-rn [n]
# @(disp,rn) [d,n] @(disp,rm) [d,m] @(r0,rn) [n] @(r0,rm) [m]
# @(disp,gbr) [d] @(r0, gbr] []
#
# The possible tags are %ret, %uncondjump, %condjump, %call, %delay, %islot.
0000000001001000 clrs
0000000000001000 clrt

View File

@ -1,81 +0,0 @@
//---------------------------------------------------------------------------//
// 1100101 |_ mov #0, r4 __ //
// 11 |_ <0xb380 %5c4> / _|_ _____ ___ //
// 0110 |_ 3.50 -> 3.60 | _\ \ / _ (_-< //
// |_ base# + offset |_| /_\_\___/__/ //
//---------------------------------------------------------------------------//
// fxos/AbstractMemory: Simulated memory
//
// This header defines the typeclass for simulated memory within a 32-bit
// address space. The class provides functions to identify the simulated range
// within the address space, as well as basic read and search operations.
//
// Suclasses of this only need to implement the translate_dynamic() method
// which gives low-level access to part of the simulated memory.
//
// Note that currently, given the implementation of virtual space bindings and
// AbstractMemory methods, most operations only try to translate_dynamic()
// once -- which means that they fail if the requested range is not simulated
// in a single block. This is usually not a problem because virtual space
// bindings simulate small memory areas separated by huge gaps, so they never
// extend each other.
//---
#ifndef FXOS_ABSTRACTMEMORY_H
#define FXOS_ABSTRACTMEMORY_H
#include <fxos/memory.h>
#include <fxos/util/Addressable.h>
#include <cstdint>
namespace FxOS {
/* A common interface for simulated memory. All non-virtual methods are
provided by the base class and need not be implemented. */
class AbstractMemory
{
public:
/* Checks if an address or interval is simulated (in its entirety) */
bool covers(uint32_t addr, int size = 1);
/* Check if a full region is simulated */
bool covers(MemoryRegion const &region);
/* Returns the data located at the provided virtual address, nullptr if it
is not entirely covered. */
char const *translate(uint32_t addr, int size = 1);
/* Returns the data located at the provided virtual address, and indicates
how much is available in *size. The pointer is null if [addr] itself is
not covered, in which case *size is also set to 0. */
virtual char const *translate_dynamic(uint32_t addr, int *size) = 0;
/* Search a binary pattern in the specified area. Returns the virtual
address of the first occurrence if any is found, [end] otherwise
(including if the range is empty or exceeds simulated memory). */
uint32_t search(uint32_t start, uint32_t end, void const *pat, int size);
/* Read a simple object from memory. The following methods all assume that
the specified address is simulated, and return a default value if it's
not -- you should probably check beforehand! Alignment constraints are
not checked either, that's up to you.
The return type has the value in [.value] and remembers the address in
[.address], which is sometimes useful. It implicitly converts to the
data type, see <fxos/util/Addressable.h>. */
/* Various sizes of integers with sign-extension or zero-extension. */
Addressable<int8_t> read_i8(uint32_t addr);
Addressable<uint8_t> read_u8(uint32_t addr);
Addressable<int16_t> read_i16(uint32_t addr);
Addressable<uint16_t> read_u16(uint32_t addr);
Addressable<int32_t> read_i32(uint32_t addr);
Addressable<uint32_t> read_u32(uint32_t addr);
/* Read a non-NUL-terminated string */
Addressable<std::string> read_str(uint32_t addr, size_t len);
};
} /* namespace FxOS */
#endif /* FXOS_ABSTRACTMEMORY_H */

View File

@ -4,16 +4,19 @@
// 0110 |_ 3.50 -> 3.60 | _\ \ / _ (_-< //
// |_ base# + offset |_| /_\_\___/__/ //
//---------------------------------------------------------------------------//
// fxos/vspace: Virtual address space with loaded code and analyses
// fxos/vspace: Virtual address space where files are loaded for analysis
//
// This is the main structure/entry point of fxos. A virtual space emulates the
// virtual memory of the MPU and can have files loaded ("bound") at chosen
// positions. Usually, there is one virtual space for each OS being studied.
// This file defines the VirtualSpace class, which is a virtual 32-bit address
// space in which buffers can be selectively mapped. Usually, there is one
// virtual space for each add-in/OS being studied. Data from the program is
// accessed through the virtual space's memory interface.
//
// Technically, each virtual space should also come with platform information,
// but currently only the MPU is specified and it's unused.
//
// Virtual spaces also centralize information related to analyses.
// The (mostly internal) AbstractMemory typeclass describes the memory
// interface, which mainly consists of translate_dynamic() to access underlying
// buffers, and general read functions. Note that most "small" read operations
// only try to translate_dynamic() once, which means that the object being read
// must be loaded as a single block. This is only a concern if bindings are
// used to hide/replace parts of loaded files.
//---
#ifndef FXOS_VSPACE_H
@ -23,7 +26,6 @@
#include <fxos/os.h>
#include <fxos/symbols.h>
#include <fxos/disassembly.h>
#include <fxos/AbstractMemory.h>
#include <fxos/util/Buffer.h>
#include <fxos/util/Addressable.h>
@ -34,6 +36,52 @@
namespace FxOS {
/* A common interface for simulated memory. All non-virtual methods are
provided by the base class and need not be implemented. */
class AbstractMemory
{
public:
/* Checks if an address or interval is simulated (in its entirety) */
bool covers(uint32_t addr, int size = 1);
/* Check if a full region is simulated */
bool covers(MemoryRegion const &region);
/* Returns the data located at the provided virtual address, nullptr if it
is not entirely covered. */
char const *translate(uint32_t addr, int size = 1);
/* Returns the data located at the provided virtual address, and indicates
how much is available in *size. The pointer is null if [addr] itself is
not covered, in which case *size is also set to 0. */
virtual char const *translate_dynamic(uint32_t addr, int *size) = 0;
/* Search a binary pattern in the specified area. Returns the virtual
address of the first occurrence if any is found, [end] otherwise
(including if the range is empty or exceeds simulated memory). */
uint32_t search(uint32_t start, uint32_t end, void const *pat, int size);
/* Read a simple object from memory. The following methods all assume that
the specified address is simulated, and return a default value if it's
not -- you should probably check beforehand! Alignment constraints are
not checked either, that's up to you.
The return type has the value in [.value] and remembers the address in
[.address], which is sometimes useful. It implicitly converts to the
data type, see <fxos/util/Addressable.h>. */
/* Various sizes of integers with sign-extension or zero-extension. */
Addressable<int8_t> read_i8(uint32_t addr);
Addressable<uint8_t> read_u8(uint32_t addr);
Addressable<int16_t> read_i16(uint32_t addr);
Addressable<uint16_t> read_u16(uint32_t addr);
Addressable<int32_t> read_i32(uint32_t addr);
Addressable<uint32_t> read_u32(uint32_t addr);
/* Read a non-NUL-terminated string */
Addressable<std::string> read_str(uint32_t addr, size_t len);
};
/* A binding of a data buffer into a memory region of the target. */
struct Binding: public AbstractMemory
{
@ -52,6 +100,7 @@ struct Binding: public AbstractMemory
};
/* A composite space where regions can be bound dynamically */
// TODO: Use a class interface for VirtualSpace
class VirtualSpace: public AbstractMemory
{
public:

View File

@ -1,104 +0,0 @@
//---------------------------------------------------------------------------//
// 1100101 |_ mov #0, r4 __ //
// 11 |_ <0xb380 %5c4> / _|_ _____ ___ //
// 0110 |_ 3.50 -> 3.60 | _\ \ / _ (_-< //
// |_ base# + offset |_| /_\_\___/__/ //
//---------------------------------------------------------------------------//
#include <fxos/AbstractMemory.h>
#include <cstring>
namespace FxOS {
bool AbstractMemory::covers(uint32_t addr, int size)
{
return (this->translate(addr, size) != nullptr);
}
bool AbstractMemory::covers(MemoryRegion const &region)
{
return this->covers(region.start, region.size());
}
char const *AbstractMemory::translate(uint32_t addr, int size)
{
int actual_size;
char const *ptr = this->translate_dynamic(addr, &actual_size);
return (ptr && actual_size >= size) ? ptr : nullptr;
}
Addressable<int8_t> AbstractMemory::read_i8(uint32_t addr)
{
int8_t *i8 = (int8_t *)this->translate(addr, 1);
if(!i8)
return Addressable((int8_t)-1);
return Addressable(addr, *i8);
}
Addressable<uint8_t> AbstractMemory::read_u8(uint32_t addr)
{
uint8_t *u8 = (uint8_t *)this->translate(addr, 1);
if(!u8)
return Addressable((uint8_t)-1);
return Addressable(addr, *u8);
}
Addressable<int16_t> AbstractMemory::read_i16(uint32_t addr)
{
uint8_t *i16 = (uint8_t *)this->translate(addr, 2);
if(!i16)
return Addressable((int16_t)-1);
int16_t v = (i16[0] << 8) | i16[1];
return Addressable(addr, v);
}
Addressable<uint16_t> AbstractMemory::read_u16(uint32_t addr)
{
uint8_t *u16 = (uint8_t *)this->translate(addr, 2);
if(!u16)
return Addressable((uint16_t)-1);
uint16_t v = (u16[0] << 8) | u16[1];
return Addressable(addr, v);
}
Addressable<int32_t> AbstractMemory::read_i32(uint32_t addr)
{
uint8_t *i32 = (uint8_t *)this->translate(addr, 4);
if(!i32)
return Addressable((int32_t)-1);
int32_t v = (i32[0] << 24) | (i32[1] << 16) | (i32[2] << 8) | i32[3];
return Addressable(addr, v);
}
Addressable<uint32_t> AbstractMemory::read_u32(uint32_t addr)
{
uint8_t *u32 = (uint8_t *)this->translate(addr, 4);
if(!u32)
return Addressable((uint32_t)-1);
uint32_t v = (u32[0] << 24) | (u32[1] << 16) | (u32[2] << 8) | u32[3];
return Addressable(addr, v);
}
Addressable<std::string> AbstractMemory::read_str(uint32_t addr, size_t len)
{
char const *str = this->translate(addr, len);
if(!str)
return Addressable(std::string());
return Addressable(addr, std::string(str, len));
}
uint32_t AbstractMemory::search(
uint32_t start, uint32_t end, void const *pattern, int size)
{
void const *data = translate(start, end - start);
if(!data)
return end;
void const *occurrence = memmem(data, end - start, pattern, size);
if(!occurrence)
return end;
return start + ((char *)occurrence - (char *)data);
}
} /* namespace FxOS */

View File

@ -11,6 +11,101 @@
namespace FxOS {
//=== AbstractMemory ===//
bool AbstractMemory::covers(uint32_t addr, int size)
{
return (this->translate(addr, size) != nullptr);
}
bool AbstractMemory::covers(MemoryRegion const &region)
{
return this->covers(region.start, region.size());
}
char const *AbstractMemory::translate(uint32_t addr, int size)
{
int actual_size;
char const *ptr = this->translate_dynamic(addr, &actual_size);
return (ptr && actual_size >= size) ? ptr : nullptr;
}
Addressable<int8_t> AbstractMemory::read_i8(uint32_t addr)
{
int8_t *i8 = (int8_t *)this->translate(addr, 1);
if(!i8)
return Addressable((int8_t)-1);
return Addressable(addr, *i8);
}
Addressable<uint8_t> AbstractMemory::read_u8(uint32_t addr)
{
uint8_t *u8 = (uint8_t *)this->translate(addr, 1);
if(!u8)
return Addressable((uint8_t)-1);
return Addressable(addr, *u8);
}
Addressable<int16_t> AbstractMemory::read_i16(uint32_t addr)
{
uint8_t *i16 = (uint8_t *)this->translate(addr, 2);
if(!i16)
return Addressable((int16_t)-1);
int16_t v = (i16[0] << 8) | i16[1];
return Addressable(addr, v);
}
Addressable<uint16_t> AbstractMemory::read_u16(uint32_t addr)
{
uint8_t *u16 = (uint8_t *)this->translate(addr, 2);
if(!u16)
return Addressable((uint16_t)-1);
uint16_t v = (u16[0] << 8) | u16[1];
return Addressable(addr, v);
}
Addressable<int32_t> AbstractMemory::read_i32(uint32_t addr)
{
uint8_t *i32 = (uint8_t *)this->translate(addr, 4);
if(!i32)
return Addressable((int32_t)-1);
int32_t v = (i32[0] << 24) | (i32[1] << 16) | (i32[2] << 8) | i32[3];
return Addressable(addr, v);
}
Addressable<uint32_t> AbstractMemory::read_u32(uint32_t addr)
{
uint8_t *u32 = (uint8_t *)this->translate(addr, 4);
if(!u32)
return Addressable((uint32_t)-1);
uint32_t v = (u32[0] << 24) | (u32[1] << 16) | (u32[2] << 8) | u32[3];
return Addressable(addr, v);
}
Addressable<std::string> AbstractMemory::read_str(uint32_t addr, size_t len)
{
char const *str = this->translate(addr, len);
if(!str)
return Addressable(std::string());
return Addressable(addr, std::string(str, len));
}
uint32_t AbstractMemory::search(
uint32_t start, uint32_t end, void const *pattern, int size)
{
void const *data = translate(start, end - start);
if(!data)
return end;
void const *occurrence = memmem(data, end - start, pattern, size);
if(!occurrence)
return end;
return start + ((char *)occurrence - (char *)data);
}
//=== Binding ===//
Binding::Binding(MemoryRegion source_region, Buffer const &source_buffer):
region {source_region}, buffer {source_buffer, region.size()}
{
@ -25,6 +120,8 @@ char const *Binding::translate_dynamic(uint32_t addr, int *size)
return nullptr;
}
//=== VirtualSpace ===//
VirtualSpace::VirtualSpace():
mpu {}, bindings {}, cursor {0}, disasm {*this}, m_os {nullptr}
{

View File

@ -38,10 +38,10 @@ void _h_hexdump(Session &session, Range r, Selections sel)
fmt::print(" ");
}
else if(is_selected(addr, sel)) {
fmt::print(theme(13), "{:02x}", v.read_u8(addr));
fmt::print(theme(13), "{:02x}", v.read_u8(addr).value);
}
else {
fmt::print("{:02x}", v.read_u8(addr));
fmt::print("{:02x}", v.read_u8(addr).value);
}
}