diff --git a/CMakeLists.txt b/CMakeLists.txt index eb79eeb..06f9cd1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/base-library/asmtables/sh3.txt b/base-library/asmtables/sh3.txt index 3d71eb9..8d14785 100644 --- a/base-library/asmtables/sh3.txt +++ b/base-library/asmtables/sh3.txt @@ -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 diff --git a/include/fxos/AbstractMemory.h b/include/fxos/AbstractMemory.h deleted file mode 100644 index bd3b737..0000000 --- a/include/fxos/AbstractMemory.h +++ /dev/null @@ -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 -#include -#include - -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 . */ - - /* Various sizes of integers with sign-extension or zero-extension. */ - Addressable read_i8(uint32_t addr); - Addressable read_u8(uint32_t addr); - Addressable read_i16(uint32_t addr); - Addressable read_u16(uint32_t addr); - Addressable read_i32(uint32_t addr); - Addressable read_u32(uint32_t addr); - - /* Read a non-NUL-terminated string */ - Addressable read_str(uint32_t addr, size_t len); -}; - -} /* namespace FxOS */ - -#endif /* FXOS_ABSTRACTMEMORY_H */ diff --git a/include/fxos/vspace.h b/include/fxos/vspace.h index ceb8583..a2d74fb 100644 --- a/include/fxos/vspace.h +++ b/include/fxos/vspace.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 #include #include -#include #include #include @@ -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 . */ + + /* Various sizes of integers with sign-extension or zero-extension. */ + Addressable read_i8(uint32_t addr); + Addressable read_u8(uint32_t addr); + Addressable read_i16(uint32_t addr); + Addressable read_u16(uint32_t addr); + Addressable read_i32(uint32_t addr); + Addressable read_u32(uint32_t addr); + + /* Read a non-NUL-terminated string */ + Addressable 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: diff --git a/lib/AbstractMemory.cpp b/lib/AbstractMemory.cpp deleted file mode 100644 index 2055cdd..0000000 --- a/lib/AbstractMemory.cpp +++ /dev/null @@ -1,104 +0,0 @@ -//---------------------------------------------------------------------------// -// 1100101 |_ mov #0, r4 __ // -// 11 |_ <0xb380 %5c4> / _|_ _____ ___ // -// 0110 |_ 3.50 -> 3.60 | _\ \ / _ (_-< // -// |_ base# + offset |_| /_\_\___/__/ // -//---------------------------------------------------------------------------// - -#include -#include - -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 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 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 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 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 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 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 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 */ diff --git a/lib/vspace.cpp b/lib/vspace.cpp index 29315e1..9de0816 100644 --- a/lib/vspace.cpp +++ b/lib/vspace.cpp @@ -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 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 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 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 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 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 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 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} { diff --git a/shell/h.cpp b/shell/h.cpp index a0db8cb..0c1ae0d 100644 --- a/shell/h.cpp +++ b/shell/h.cpp @@ -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); } }