From c9ecb855deccbc792bdf47b27fac6dcb71d0e8c8 Mon Sep 17 00:00:00 2001 From: Lephenixnoir Date: Sun, 15 Dec 2019 18:40:05 +0100 Subject: [PATCH] finish the base interfaces * switch from File to Buffer to manage file contents * boilerplate for a proprer command-line interface, and complete help * add a notion of Addressable to avoid losing the source address of very information provided by the OS type * use addressables in the AbstractMemory interface * fully parse the header and footer of OSes * bring [fxos info] to the level of the previous implementation * make most of the OS attributes public * use char* instead of void* for translation, since void* arithmetic triggers a hell of warnings in C++ --- fxos/fxos-cli.h | 13 +++ fxos/info.cpp | 76 +++++++++++++++++ fxos/main.cpp | 172 +++++++++++++++++++++++++++----------- include/fxos/endianness.h | 33 -------- include/fxos/load.h | 4 +- include/fxos/os.h | 43 +++++----- include/fxos/target.h | 72 +++++++++------- include/fxos/util.h | 71 +++++++++------- lib/load-asm.l | 4 +- lib/load-header.l | 2 +- lib/load.cpp | 2 +- lib/os.cpp | 70 +++++++++------- lib/target.cpp | 55 ++++++------ lib/util.cpp | 99 +++++++++++++--------- 14 files changed, 458 insertions(+), 258 deletions(-) create mode 100644 fxos/fxos-cli.h create mode 100644 fxos/info.cpp delete mode 100644 include/fxos/endianness.h diff --git a/fxos/fxos-cli.h b/fxos/fxos-cli.h new file mode 100644 index 0000000..ed269fd --- /dev/null +++ b/fxos/fxos-cli.h @@ -0,0 +1,13 @@ +//--- +// fxos-cli: A disassembler and OS reverse-engineering tool +//--- + +#ifndef FXOS_CLI_H +#define FXOS_CLI_H + +#include + +/* Print general information on an OS file */ +void os_info(std::string path); + +#endif /* FXOS_CLI_H */ diff --git a/fxos/info.cpp b/fxos/info.cpp new file mode 100644 index 0000000..da5f87b --- /dev/null +++ b/fxos/info.cpp @@ -0,0 +1,76 @@ +#include "fxos-cli.h" +#include +#include +#include + +using namespace FxOS; + +static char const *info_str = +"Header information:\n" +" Bootcode timestamp (DateA) (0x%000008x) : %s\n" +" Bootcode checksum (0x%000008x) : 0x%08x\n" +//" Serial number (0x%000008x) : %s\n" +" OS version (0x%000008x) : %s\n"; + +static char const *footer_str = +"\nFooter information:\n" +" Detected footer address : 0x%08x\n" +" Langdata entries found : %d\n" +" OS date (DateO) (0x%000008x) : %s\n" +" OS checksum (0x%000008x) : 0x%08x\n"; + +static char const *syscall_str = +"\nSyscall information:\n" +" Syscall table address (0x8001007c) : 0x%08x\n" +" Entries that point to valid memory : 0x%x\n" +" First seemingly invalid entry : 0x%08x\n" +" Syscall entries outside ROM:\n"; + +static char const *syscall_nonrom_str = +" %%%03x -> 0x%08x (%s memory)\n"; + +void os_info(std::string path) +{ + /* Create an 8M buffer and load the ROM there */ + Buffer romfile(path, MemoryRegion::ROM.size()); + OS os(romfile); + + /* There is some stuff we want to read directly */ + Target &t = os.target; + + printf(info_str, + &os.bootcode_timestamp, os.bootcode_timestamp.value.c_str(), + &os.bootcode_checksum, os.bootcode_checksum, +// &os.serial_number, os.serial_number, + &os.version, os.version.value.c_str()); + + if(os.footer == (uint32_t)-1) + { + printf("\nFooter could not be found.\n"); + } + else + { + printf(footer_str, os.footer, os.langdata, + &os.timestamp, os.timestamp.value.c_str(), + &os.checksum, os.checksum); + } + + uint32_t syscall_table = t.read_u32(0x8001007c); + uint32_t first_noncall = t.read_u32(syscall_table + + 4 * os.syscall_count()); + + printf(syscall_str, syscall_table, os.syscall_count(), first_noncall); + + int total = 0; + for(int i = 0; i < os.syscall_count(); i++) + { + uint32_t e = os.syscall(i); + MemoryRegion const *r = MemoryRegion::region_for(e); + if(!r || r->name == "ROM" || r->name == "ROM_P2") continue; + + printf(syscall_nonrom_str, i, e, r->name.c_str()); + total++; + } + + if(!total) printf(" (none)\n"); +} diff --git a/fxos/main.cpp b/fxos/main.cpp index 735cfd5..90cac9d 100644 --- a/fxos/main.cpp +++ b/fxos/main.cpp @@ -1,66 +1,111 @@ -#include +#include "fxos-cli.h" #include #include -#include -#include +#include using namespace FxOS; -char const *info_str = -"Header information:\n" -" Bootcode timestamp (DateA) (0x8000ffb0) : %s\n" -" Serial number (0x8000ffd0) : %s\n" -" Bootcode checksum (0x8000fffc) : 0x%s\n" -" OS version (0x80010020) : %s\n"; +static char const *help_string = R"( +usage: fxos info + fxos disasm (-a
| -s ) [options...] + fxos disasm -b [options...] + fxos analyze [-f] [-s] [-a] [-r] [options...] -char const *footer_str = -"\nFooter information:\n" -" Detected footer address : 0x8%07x\n" -" Langdata entries found : %d\n" -" OS date (DateO) (0x8%07x)" " : %s\n" -" OS checksum (0x8%07x)" " : 0x%s\n"; +fxos is a reverse-engineering tool to disassemble and analyze fx9860g-like +OS dumps, providing efficient annotations through an editable database. -char const *syscall_str = -"\nSyscall information:\n" -" Syscall table address (0x8001007c) : 0x%08x\n" -" Entries that point to valid memory : 0x%x\n" -" First seemingly invalid entry : 0x%08x\n" -" Syscall entries outside ROM:\n"; +Commands: + info Identify an OS image: version, platform, date, checksums... + disasm Disassemble and annotate code with relative address targets, + syscall invocations, control flow, constant propagation and hints + about memory structure. + analyze Dig an address or syscall number, finding syscall references, + 4-aligned occurrences, memory region and probable role. -char const *syscall_nonrom_str = -" %%%03x -> 0x%08x (%s memory)\n"; +General options: + -b Work with an arbitrary binary file, not an OS + -3, --sh3 Assume SH3 OS and platform (default: SH4) + -4, --sh4 Assume SH4 OS and platform (default: SH4) -void info(std::string path) +Database extensions: + --load Read documentation from + --load Read documentation recursively from + +Disassembly options: + -a
Start disassembling at this address + -s Start disassembling at this syscall's address + -l Length of region + --passes= Execute the specified comma-separated list of passes + +The default list of passes is pcrel,cfg,cstprop,syscall,regs. The available +passes are the following: + pcrel Resolve PC-relative references as their target address + cfg Build the control flow graph (uses pcrel) + cstprop Propagate constants by abstract interpretation (uses cfg) + syscall Annotate code with reverse syscalls + regs Annotate code with peripheral register addresses + +Analysis modes: + -f, --full Run all analysis passes on (same as -sar) + -s, --syscall Run syscall ID analysis + -a, --address Run code/data address analyis + -r, --register Run peripheral register analysis + +Analysis options: + --occurrences Show at most occurrences (integer or "all") + +All numbers support base prefixes "0" (octal) and "0x" (hexadecimal). +)"+1; + +int main_info(int argc, char **argv) { - File file(path); - OS os(file); + int error=0, option=0, mpu=0; - Target t; - t.bind_region(MemoryRegion::ROM, file); - t.bind_region(MemoryRegion::ROM_P2, file); + struct option const longs[] = { + { "help", no_argument, NULL, 'h' }, + { "sh3", no_argument, NULL, '3' }, + { "sh4", no_argument, NULL, '4' }, + }; - uint32_t syscall_table = t.read_u32(0x8001007c); - uint32_t first_noncall = t.read_u32(syscall_table + - 4 * os.syscall_count()); - - printf(syscall_str, syscall_table, os.syscall_count(), first_noncall); - - int total = 0; - for(int i = 0; i < os.syscall_count(); i++) + while(option >= 0 && option != '?') + switch((option = getopt_long(argc, argv, "h34", longs, NULL))) { - uint32_t e = os.syscall(i); - MemoryRegion const *r = MemoryRegion::region_for(e); - if(!r || r->name == "ROM" || r->name == "ROM_P2") continue; - - printf(syscall_nonrom_str, i, e, r->name.c_str()); - total++; + case 'h': + std::cerr << help_string; + break; + case '3': + case '4': + /* TODO: Use sh3/sh4 information in [fxos info)? */ + mpu = option; + break; + case '?': + error = 1; } - if(!total) printf(" (none)\n"); + if(error) return 1; + char const *path = argv[optind + 1]; + + if(!path) + { + std::cerr << help_string; + return 1; + } + + try { + os_info(path); + } + catch(std::exception &e) { + std::cerr << "error: " << e.what() << "\n"; + return 1; + } + + return 0; } -int main(void) +int main_disassembly(int argc, char **argv) { + std::cerr << "doing main_disasm, which is incomplete x_x\n"; + try { FxOS::load("data/sh3.txt"); @@ -73,7 +118,40 @@ int main(void) return 1; } - info("/home/lake/Documents/PC/DonnĂ©es/OS Graph 35+E II/3.10.bin"); - return 0; } + +int main_analyze(int argc, char **argv) +{ + std::cerr << "doing main_analyze, which is incomplete x_x\n"; + return 0; +} + +int main(int argc, char **argv) +{ + if(argc < 2) + { + std::cerr << help_string; + return 1; + } + + std::string cmd = argv[1]; + argv[1] = (char *)""; + + if(cmd == "info") + return main_info(argc, argv); + else if(cmd == "disasm") + return main_disassembly(argc, argv); + else if(cmd == "analyze") + return main_analyze(argc, argv); + + else if(cmd == "-?" || cmd == "-h" || cmd == "--help") + { + std::cerr << help_string; + return 0; + } + + std::cerr << "invalid operation '" << cmd << "'\n"; + std::cerr << "Try '" << argv[0] << " --help'.\n"; + return 1; +} diff --git a/include/fxos/endianness.h b/include/fxos/endianness.h deleted file mode 100644 index cf2cf82..0000000 --- a/include/fxos/endianness.h +++ /dev/null @@ -1,33 +0,0 @@ -//--- -// fxos.endianness. Somewhat cross-platform endianness conversion. (seriously?) -//--- - -#ifndef LIFXOS_ENDIANNESS_H -#define LIFXOS_ENDIANNESS_H - -#if defined(__APPLE__) - - #include - - #define htobe16(x) OSSwapHostToBigInt16(x) - #define htole16(x) OSSwapHostToLittleInt16(x) - #define be16toh(x) OSSwapBigToHostInt16(x) - #define le16toh(x) OSSwapLittleToHostInt16(x) - - #define htobe32(x) OSSwapHostToBigInt32(x) - #define htole32(x) OSSwapHostToLittleInt32(x) - #define be32toh(x) OSSwapBigToHostInt32(x) - #define le32toh(x) OSSwapLittleToHostInt32(x) - - #define htobe64(x) OSSwapHostToBigInt64(x) - #define htole64(x) OSSwapHostToLittleInt64(x) - #define be64toh(x) OSSwapBigToHostInt64(x) - #define le64toh(x) OSSwapLittleToHostInt64(x) - -#elif defined(__linux__) - - #include - -#endif - -#endif /* LIFXOS_ENDIANNESS_H */ diff --git a/include/fxos/load.h b/include/fxos/load.h index 423af1a..7c4d6d3 100644 --- a/include/fxos/load.h +++ b/include/fxos/load.h @@ -33,13 +33,13 @@ void load(std::string path); The parameters [offset] and [line] are set to reflect the location in the file where the raw content starts. These parameters are used to initialize the lexers in all other load functions. */ -Header load_header(File &file, size_t &offset, int &line); +Header load_header(Buffer const &file, size_t &offset, int &line); /* Load an assembly instruction table for the disassembler. @file Data file, presumably analyzed with lex_header() @start_offset Offset of assembly data in the file @start_line Line where assembly data starts in the file (for errors) */ -void load_asm(File &file, size_t start_offset, size_t start_line); +void load_asm(Buffer const &file, size_t start_offset, size_t start_line); } /* namespace FxOS */ diff --git a/include/fxos/os.h b/include/fxos/os.h index 8030cea..7bca245 100644 --- a/include/fxos/os.h +++ b/include/fxos/os.h @@ -16,11 +16,21 @@ namespace FxOS { class OS { public: - /* Load an OS from a file. */ - OS(File &file); + /* Load an OS from a buffer. */ + OS(Buffer &buffer); + /* This target contains just the OS in ROM and ROM_P2. It can be used + freely to extract data which is not already available here. */ + Target target; - /* Get OS version */ - std::string version() const noexcept; + /* Bootcode timestamp and checksum */ + Addressable bootcode_timestamp; + Addressable bootcode_checksum; + + /* OS version, serial number, timestamp and checksum */ + Addressable version; + Addressable serial_number; + Addressable timestamp; + Addressable checksum; /* Get number of syscalls */ int syscall_count() const noexcept; @@ -29,39 +39,32 @@ public: /* Find a syscall entry. Returns -1 if syscall is not found */ int find_syscall(uint32_t entry) const noexcept; - /* Get the footer address. Returns -1 if not found */ - uint32_t footer() const noexcept; + /* Footer address, or -1 if not found */ + uint32_t footer; + /* Number of langdata entries */ + int langdata; private: - /* Determine the OS version. This should be the first analysis function - to be called, because it determines the type of model (ie. fx9860g - versus fxcg50) thus the location of the syscall table and many more + /* Parse the OS header. This should be the first analysis function to + be called, because it determines the type of model (ie. fx9860g vs + fxcg50) thus the location of the syscall table and many more important parameters. */ - void parse_version(); + void parse_header(); /* Locate and parse the syscall table. */ void parse_syscall_table(); - /* Locate the footer */ + /* Locate and parse the footer. */ void parse_footer(); - /* Working target which is a simulated memory with just the OS */ - Target m_target; - //--- // OS information //--- - /* Version */ - std::string m_version; - /* Syscall table, in order of syscall IDs */ std::vector m_syscall_table; /* Bimap converse, syscalls sorted by address */ std::map m_syscall_addresses; - - /* Footer address */ - uint32_t m_footer; }; } /* namespace FxOS */ diff --git a/include/fxos/target.h b/include/fxos/target.h index 6478502..c6a12b0 100644 --- a/include/fxos/target.h +++ b/include/fxos/target.h @@ -24,21 +24,34 @@ public: /* Returns the data located at the provided virtual address. Throws std::out_of_range if the interval is not entirely simulated */ - virtual void const *translate(uint32_t addr, int size=1) const = 0; + virtual char const *translate(uint32_t addr, int size=1) const = 0; - /* Read data, with signed or unsigned extension. Virtual addresses - are used here, so they should be within the range of the region. - Throws std::out_of_range if this is not satisfied. + /* Read data from the memory. The following methods read data of + various types. (Not a template because of the restriction about + template specialization in non-namespaces scopes still in g++.) - These functions do *not* check alignment because exceptionally there - are instructions which can read unaligned (movua.l). Check it - yourself! */ - int32_t read_i8 (uint32_t addr) const; - uint32_t read_u8 (uint32_t addr) const; - int32_t read_i16(uint32_t addr) const; - uint32_t read_u16(uint32_t addr) const; - int32_t read_i32(uint32_t addr) const; - uint32_t read_u32(uint32_t addr) const; + When reading data, provide a virtual address. The addres is saved in + the returned object for later printing or inspection. The returned + object Addressable automatically converts to T when used, and + supports operator & which returns the original address. + + The size parameter is only meaningful for variable-sized types such + as string, and ignored for fixed-size types such as integers. If the + desired object is not within the range of the simulated memory, + throws std::out_of_range. */ + + /* Read integers with signed or unsigned extension. These functions do + not check alignment, because exceptionnally the processor supports + unaligned operations (eg. movual.l). */ + Addressable read_i8 (uint32_t addr) const; + Addressable read_u8 (uint32_t addr) const; + Addressable read_i16(uint32_t addr) const; + Addressable read_u16(uint32_t addr) const; + Addressable read_i32(uint32_t addr) const; + Addressable read_u32(uint32_t addr) const; + + /* Read a non-NUL-terminated string */ + Addressable read_str(uint32_t addr, size_t len) const; /* Search a binary pattern in the specified area. Returns the virtual address of the first occurrence if any is found, [end] otherwise. */ @@ -49,14 +62,16 @@ public: /* A binding of a data buffer into a memory region of the target. */ struct Binding: public AbstractMemory { - /* Constructor from file */ - Binding(MemoryRegion const ®ion, File &file); + /* Constructor from data buffer. An error is raised if the buffer is + not at least of the size of the region. In this case, a new buffer + can be constructed with the required size. */ + Binding(MemoryRegion const ®ion, Buffer const &buffer); /* Targeted region, might overlap with other bindings */ MemoryRegion region; - /* Actual data. This buffer must have at least [size] bytes */ - uint8_t *data; - /* Binding size, is the minimum of the region size and the data size */ + /* Actual data. This area must have at least [size] bytes */ + void const *data; + /* Binding size (equal to the region size) */ uint32_t size; /* Checks if an address is covered by the binding */ @@ -64,26 +79,27 @@ struct Binding: public AbstractMemory /* Returns this process' address (in [data]) corresponding to the provided virtual address */ - void const *translate(uint32_t addr, int size=1) const override; + char const *translate(uint32_t addr, int size=1) const override; /* Search a pattern */ uint32_t search(uint32_t start, uint32_t end, void const *pattern, int size) const override; }; +/* A composite target where regions can be bound dynamically */ class Target: public AbstractMemory { public: - /* Create an empty target with no sections */ + /* Create an empty target with no regions */ Target(); /* Bind an OS. This is used to either disassemble the OS itself, or - select the OS version for which code is being disassembled (typical - use is for add-ins). */ + select the OS version for which code is being disassembled (for + instance for add-ins). */ void bind_os(OS &os); - /* Bind a memory region from a file. The region can either be standard - (see ) or custom. + /* Bind a memory region from a buffer. The region can either be + standard (see ) or custom. If several loaded regions overlap on some addresses, *the last loaded region will be used*. Thus, new regions can be loaded to @@ -94,15 +110,15 @@ public: because bind_os() which will also enable OS-specific tasks such as syscall resolution. - If the file is smaller than the region being bound, the region is - shrunk to fit the file. */ - void bind_region(MemoryRegion const ®ion, File &file); + An error is raised if the buffer is smaller than the region being + bound. */ + void bind_region(MemoryRegion const ®ion, Buffer const &buffer); /* Check if an address is bound */ bool covers(uint32_t addr, int size=1) const noexcept override; /* Returns the data at the provided virtual address */ - void const *translate(uint32_t addr, int size=1) const override; + char const *translate(uint32_t addr, int size=1) const override; /* Search a pattern */ uint32_t search(uint32_t start, uint32_t end, void const *pattern, diff --git a/include/fxos/util.h b/include/fxos/util.h index 937c18a..53b67c6 100644 --- a/include/fxos/util.h +++ b/include/fxos/util.h @@ -25,50 +25,63 @@ std::string format(std::string const &format, Args ... args) return std::string(buf.get(), buf.get() + size - 1); } +/* An object extracted from a targets, which has a virtual address */ +template +struct Addressable +{ + /* Value */ + T value; + /* Original virtual address */ + uint32_t address; + + Addressable() = default; + Addressable(T value): value(value), address(-1) {} + Addressable(uint32_t addr, T value): value(value), address(addr) {} + + /* Implicitly decay to the base type */ + operator T () const { return value; } + + /* Return the address when using [&] */ + uint32_t operator & () const { return address; } +}; + /* An RAII contiguous memory buffer */ class Buffer { public: /* Empty buffer initialized with given byte */ - Buffer(int size, int fill=0x00); + Buffer(size_t size, int fill=0x00); - /* Buffer initialized from file, reading the given size and offset. - * Default offset is beginning of file. - * Default size (-1) is file size. If the specified region ends after - the end of the file, the buffer is padded. */ - Buffer(std::string filepath, int size=-1, int offset=0, int fill=0x00); + /* Buffer initialized from file, reading the given size from the + beginning of the file. If the file is smaller than the specified + size, the buffer is padded. - /* Create a buffer by copying (and possibly resizing) another buffer */ - Buffer(Buffer const &other, int new_size=-1); + If this constructor is used, the file path is remembered. */ + Buffer(std::string filepath, ssize_t size=-1, int fill=0x00); - /* Size */ - int size() const noexcept; -}; + /* Create a buffer by copying another buffer */ + Buffer(Buffer const &other); -/* A file abstraction that supports both direct load and memory mapping */ -class File -{ -public: - /* Load a file, either by buffer or by memory mapping */ - File(std::string path, bool mmap=false); + /* Create a buffer by copying and resizing another buffer */ + Buffer(Buffer const &other, size_t new_size, int fill=0x00); - /* Get the path, size and loading address of the file */ - std::string path() const noexcept; + /* Free allocated data, obviously */ + ~Buffer(); + + /* Get buffer size */ size_t size() const noexcept; - char *data() const noexcept; - /* Free the allocated buffers */ - ~File(); + /* Get data */ + char *data() noexcept; + char const *data() const noexcept; + + /* Get file path, when constructed from file */ + std::string path() const noexcept; private: - /* Path to file */ - std::string m_path; - /* Size of buffer, or mapping */ + void *m_data; size_t m_size; - /* Whether mmap() was used on the file */ - bool m_mmap; - /* Data buffer (m_mmap=false) or mapping address (m_mmap=true) */ - char *m_addr; + std::string m_path; }; #endif /* LIBFXOS_UTIL_H */ diff --git a/lib/load-asm.l b/lib/load-asm.l index 5fa4c2f..531f034 100644 --- a/lib/load-asm.l +++ b/lib/load-asm.l @@ -264,7 +264,7 @@ static void instantiate(struct Pattern p, std::string mnemonic, int argtoken1, } /* Load an assembly instruction table for the disassembler. */ -void load_asm(File &file, size_t start_offset, size_t start_line) +void load_asm(Buffer const &file, size_t start_offset, size_t start_line) { /* Lex all instructions and fill in the general assembly table */ @@ -273,7 +273,7 @@ void load_asm(File &file, size_t start_offset, size_t start_line) yylineno = start_line; filename = file.path(); - /* Insruction information */ + /* Instruction information */ char *code=nullptr, *mnemonic=nullptr; int argtoken1=0, argtoken2=0; diff --git a/lib/load-header.l b/lib/load-header.l index 82d4195..fd5cdcb 100644 --- a/lib/load-header.l +++ b/lib/load-header.l @@ -65,7 +65,7 @@ space [ \t]+ namespace FxOS { /* Load the header of a data file. */ -Header load_header(File &file, size_t &offset_ref, int &line_ref) +Header load_header(Buffer const &file, size_t &offset_ref, int &line_ref) { /* Build a map of properties */ FxOS::Header header; diff --git a/lib/load.cpp b/lib/load.cpp index e81ad16..247bb91 100644 --- a/lib/load.cpp +++ b/lib/load.cpp @@ -6,7 +6,7 @@ namespace FxOS { /* Load any fxos data file. */ void load(std::string path) { - File file(path); + Buffer file(path); size_t offset; int line; diff --git a/lib/os.cpp b/lib/os.cpp index 5953565..d4110ca 100644 --- a/lib/os.cpp +++ b/lib/os.cpp @@ -6,32 +6,38 @@ namespace FxOS { -OS::OS(File &file): m_target() +OS::OS(Buffer &buffer): target() { /* OS files are all at least 1 MB large */ - if(file.size() < 1000000) + if(buffer.size() < 1000000) throw std::runtime_error("OS files cannot be < 1MB"); /* Bind the given file to the internal analysis target */ - m_target.bind_region(MemoryRegion::ROM, file); - m_target.bind_region(MemoryRegion::ROM_P2, file); + this->target.bind_region(MemoryRegion::ROM, buffer); + this->target.bind_region(MemoryRegion::ROM_P2, buffer); - parse_version(); + parse_header(); parse_syscall_table(); -// parse_footer(); + parse_footer(); } -void OS::parse_version() +void OS::parse_header() { - /* Extract the version string at 0x10020 */ - Target &t = m_target; + Target &t = this->target; - char *version = (char *)t.translate(0x80010020, 10); - m_version = std::string(version, 10); + /* Bootcode timestamp at 0xffb0 (the very end of the bootcode) */ + this->bootcode_timestamp = t.read_str(0x8000ffb0, 14); + /* Bootcode checksum at 0xfffc */ + this->bootcode_checksum = t.read_u32(0x8000fffc); + + /* Version string at 0x10020 */ + this->version = t.read_str(0x80010020, 10); + /* Serial numer at 0xffd0... sometimes? */ + this->serial_number = t.read_str(0x8000ffd0, 8); } //--- -// Syscalle resolution +// Syscall resolution //--- int OS::syscall_count() const noexcept @@ -56,7 +62,7 @@ int OS::find_syscall(uint32_t entry) const noexcept void OS::parse_syscall_table() { - Target &t = m_target; + Target &t = this->target; /* Traverse the syscall table */ uint32_t syscall_table = t.read_u32(0x8001007c); @@ -80,31 +86,35 @@ void OS::parse_syscall_table() // Footer search //--- -uint32_t OS::footer() const noexcept -{ - return m_footer; -} - void OS::parse_footer() { - /* Find the footer address (last occurrence of "CASIOABSLangdata") */ + Target &t = this->target; + + /* Find the footer address (occurrence of "CASIOABSLangdata") */ uint32_t start = MemoryRegion::ROM.start; uint32_t end = MemoryRegion::ROM.end; - m_target.search(start, end, "CASIOABSLangdata", 16); -#if 0 - char const *signature = "CASIOABSLangdata"; - void *occ = NULL, *next = memmem(os->data, os->len, signature, 16); - void *end = os->data + os->len; - - while(next) + this->footer = t.search(start, end, "CASIOABSLangdata", 16); + if(this->footer == end) { - occ = next; - next = memmem(next + 1, end - (next + 1), signature, 16); + this->footer = -1; + this->timestamp = std::string(""); + this->bootcode_timestamp = std::string(""); + this->langdata = 0; + return; } - os->footer = (occ) ? (occ - os->data) : (uint32_t)-1; -#endif + uint32_t addr = this->footer + 8; + this->langdata = 0; + + while(!memcmp(t.translate(addr, 8), "Langdata", 8)) + { + this->langdata++; + addr += 0x30; + } + + this->timestamp = t.read_str(addr, 14); + this->checksum = t.read_u32(addr + 0x18); } } /* namespace FxOS */ diff --git a/lib/target.cpp b/lib/target.cpp index 11c1fa4..65e32ad 100644 --- a/lib/target.cpp +++ b/lib/target.cpp @@ -4,63 +4,66 @@ namespace FxOS { //--- -// Simulated memory primitives +// Simulated memory access primitives //--- -int32_t AbstractMemory::read_i8(uint32_t addr) const +Addressable AbstractMemory::read_i8(uint32_t addr) const { int8_t *i8 = (int8_t *)translate(addr, 1); - return *i8; + return Addressable(addr, *i8); } -uint32_t AbstractMemory::read_u8(uint32_t addr) const +Addressable AbstractMemory::read_u8(uint32_t addr) const { uint8_t *u8 = (uint8_t *)translate(addr, 1); - return *u8; + return Addressable(addr, *u8); } -int32_t AbstractMemory::read_i16(uint32_t addr) const +Addressable AbstractMemory::read_i16(uint32_t addr) const { uint8_t *i16 = (uint8_t *)translate(addr, 2); int16_t v = (i16[0] << 8) | i16[1]; - return v; + return Addressable(addr, v); } -uint32_t AbstractMemory::read_u16(uint32_t addr) const +Addressable AbstractMemory::read_u16(uint32_t addr) const { uint8_t *u16 = (uint8_t *)translate(addr, 2); uint16_t v = (u16[0] << 8) | u16[1]; - return v; + return Addressable(addr, v); } -int32_t AbstractMemory::read_i32(uint32_t addr) const +Addressable AbstractMemory::read_i32(uint32_t addr) const { uint8_t *i32 = (uint8_t *)translate(addr, 4); int32_t v = (i32[0] << 24) | (i32[1] << 16) | (i32[2] << 8) | i32[3]; - return v; + return Addressable(addr, v); } -uint32_t AbstractMemory::read_u32(uint32_t addr) const +Addressable AbstractMemory::read_u32(uint32_t addr) const { uint8_t *u32 = (uint8_t *)translate(addr, 4); uint32_t v = (u32[0] << 24) | (u32[1] << 16) | (u32[2] << 8) | u32[3]; - return v; + return Addressable(addr, v); +} + +Addressable AbstractMemory::read_str(uint32_t addr, size_t len) + const +{ + char const *str = translate(addr, len); + return Addressable(addr, std::string(str, len)); } //--- // Bindings of data buffers into memory regions //--- -Binding::Binding(MemoryRegion const &source_region, File &file): - region(source_region) +Binding::Binding(MemoryRegion const &source_region, Buffer const &buffer): + region(source_region), data(buffer.data()), size(region.size()) { - data = reinterpret_cast(file.data()); - size = region.size(); - - if(file.size() < region.size()) + if(buffer.size() < region.size()) { - region.end = region.start + file.size(); - size = file.size(); + throw std::runtime_error("Buffer too small to create binding"); } } @@ -69,14 +72,14 @@ bool Binding::covers(uint32_t addr, int size) const noexcept return addr >= region.start && addr + size <= region.end; } -void const *Binding::translate(uint32_t addr, int size) const +char const *Binding::translate(uint32_t addr, int size) const { if(!covers(addr, size)) { throw std::out_of_range("Out of binding range"); } - return (void *)(data + (addr - region.start)); + return (char *)data + (addr - region.start); } uint32_t Binding::search(uint32_t start, uint32_t end, void const *pattern, @@ -109,9 +112,9 @@ void Target::bind_os(OS &os) m_os = &os; } -void Target::bind_region(MemoryRegion const ®ion, File &file) +void Target::bind_region(MemoryRegion const ®ion, Buffer const &buffer) { - Binding b(region, file); + Binding b(region, buffer); m_bindings.push_back(b); } @@ -125,7 +128,7 @@ bool Target::covers(uint32_t addr, int size) const noexcept return false; } -void const *Target::translate(uint32_t addr, int size) const +char const *Target::translate(uint32_t addr, int size) const { for(auto it = m_bindings.crbegin(); it != m_bindings.crend(); it++) { diff --git a/lib/util.cpp b/lib/util.cpp index b7d5ede..6ba7e27 100644 --- a/lib/util.cpp +++ b/lib/util.cpp @@ -1,16 +1,27 @@ #include - -#include -#include +#include +#include #include #include +#include -/* A file abstraction that supports both direct load and memory mapping */ -File::File(std::string file_path, bool use_mmap): - m_path(file_path), m_mmap(use_mmap) +/* Empty buffer initialized with given byte */ +Buffer::Buffer(size_t size, int fill) { - char const *path = file_path.c_str(); + m_data = malloc(size); + if(!m_data) throw std::bad_alloc(); + + m_size = size; + memset(m_data, fill, size); + + m_path = "(anonymous)"; +} + +/* Buffer initialized from file */ +Buffer::Buffer(std::string filepath, ssize_t size, int fill) +{ + char const *path = filepath.c_str(); int fd = open(path, O_RDONLY); if(!fd) throw std::runtime_error(format("cannot open '%s'", path)); @@ -24,53 +35,63 @@ File::File(std::string file_path, bool use_mmap): throw std::runtime_error(format("cannot stat '%s'", path)); } - m_size = statbuf.st_size; + m_size = (size < 0) ? statbuf.st_size : size; + size_t size_to_read = std::min(m_size, (size_t)statbuf.st_size); - if(use_mmap) + m_data = malloc(m_size); + if(!m_data) throw std::bad_alloc(); + + /* Read buffer and fill whatever is left */ + memset(m_data, fill, m_size); + x = read(fd, m_data, size_to_read); + + close(fd); + if(x != (ssize_t)size_to_read) { - m_addr = (char *)mmap(nullptr, m_size, PROT_READ, MAP_SHARED, - fd, 0); - close(fd); - - if(m_addr == (char *)MAP_FAILED) - { - throw std::runtime_error(format( - "cannot map '%s'", path)); - } + throw std::runtime_error(format( + "error while reading '%s'", path)); } - else - { - m_addr = new char [m_size]; - x = read(fd, m_addr, m_size); - close(fd); - - if(x != statbuf.st_size) - { - throw std::runtime_error(format( - "error while reading '%s'", path)); - } - } + m_path = filepath; } -std::string File::path() const noexcept +/* Create a buffer by copying another buffer */ +Buffer::Buffer(Buffer const &other): + Buffer(other.size(), 0x00) { - return m_path; } -size_t File::size() const noexcept +/* Create a buffer by copying and resizing another buffer */ +Buffer::Buffer(Buffer const &other, size_t new_size, int fill): + Buffer(new_size, fill) +{ + memcpy(m_data, other.data(), std::min(new_size, other.size())); +} + +/* Free allocated data, obviously */ +Buffer::~Buffer() +{ + free(m_data); +} + +/* Buffer size */ +size_t Buffer::size() const noexcept { return m_size; } -char *File::data() const noexcept +/* Buffer data */ +char *Buffer::data() noexcept { - return m_addr; + return static_cast(m_data); +} +char const *Buffer::data() const noexcept +{ + return static_cast(m_data); } - -File::~File() +/* File path */ +std::string Buffer::path() const noexcept { - if(m_mmap) munmap(m_addr, m_size); - else delete[] m_addr; + return m_path; }