diff --git a/Makefile b/Makefile index e9220e2..b500bbc 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ LIB_DIR=lib TARGET=$(LIB_DIR)/libdwarfinterpret.so -SRC=src/DwarfInterpret.cpp src/MemoryMap.cpp +SRC=src/DwarfInterpret.cpp src/MemoryMap.cpp src/StackDump.cpp INCLUDE_DIR=include diff --git a/include/dwarfinterpret/DwarfInterpret.hpp b/include/dwarfinterpret/DwarfInterpret.hpp index 105b953..0a46054 100644 --- a/include/dwarfinterpret/DwarfInterpret.hpp +++ b/include/dwarfinterpret/DwarfInterpret.hpp @@ -12,6 +12,7 @@ #include #include "MemoryMap.hpp" +#include "StackDump.hpp" #define OF_WHAT_EXCEPTION(cl_name) \ cl_name: public WhatException { \ @@ -64,6 +65,10 @@ class DwarfInterpret { * to an ELF file */ class OF_WHAT_EXCEPTION(NoElfForPC); + /** This register was needed, but is not common within DWARF + * expressions and thus wasn't unwinded. */ + class OF_WHAT_EXCEPTION(ExoticRegister); + /** Thrown when trying to get the value of a Dwarf register (column) * that is not defined or has, somehow, no value at this PC. */ class OF_WHAT_EXCEPTION(ValuelessRegister); @@ -87,6 +92,18 @@ class DwarfInterpret { /// The value type of a register's contents typedef uintptr_t reg_content_t; + + /// An unwind context, holding registers + struct UnwindContext { + UnwindContext(const StackDump& dump): stack(dump) {} + + // Let's pretend this is enough + StackDump stack; + uintptr_t rip; + uintptr_t rsp; + uintptr_t rbp; + }; + public: // methods DwarfInterpret(DwarfInterpret const&) = delete; void operator=(DwarfInterpret const&) = delete; @@ -104,14 +121,15 @@ class DwarfInterpret { * * \throws NotFound if for some reason, the Dwarf row cannot be * accessed at this PC. */ - const DwarfRow& dwarf_row_at(uintptr_t pc) const; + DwarfRow dwarf_row_at(uintptr_t pc) const; /** Retrieves the value pointed to by the given Dwarf register * * \throws ValuelessRegister */ reg_content_t interpret_dw_register( const DwarfRow& row, - const DwarfRegister& reg + const DwarfRegister& reg, + const UnwindContext& ctx ) const; /** Retrieves the value pointed to by the given Dwarf register @@ -119,7 +137,8 @@ class DwarfInterpret { * \throws ValuelessRegister */ reg_content_t interpret_dw_register( const DwarfRow& row, - int reg_id + int reg_id, + const UnwindContext& ctx ) const; /** Get the return address at a given program counter, assuming the @@ -132,6 +151,12 @@ class DwarfInterpret { /// Get the current program counter static uintptr_t get_current_pc(); + /// Get the current UnwindContext (from the caller's point of view) + static UnwindContext get_current_unwind_context(); + + /// Unwinds once the given context + UnwindContext unwind_context(const UnwindContext& ctx); + private: DwarfInterpret(const MemoryMap::MapEntry& memory_object); diff --git a/include/dwarfinterpret/StackDump.hpp b/include/dwarfinterpret/StackDump.hpp new file mode 100644 index 0000000..2686378 --- /dev/null +++ b/include/dwarfinterpret/StackDump.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +#include + +class StackDump { + public: + static StackDump snapshot(uintptr_t rsp); ///< Take an instant snapshot + + StackDump(const StackDump& oth); ///< copy + StackDump& operator=(const StackDump& oth); ///< copy + + template T deref(uintptr_t pos) const { + return *((T*)(stack.get() + pos - offset)); + } + uintptr_t at(uintptr_t pos) const { + return deref(pos); + } + + private: + StackDump(); + + typedef char cell_t; + std::shared_ptr stack; + uintptr_t offset; ///< such that stack[stack_addr - offset] is ok +}; diff --git a/src/DwarfInterpret.cpp b/src/DwarfInterpret.cpp index 59e3bdf..d312ea7 100644 --- a/src/DwarfInterpret.cpp +++ b/src/DwarfInterpret.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -101,7 +102,8 @@ DwarfInterpret& DwarfInterpret::acquire(uintptr_t pc) { DwarfInterpret::reg_content_t DwarfInterpret::interpret_dw_register( const DwarfInterpret::DwarfRow& row, - const DwarfInterpret::DwarfRegister& reg + const DwarfInterpret::DwarfRegister& reg, + const UnwindContext& ctx ) const { switch(reg.k) { @@ -113,21 +115,48 @@ DwarfInterpret::reg_content_t DwarfInterpret::interpret_dw_register( ostringstream exprs; int dwarf_regnum = reg.register_plus_offset_r().first; assert(dwarf_regnum != DW_FRAME_CFA_COL3); - int cpu_regnum = REG_DWARF_TO_UCONTEXT[dwarf_regnum]; - reg_content_t* cpu_content = - (reg_content_t*) get_cpu_register(cpu_regnum); - reg_content_t* addr = + reg_content_t cpu_content = 9; + cerr << "@@ Interpreting register "; + switch(dwarf_regnum) { + case lib::DWARF_X86_64_RIP: + cerr << "RIP"; + cpu_content = (reg_content_t) ctx.rip; + break; + case lib::DWARF_X86_64_RBP: + cerr << "RBP"; + cpu_content = (reg_content_t) ctx.rbp; + break; + case lib::DWARF_X86_64_RSP: + cerr << "RSP"; + cpu_content = (reg_content_t) ctx.rsp; + break; + default: + throw ExoticRegister(to_string(dwarf_regnum)); + } + reg_content_t addr = cpu_content + reg.register_plus_offset_r().second; - return *addr; + cerr << " = " << hex + << cpu_content << " + offset = " << addr //<< " -> " << *addr + << dec << endl; + + + return addr; } break; case core::FrameSection::register_def::SAVED_AT_OFFSET_FROM_CFA: { - reg_content_t* cfa_loc = - (reg_content_t*) interpret_dw_register(row, DW_FRAME_CFA_COL3); - reg_content_t* addr = cfa_loc; - return *addr; + reg_content_t cfa_loc = + interpret_dw_register(row, DW_FRAME_CFA_COL3, ctx); + int cfa_offset = reg.saved_at_offset_from_cfa_r(); + reg_content_t addr = cfa_loc + cfa_offset; + reg_content_t value = ctx.stack.deref(addr); + cerr << "@@ Interpreting CFA offset: CFA is " << hex + << cfa_loc << " + offset " << dec << cfa_offset << hex + << " = " << addr + << " -> " << value + << dec << endl; + return value; } break; case core::FrameSection::register_def::SAVED_AT_EXPR: @@ -143,10 +172,11 @@ DwarfInterpret::reg_content_t DwarfInterpret::interpret_dw_register( DwarfInterpret::reg_content_t DwarfInterpret::interpret_dw_register( const DwarfInterpret::DwarfRow& row, - int reg_id + int reg_id, + const UnwindContext& ctx ) const { - return interpret_dw_register(row, get_column(row, reg_id)); + return interpret_dw_register(row, get_column(row, reg_id), ctx); } uintptr_t DwarfInterpret::get_return_address(uintptr_t cur_pc) const { @@ -157,8 +187,12 @@ uintptr_t DwarfInterpret::get_return_address(uintptr_t cur_pc) const { const core::Cie& cie = *cie_at(cur_pc); const DwarfRow& row = dwarf_row_at(cur_pc); + UnwindContext ctx = get_current_unwind_context(); + // FIXME ^^^ ugly patch, this should not be a thing uintptr_t translated_ra = - interpret_dw_register(row, cie.get_return_address_register_rule()); + interpret_dw_register(row, + cie.get_return_address_register_rule(), + ctx); cerr << "Return address from 0x" << hex << cur_pc << ": " << "0x" << translated_ra << endl; return translated_ra; @@ -180,6 +214,61 @@ uintptr_t DwarfInterpret::get_current_pc() { return dw.get_return_address(pc_here); } +DwarfInterpret::UnwindContext DwarfInterpret::get_current_unwind_context() { + // FIXME for now this returns SOME unwind context (actually, the unwind + // context snapshot naively taken from inside this function). Unwinding + // it some number of times should yield the expected context + + uintptr_t rsp = DwarfInterpret::acquire().get_cpu_register(REG_RSP); + + UnwindContext ctx(StackDump::snapshot(rsp)); + DwarfInterpret& dw = DwarfInterpret::acquire(); + ctx.rip = dw.get_cpu_register(REG_RIP); + ctx.rsp = rsp; + ctx.rbp = dw.get_cpu_register(REG_RBP); + + cerr << "CREATING CONTEXT. %rsp=0x" << hex + << ctx.rsp + << ", %rbp=0x" << ctx.rbp + << ", %rip=0x" << ctx.rip + << dec << endl; + + return ctx; +} + +DwarfInterpret::UnwindContext DwarfInterpret::unwind_context( + const DwarfInterpret::UnwindContext& ctx + ) +{ + DwarfInterpret* responsible = get_responsible_instance(ctx.rip); + if(responsible != nullptr) + return responsible->unwind_context(ctx); + + DwarfRow cur_row = dwarf_row_at(ctx.rip); + const core::Cie& cie = *cie_at(ctx.rip); + UnwindContext new_context(ctx.stack); + cerr << "Obtaining previous context as reg " + << cie.get_return_address_register_rule() + << " at current IP = " + << hex << ctx.rip << endl; + new_context.rip = interpret_dw_register( + cur_row, + cie.get_return_address_register_rule(), + ctx); + cerr << "Yielding " << hex << new_context.rip << dec << endl; + new_context.rbp = interpret_dw_register( + cur_row, + lib::DWARF_X86_64_RBP, + ctx); + + new_context.rsp = interpret_dw_register( + cur_row, + DW_FRAME_CFA_COL3, + ctx); + + return new_context; +} + uintptr_t DwarfInterpret::get_caller_pc() const { // We assume we want the PC of the caller of the calling function. This // means we have to unwind twice. `get_current_pc` unwinds once. @@ -268,7 +357,7 @@ const core::FrameSection::cie_iterator DwarfInterpret::cie_at( return cie_it; } -const DwarfInterpret::DwarfRow& DwarfInterpret::dwarf_row_at( +DwarfInterpret::DwarfRow DwarfInterpret::dwarf_row_at( uintptr_t pc ) const { diff --git a/src/MemoryMap.cpp b/src/MemoryMap.cpp index ba36e53..d688192 100644 --- a/src/MemoryMap.cpp +++ b/src/MemoryMap.cpp @@ -88,9 +88,7 @@ size_t MemoryMap::id_of_address(uintptr_t addr) const { MemoryRegion reg_index(addr, addr); auto bound_it = rev_addr_map.lower_bound(reg_index); - if(bound_it == rev_addr_map.end()) - throw std::out_of_range("No region containing this address"); - if(bound_it->first.contains(addr)) + if(bound_it != rev_addr_map.end() && bound_it->first.contains(addr)) return bound_it->second; if(bound_it != rev_addr_map.begin()) { --bound_it; diff --git a/src/StackDump.cpp b/src/StackDump.cpp new file mode 100644 index 0000000..6e3bbce --- /dev/null +++ b/src/StackDump.cpp @@ -0,0 +1,40 @@ +#include + +#include +#include +#include // FIXME +#include + +using namespace std; + +StackDump StackDump::snapshot(uintptr_t rsp) { + StackDump stack; + + MemoryMap memory_map; + const MemoryMap::MapEntry& stack_region = + memory_map[memory_map.id_of_address(rsp)]; + + assert(stack_region.pathname == "[stack]"); + size_t stack_size = stack_region.mem_region.end - rsp; + stack.stack = std::shared_ptr(new cell_t[stack_size]); + cerr << "memcpy'ing " << stack_size << " bytes" << endl; + memcpy(stack.stack.get(), (void*)rsp, stack_size); // FIXME way too brutal + + stack.offset = rsp; + + return stack; +} + +StackDump::StackDump() + : stack(nullptr), offset(0) +{} + +StackDump::StackDump(const StackDump& oth) { + this->operator=(oth); +} + +StackDump& StackDump::operator=(const StackDump& oth) { + stack = oth.stack; + offset = oth.offset; + return *this; +}