fxos: refactor AbstractMemory into <fxos/vspace.h>
This commit is contained in:
parent
56a4800bbd
commit
c0820b59f0
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ®ion);
|
||||
|
||||
/* 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 */
|
|
@ -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 ®ion);
|
||||
|
||||
/* 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:
|
||||
|
|
|
@ -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 ®ion)
|
||||
{
|
||||
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 */
|
|
@ -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 ®ion)
|
||||
{
|
||||
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}
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue