diff --git a/CMakeLists.txt b/CMakeLists.txt index 3001af6..70d03f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,7 @@ 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/include/fxos/AbstractMemory.h b/include/fxos/AbstractMemory.h new file mode 100644 index 0000000..4703a22 --- /dev/null +++ b/include/fxos/AbstractMemory.h @@ -0,0 +1,81 @@ +//---------------------------------------------------------------------------// +// 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 6ce88c6..4281a0f 100644 --- a/include/fxos/vspace.h +++ b/include/fxos/vspace.h @@ -1,13 +1,24 @@ +//---------------------------------------------------------------------------// +// 1100101 |_ mov #0, r4 __ // +// 11 |_ <0xb380 %5c4> / _|_ _____ ___ // +// 0110 |_ 3.50 -> 3.60 | _\ \ / _ (_-< // +// |_ base# + offset |_| /_\_\___/__/ // +//---------------------------------------------------------------------------// +// fxos/vspace: Virtual address space with loaded code and analyses +// +// This is the main structure/entry point of fxos. A Virtu + //--- // fxos.vspace: A virtual space where code is being studied //--- -#ifndef LIBFXOS_VSPACE_H -#define LIBFXOS_VSPACE_H +#ifndef FXOS_VSPACE_H +#define FXOS_VSPACE_H #include #include #include +#include #include #include @@ -18,148 +29,65 @@ namespace FxOS { -/* A common interface for simulated memory */ -class AbstractMemory -{ -public: - /* Checks if an address or interval is simulated */ - virtual bool covers(uint32_t addr, int size=1) const noexcept = 0; - /* Check if a full region is simulated */ - virtual bool covers(MemoryRegion const ®ion) const noexcept; - - /* Returns the data located at the provided virtual address, nullptr if it - is not entirely covered. */ - virtual char const *translate(uint32_t addr, int size=1) const = 0; - - /* 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) const = 0; - - /* 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++.) - - When reading data, provide a virtual address. The address is saved - in the returned object for later printing or inspection. The - returned object Addressable automatically converts to T when - used, and supports [.address] 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, returns - a dummy value of address -1. You should check the range beforehand! */ - - /* Read integers with signed or unsigned extension. These functions do - not check alignment, because exceptionally 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 - (including if the range is empty or exceeds simulated memory). */ - virtual uint32_t search(uint32_t start, uint32_t end, - void const *pattern, int size) const = 0; -}; - /* A binding of a data buffer into a memory region of the target. */ struct Binding: public AbstractMemory { - /* 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 region, Buffer buffer); + /* 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 region, Buffer buffer); - /* Targeted region, might overlap with other bindings */ - MemoryRegion region; - /* Underlying buffer (copy of the original one) */ - Buffer buffer; + /* Targeted region, might overlap with other bindings */ + MemoryRegion region; + /* Underlying buffer (copy of the original one) */ + Buffer buffer; - bool covers(uint32_t addr, int size=1) const noexcept override; - bool covers(MemoryRegion const ®ion) const noexcept override; - - char const *translate(uint32_t addr, int size=1) const override; - char const *translate_dynamic(uint32_t addr, int *size) const override; - - uint32_t search(uint32_t start, uint32_t end, void const *pattern, - int size) const override; -}; - -/* A target description in the database; loadable, but not loaded yet */ -struct Target -{ - /* Just a list of bindings to be formed */ - using Binding = std::pair; - std::vector bindings; - /* Also the MPU to be set */ - std::string mpu; + char const *translate_dynamic(uint32_t addr, int *size) override; }; /* A composite space where regions can be bound dynamically */ class VirtualSpace: public AbstractMemory { public: - /* Create an empty space with no regions */ - VirtualSpace(); + /* Create an empty space with no regions */ + VirtualSpace(); - /* Create a new virtual space with a target loaded. */ - VirtualSpace(Target const &target,std::vector const &folders); + /* MPU used by this target, or an empty string if unspecified */ + std::string mpu; - /* MPU used by this target, or an empty string if unspecified */ - std::string mpu; + /* List of bindings (most recent first) */ + std::vector bindings; - /* List of bindings */ - std::vector const &bindings() const { - return m_bindings; - } + /* OS analysis; performed on-demand. Returns the new or cached OS analysis, + and nullptr only if OS cannot be analyzed */ + OS *os_analysis(bool force=false); - /* OS analysis; performed on-demand. Returns the new or cached OS analysis, - and nullptr only if OS cannot be analyzed */ - OS *os_analysis(bool force=false); + /* Cursor position, used by the interactive shell */ + uint32_t cursor; - /* Cursor position, used by the interactive shell */ - uint32_t cursor; + /* Symbol table */ + SymbolTable symbols; - /* Symbol table */ - SymbolTable symbols; + /* Bind a memory region from a buffer. 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 + selectively override parts of the target. - If several loaded regions overlap on some addresses, *the last - loaded region will be used*. Thus, new regions can be loaded to - selectively override parts of the target. + An error is raised if the buffer is smaller than the region being + bound. */ + void bind_region(MemoryRegion const ®ion, Buffer const &buffer); - An error is raised if the buffer is smaller than the region being - bound. */ - void bind_region(MemoryRegion const ®ion, Buffer const &buffer); + /* Implementation of AbstractMemory primitives */ - bool covers(uint32_t addr, int size=1) const noexcept override; - bool covers(MemoryRegion const ®ion) const noexcept override; - - char const *translate(uint32_t addr, int size=1) const override; - char const *translate_dynamic(uint32_t addr, int *size) const override; - - uint32_t search(uint32_t start, uint32_t end, void const *pattern, - int size) const override; + char const *translate_dynamic(uint32_t addr, int *size) override; private: - /* Bound regions (in order of binding) */ - std::vector m_bindings; - /* Buffers owned by the target (when loaded from description) */ - std::vector m_buffers; - /* Current OS analyzer */ - std::unique_ptr m_os; + /* OS analysis results */ + std::unique_ptr m_os; }; } /* namespace FxOS */ -#endif /* LIBFXOS_VSPACE_H */ +#endif /* FXOS_VSPACE_H */ diff --git a/lib/AbstractMemory.cpp b/lib/AbstractMemory.cpp new file mode 100644 index 0000000..e8175a2 --- /dev/null +++ b/lib/AbstractMemory.cpp @@ -0,0 +1,104 @@ +//---------------------------------------------------------------------------// +// 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/os.cpp b/lib/os.cpp index c07bc99..650fd7f 100644 --- a/lib/os.cpp +++ b/lib/os.cpp @@ -181,8 +181,8 @@ void OS::parse_footer() // Checksum //--- -static uint32_t accumulate_range(VirtualSpace const &m_space, uint32_t start, - uint32_t end) +static uint32_t accumulate_range(VirtualSpace &m_space, + uint32_t start, uint32_t end) { uint32_t sum = 0; diff --git a/lib/vspace.cpp b/lib/vspace.cpp index cf88bc7..7d80cb7 100644 --- a/lib/vspace.cpp +++ b/lib/vspace.cpp @@ -4,123 +4,25 @@ namespace FxOS { -//--- -// Simulated memory access primitives -//--- - -bool AbstractMemory::covers(MemoryRegion const ®ion) const noexcept -{ - return covers(region.start, region.size()); -} - -Addressable AbstractMemory::read_i8(uint32_t addr) const -{ - int8_t *i8 = (int8_t *)translate(addr, 1); - if(!i8) - return Addressable((int8_t)-1); - return Addressable(addr, *i8); -} - -Addressable AbstractMemory::read_u8(uint32_t addr) const -{ - uint8_t *u8 = (uint8_t *)translate(addr, 1); - if(!u8) - return Addressable((uint8_t)-1); - return Addressable(addr, *u8); -} - -Addressable AbstractMemory::read_i16(uint32_t addr) const -{ - uint8_t *i16 = (uint8_t *)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) const -{ - uint8_t *u16 = (uint8_t *)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) const -{ - uint8_t *i32 = (uint8_t *)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) const -{ - uint8_t *u32 = (uint8_t *)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) - const -{ - char const *str = translate(addr, len); - if(!str) - return Addressable(std::string()); - return Addressable(addr, std::string(str, len)); -} - //--- // Bindings of data buffers into memory regions //--- Binding::Binding(MemoryRegion source_region, Buffer source_buffer): - region(source_region), buffer(source_buffer) + region {source_region}, buffer {source_buffer} { - /* Extend the buffer if it's not at least as large as the region */ - if(buffer.size < region.size()) { - buffer = Buffer(buffer, region.size()); - } + /* Extend the buffer if it's not at least as large as the region */ + if(buffer.size < region.size()) + buffer = Buffer(buffer, region.size()); } -bool Binding::covers(uint32_t addr, int size) const noexcept +char const *Binding::translate_dynamic(uint32_t addr, int *size) { - return size >= 0 && addr >= region.start && addr + size <= region.end; -} - -bool Binding::covers(MemoryRegion const &r) const noexcept -{ - return covers(r.start, r.size()); -} - -char const *Binding::translate(uint32_t addr, int size) const -{ - if(!covers(addr, size)) return nullptr; - return buffer.data.get() + (addr - region.start); -} - -char const *Binding::translate_dynamic(uint32_t addr, int *size) const -{ - if(!covers(addr, 1)) return nullptr; - *size = region.end - addr; - return buffer.data.get() + (addr - region.start); -} - -uint32_t Binding::search(uint32_t start, uint32_t end, void const *pattern, - int size) const -{ - 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); + if(addr >= region.start && addr < region.end) { + *size = region.end - addr; + return buffer.data.get() + (addr - region.start); + } + return nullptr; } //--- @@ -128,96 +30,34 @@ uint32_t Binding::search(uint32_t start, uint32_t end, void const *pattern, //--- VirtualSpace::VirtualSpace(): - m_bindings {}, m_buffers {}, m_os {nullptr} + mpu {}, bindings {}, m_os {nullptr} { } -VirtualSpace::VirtualSpace(Target const &target, - std::vector const &folders): - VirtualSpace() -{ - for(auto binding: target.bindings) - { - MemoryRegion region = binding.first; - ssize_t size = (region.size() > 0 ? region.size() : -1); - - auto b = m_buffers.emplace(m_buffers.end(), - binding.second, folders, size); - bind_region(region, *b); - } - - this->mpu = target.mpu; -} - OS *VirtualSpace::os_analysis(bool force) { - if(!m_os || force) { - m_os = std::make_unique(*this); - /* We don't keep an OS analysis result that failed */ - if(m_os->type == OS::UNKNOWN) - m_os = nullptr; - } - return m_os.get(); + if(!m_os || force) { + m_os = std::make_unique(*this); + /* We don't keep an OS analysis result that failed */ + if(m_os->type == OS::UNKNOWN) + m_os = nullptr; + } + return m_os.get(); } -void VirtualSpace::bind_region(MemoryRegion const ®ion,Buffer const &buffer) +void VirtualSpace::bind_region(MemoryRegion const ®ion, Buffer const &buf) { - Binding b(region, buffer); - m_bindings.push_back(b); + this->bindings.emplace(this->bindings.begin(), region, buf); } -bool VirtualSpace::covers(uint32_t addr, int size) const noexcept +char const *VirtualSpace::translate_dynamic(uint32_t addr, int *size) { - for(auto it = m_bindings.crbegin(); it != m_bindings.crend(); it++) - { - if(it->covers(addr, size)) return true; - } - - return false; -} - -bool VirtualSpace::covers(MemoryRegion const ®ion) const noexcept -{ - return covers(region.start, region.size()); -} - -char const *VirtualSpace::translate(uint32_t addr, int size) const -{ - for(auto it = m_bindings.crbegin(); it != m_bindings.crend(); it++) - { - char const *ptr = it->translate(addr, size); - if(ptr) return ptr; - } - return nullptr; -} - -char const *VirtualSpace::translate_dynamic(uint32_t addr, int *size) const -{ - for(auto it = m_bindings.crbegin(); it != m_bindings.crend(); it++) - { - char const *ptr = it->translate_dynamic(addr, size); - if(ptr) return ptr; - } - return nullptr; -} - -uint32_t VirtualSpace::search(uint32_t start, uint32_t end, - void const *pattern, int size) const -{ - uint32_t occurrence; - if(end < start || !covers(start, end - start)) - return end; - - for(auto it = m_bindings.crbegin(); it != m_bindings.crend(); it++) - { - if(it->covers(start, end - start)) - { - occurrence = it->search(start, end, pattern, size); - if(occurrence != end) return occurrence; - } - } - - return end; + for(auto &b: this->bindings) { + char const *ptr = b.translate_dynamic(addr, size); + if(ptr) + return ptr; + } + return nullptr; } } /* namespace FxOS */ diff --git a/shell/a.cpp b/shell/a.cpp index 295a660..94048d0 100644 --- a/shell/a.cpp +++ b/shell/a.cpp @@ -113,11 +113,11 @@ void _afh(Session &session, char const *reference, char const *pattern, if(distance < 0) distance = 32; if(align <= 0) align = 1; - VirtualSpace const &v = *session.current_space; + VirtualSpace &v = *session.current_space; /* If no region is specified, explore the regions for all bindings */ if(regions.size() == 0) { - for(auto &b: v.bindings()) + for(auto &b: v.bindings) regions.push_back(b.region); } @@ -129,8 +129,7 @@ void _afh(Session &session, char const *reference, char const *pattern, std::vector> pending; for(auto const &r: regions) { - uint32_t region_size = r.end - r.start; - char const *data = v.translate(r.start, region_size); + char const *data = v.translate(r.start, r.size()); if(!data) throw CommandError("region 0x{:08x} .. 0x{:08x} is not " "fully bound", r.start, r.end); @@ -139,7 +138,7 @@ void _afh(Session &session, char const *reference, char const *pattern, while((r.start + i) % align != 0) i++; /* Search patterns for (size) bytes inside (data) */ - for(; i <= (int)region_size - (int)size; i += align) { + for(; i <= (int)r.size() - (int)size; i += align) { if(!matches(data + i, reference, pattern, size)) continue; uint32_t start = r.start + i; @@ -150,7 +149,7 @@ void _afh(Session &session, char const *reference, char const *pattern, if(p.first + p.second + distance < start) { Range r; r.start = pending[0].first - distance; - r.end = p.first + p.second + distance; + r.end = p.first + p.second + distance - 1; if(output_started) fmt::print("...\n"); _h_hexdump(session, r, pending); @@ -166,14 +165,15 @@ void _afh(Session &session, char const *reference, char const *pattern, if(match_count >= 128) break; } if(match_count >= 128) break; - } - /* Print the last pending elements */ - if(pending.size()) { - auto const &p = pending[pending.size() - 1]; - Range r = { pending[0].first-distance, p.first+p.second+distance }; - if(output_started) fmt::print("...\n"); - _h_hexdump(session, r, pending); + /* Print the last pending elements */ + if(pending.size()) { + auto const &p = pending[pending.size() - 1]; + Range r = { pending[0].first-distance, p.first+p.second+distance }; + if(output_started) fmt::print("...\n"); + _h_hexdump(session, r, pending); + } + pending.clear(); } if(match_count == 0) diff --git a/shell/v.cpp b/shell/v.cpp index b4efb3b..e4834b4 100644 --- a/shell/v.cpp +++ b/shell/v.cpp @@ -33,13 +33,13 @@ static void show_vspace(std::string name, VirtualSpace &s, Session &session) if(is_current) fmt::print("* "); fmt::print(theme(11), "{}\n", name); - if(s.bindings().size() == 0) { + if(s.bindings.size() == 0) { fmt::print(" (no bindings)\n"); return; } fmt::print(" Region Start End File\n"); - for(auto &b: s.bindings()) { + for(auto &b: s.bindings) { MemoryRegion const *ref = MemoryRegion::region_for(b.region); fmt::print(" {:<7s} 0x{:08x} .. 0x{:08x}", (ref ? ref->name : ""), b.region.start, b.region.end); @@ -168,7 +168,7 @@ void _vm(Session &session, std::string file, std::vector regions) Buffer contents(path); /* If no files are loaded yet, set the PC to the first loaded region */ - if(!session.current_space->bindings().size()) + if(!session.current_space->bindings.size()) session.pc = regions[0].start; for(auto &r: regions)