Compare commits
6 commits
7ca8329840
...
18d3655890
Author | SHA1 | Date | |
---|---|---|---|
Théophile Bastian | 18d3655890 | ||
Théophile Bastian | 97032ee31b | ||
Théophile Bastian | 0bd5a40bce | ||
Théophile Bastian | cd7c1635aa | ||
Théophile Bastian | d893b9138a | ||
Théophile Bastian | 3c9dae009e |
4
Makefile
4
Makefile
|
@ -1,11 +1,11 @@
|
|||
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
|
||||
|
||||
CXX=g++
|
||||
CXXFLAGS=-Wall -Wextra -O2 --std=c++14
|
||||
CXXFLAGS=-Wall -Wextra -O1 -g --std=c++14
|
||||
CXXLIBS=-ldwarfpp -ldwarf -lelf -lc++fileno
|
||||
CXXINCLUDE=-I$(INCLUDE_DIR)
|
||||
|
||||
|
|
|
@ -1,147 +1,4 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
|
||||
#include <dwarfpp/lib.hpp>
|
||||
#include <dwarfpp/regs.hpp>
|
||||
#include <dwarfpp/frame.hpp>
|
||||
#include <dwarfpp/attr.hpp>
|
||||
#include <dwarfpp/frame.hpp>
|
||||
#include <dwarfpp/root.hpp>
|
||||
|
||||
#define OF_WHAT_EXCEPTION(cl_name) \
|
||||
cl_name: public WhatException { \
|
||||
public:\
|
||||
cl_name(const std::string& what): WhatException(what) {} \
|
||||
cl_name() = default; \
|
||||
}
|
||||
|
||||
class DwarfInterpret {
|
||||
/** Singleton class holding a Dwarf interpret.
|
||||
* Must be first instanciated with the path to the binary being run, with a
|
||||
* call to `instanciate`, and can afterwards be accessed with calls to
|
||||
* `acquire`.
|
||||
*/
|
||||
|
||||
public: // Types, sub-classes, …
|
||||
class WhatException: public std::exception {
|
||||
/** Base exception for other exceptions, not supposed to be thrown
|
||||
* by itself */
|
||||
|
||||
std::string what_str;
|
||||
|
||||
public:
|
||||
/// Initialize the exception with an explanatory text chunk
|
||||
explicit WhatException(const std::string& what)
|
||||
: what_str(what) {}
|
||||
|
||||
/// Leave the explanatory text empty
|
||||
WhatException(): what_str("") {}
|
||||
|
||||
/// Get the explanatory text for this exception
|
||||
const char* what() const noexcept {
|
||||
return what_str.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// Thrown when `acquire` is called before `instanciate`
|
||||
class OF_WHAT_EXCEPTION(NotInstanciated);
|
||||
|
||||
/// Thrown when `instanciate` is called twice
|
||||
class OF_WHAT_EXCEPTION(AlreadyInstanciated);
|
||||
|
||||
/** 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);
|
||||
|
||||
/// Thrown when accessing unimplemented parts of this library
|
||||
class OF_WHAT_EXCEPTION(NotImplemented);
|
||||
|
||||
/// Thrown when somehow, getcontext (read CPU registers) fails
|
||||
class OF_WHAT_EXCEPTION(FailedGetContext);
|
||||
|
||||
/// Thrown when a Dwarf element is not found for a given PC
|
||||
class OF_WHAT_EXCEPTION(NotFound);
|
||||
|
||||
|
||||
/// A Dwarf register
|
||||
typedef dwarf::core::FrameSection::register_def DwarfRegister;
|
||||
|
||||
/// A Dwarf row of registers (for a given PC)
|
||||
typedef std::set<std::pair<int, DwarfRegister> > DwarfRow;
|
||||
|
||||
/// The value type of a register's contents
|
||||
typedef uintptr_t reg_content_t;
|
||||
|
||||
public: // methods
|
||||
DwarfInterpret(DwarfInterpret const&) = delete;
|
||||
void operator=(DwarfInterpret const&) = delete;
|
||||
|
||||
/** Acquire the instance of `DwarfInterpret`. The method #instanciate
|
||||
* must have been called beforehand.
|
||||
*
|
||||
* \throws NotInstanciated whenever this condition is not met.
|
||||
*/
|
||||
static DwarfInterpret& acquire();
|
||||
|
||||
/** Instanciate the instance of DwarfInterpret with the path to the
|
||||
* program currently running (usually, argv[0])
|
||||
*
|
||||
* \throws AlreadyInstanciated if this method is called twice. */
|
||||
static DwarfInterpret& instanciate(const std::string& elf_path);
|
||||
|
||||
/// Returns the ELF machine number for this ELF
|
||||
int get_elf_machine() const { return elf_machine; }
|
||||
|
||||
/** 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;
|
||||
|
||||
/** Retrieves the value pointed to by the given Dwarf register
|
||||
*
|
||||
* \throws ValuelessRegister */
|
||||
reg_content_t interpret_dw_register(
|
||||
const DwarfRow& row,
|
||||
int reg_id
|
||||
) const;
|
||||
|
||||
/** Get the return address at a given program counter, assuming the
|
||||
* correct registers are stored */
|
||||
uintptr_t get_return_address(uintptr_t cur_pc) const;
|
||||
|
||||
/** Get the return address of the current program point */
|
||||
uintptr_t get_self_return_address() const;
|
||||
|
||||
/// Get the current program counter
|
||||
static uintptr_t get_current_pc();
|
||||
|
||||
private:
|
||||
DwarfInterpret(const std::string& elf_path);
|
||||
|
||||
DwarfRegister get_column(const DwarfRow& row, int column) const;
|
||||
reg_content_t get_cpu_register(int reg_id) const;
|
||||
|
||||
const dwarf::core::FrameSection::fde_iterator fde_at(
|
||||
uintptr_t pc) const;
|
||||
const dwarf::core::FrameSection::cie_iterator cie_at(
|
||||
uintptr_t pc) const;
|
||||
const DwarfRow& dwarf_row_at(uintptr_t pc) const;
|
||||
|
||||
uintptr_t get_caller_pc() const;
|
||||
|
||||
|
||||
private: // members
|
||||
static std::unique_ptr<DwarfInterpret> instance;
|
||||
|
||||
dwarf::core::root_die root_die;
|
||||
int elf_machine;
|
||||
|
||||
friend class std::unique_ptr<DwarfInterpret>;
|
||||
};
|
||||
#include <dwarfinterpret/DwarfInterpret.hpp>
|
||||
#include <dwarfinterpret/MemoryMap.hpp>
|
||||
|
|
193
include/dwarfinterpret/DwarfInterpret.hpp
Normal file
193
include/dwarfinterpret/DwarfInterpret.hpp
Normal file
|
@ -0,0 +1,193 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
|
||||
#include <dwarfpp/lib.hpp>
|
||||
#include <dwarfpp/regs.hpp>
|
||||
#include <dwarfpp/frame.hpp>
|
||||
#include <dwarfpp/attr.hpp>
|
||||
#include <dwarfpp/frame.hpp>
|
||||
#include <dwarfpp/root.hpp>
|
||||
|
||||
#include "MemoryMap.hpp"
|
||||
#include "StackDump.hpp"
|
||||
|
||||
#define OF_WHAT_EXCEPTION(cl_name) \
|
||||
cl_name: public WhatException { \
|
||||
public:\
|
||||
cl_name(const std::string& what): WhatException(what) {} \
|
||||
cl_name() = default; \
|
||||
}
|
||||
|
||||
class DwarfInterpret {
|
||||
/** Singleton-ish class holding a Dwarf interpret.
|
||||
*
|
||||
* There is an instance of this class per object path from the #MemoryMap.
|
||||
* This class' interface helps making it a seamless integration, by
|
||||
* allowing to call a method for a program counter in any object path, and
|
||||
* internally passing the call to the appropriate instance.
|
||||
*/
|
||||
|
||||
public: // Types, sub-classes, …
|
||||
class WhatException: public std::exception {
|
||||
/** Base exception for other exceptions, not supposed to be thrown
|
||||
* by itself */
|
||||
|
||||
std::string what_str;
|
||||
|
||||
public:
|
||||
/// Initialize the exception with an explanatory text chunk
|
||||
explicit WhatException(const std::string& what)
|
||||
: what_str(what) {}
|
||||
|
||||
/// Leave the explanatory text empty
|
||||
WhatException(): what_str("") {}
|
||||
|
||||
/// Get the explanatory text for this exception
|
||||
const char* what() const noexcept {
|
||||
return what_str.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** Thrown when trying to create an instance with an invalid ELF file:
|
||||
* this can mean that the file did not exist, was a special path (eg.
|
||||
* [heap]) or that it cannot be read. */
|
||||
class OF_WHAT_EXCEPTION(InvalidElf);
|
||||
|
||||
/** Thrown when trying to create an instance with an ELF file that
|
||||
* contains no debug information */
|
||||
class OF_WHAT_EXCEPTION(NoDebugData);
|
||||
|
||||
/** Thrown when trying to access a program counter that is not mapped
|
||||
* 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);
|
||||
|
||||
/// Thrown when accessing unimplemented parts of this library
|
||||
class OF_WHAT_EXCEPTION(NotImplemented);
|
||||
|
||||
/// Thrown when somehow, getcontext (read CPU registers) fails
|
||||
class OF_WHAT_EXCEPTION(FailedGetContext);
|
||||
|
||||
/// Thrown when a Dwarf element is not found for a given PC
|
||||
class OF_WHAT_EXCEPTION(NotFound);
|
||||
|
||||
|
||||
/// A Dwarf register
|
||||
typedef dwarf::core::FrameSection::register_def DwarfRegister;
|
||||
|
||||
/// A Dwarf row of registers (for a given PC)
|
||||
typedef std::set<std::pair<int, DwarfRegister> > DwarfRow;
|
||||
|
||||
/// 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;
|
||||
|
||||
/** Acquire the anonymous instance of DwarfInterpret, which works on
|
||||
* the empty memory range. Acts as a handle: passes every call to some
|
||||
* other instance */
|
||||
static DwarfInterpret& acquire();
|
||||
|
||||
/** Acquire the relevant instance of #DwarfInterpret for the given
|
||||
* program counter. Instanciate it if needed. */
|
||||
static DwarfInterpret& acquire(uintptr_t pc);
|
||||
|
||||
/** Get the Dwarf registers row at the given PC.
|
||||
*
|
||||
* \throws NotFound if for some reason, the Dwarf row cannot be
|
||||
* accessed at this PC. */
|
||||
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 UnwindContext& ctx
|
||||
) const;
|
||||
|
||||
/** Retrieves the value pointed to by the given Dwarf register
|
||||
*
|
||||
* \throws ValuelessRegister */
|
||||
reg_content_t interpret_dw_register(
|
||||
const DwarfRow& row,
|
||||
int reg_id,
|
||||
const UnwindContext& ctx
|
||||
) const;
|
||||
|
||||
/** Get the return address at a given program counter, assuming the
|
||||
* correct registers are stored */
|
||||
uintptr_t get_return_address(uintptr_t cur_pc) const;
|
||||
|
||||
/** Get the return address of the current program point */
|
||||
uintptr_t get_self_return_address() const;
|
||||
|
||||
/// 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);
|
||||
|
||||
DwarfRegister get_column(const DwarfRow& row, int column) const;
|
||||
reg_content_t get_cpu_register(int reg_id) const;
|
||||
|
||||
const dwarf::core::FrameSection::fde_iterator fde_at(
|
||||
uintptr_t pc) const;
|
||||
const dwarf::core::FrameSection::cie_iterator cie_at(
|
||||
uintptr_t pc) const;
|
||||
|
||||
uintptr_t get_caller_pc() const;
|
||||
|
||||
/** Get the #DwarfInterpret instance responsible for the given PC, or
|
||||
* nullptr if the current instance is responsible. */
|
||||
DwarfInterpret* get_responsible_instance(uintptr_t pc) const;
|
||||
|
||||
|
||||
private: // members
|
||||
static MemoryMap memory_map;
|
||||
|
||||
// This map maps a `memory_map` memory region index to some instance of
|
||||
// a DwarfInterpret. The special index -1 is mapped to the anonymous
|
||||
// instance.
|
||||
static std::map<int, std::unique_ptr<DwarfInterpret> > instances;
|
||||
|
||||
MemoryMap::MapEntry map_entry;
|
||||
|
||||
std::unique_ptr<dwarf::core::root_die> root_die;
|
||||
|
||||
uintptr_t pc_offset;
|
||||
|
||||
friend class std::unique_ptr<DwarfInterpret>;
|
||||
};
|
28
include/dwarfinterpret/StackDump.hpp
Normal file
28
include/dwarfinterpret/StackDump.hpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
|
||||
#include <dwarfinterpret/MemoryMap.hpp>
|
||||
|
||||
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 <typename T> T deref(uintptr_t pos) const {
|
||||
return *((T*)(stack.get() + pos - offset));
|
||||
}
|
||||
uintptr_t at(uintptr_t pos) const {
|
||||
return deref<uintptr_t>(pos);
|
||||
}
|
||||
|
||||
private:
|
||||
StackDump();
|
||||
|
||||
typedef char cell_t;
|
||||
std::shared_ptr<cell_t> stack;
|
||||
uintptr_t offset; ///< such that stack[stack_addr - offset] is ok
|
||||
};
|
|
@ -1,10 +1,11 @@
|
|||
#include <DwarfInterpret.hpp>
|
||||
#include <dwarfinterpret/DwarfInterpret.hpp>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <cstring>
|
||||
|
||||
#include <fileno.hpp>
|
||||
#include <dwarfpp/lib.hpp>
|
||||
|
@ -23,38 +24,86 @@ using namespace std;
|
|||
using namespace dwarf;
|
||||
|
||||
|
||||
std::unique_ptr<DwarfInterpret> DwarfInterpret::instance(nullptr);
|
||||
MemoryMap DwarfInterpret::memory_map;
|
||||
map<int, unique_ptr<DwarfInterpret> > DwarfInterpret::instances;
|
||||
|
||||
DwarfInterpret::DwarfInterpret(const std::string& elf_path)
|
||||
: root_die(fileno(std::ifstream(elf_path)))
|
||||
DwarfInterpret::DwarfInterpret(const MemoryMap::MapEntry& memory_object)
|
||||
: map_entry(memory_object)
|
||||
{
|
||||
if(memory_object.mem_region.begin >= memory_object.mem_region.end) {
|
||||
// We're building the anonymous interpret: don't do anything
|
||||
root_die = nullptr;
|
||||
}
|
||||
else {
|
||||
const string& path = memory_object.pathname;
|
||||
if(path[0] == '[') {
|
||||
throw InvalidElf(
|
||||
string("This pathname is a special object: ") + path);
|
||||
}
|
||||
std::ifstream handle(path);
|
||||
if(!handle.good())
|
||||
throw InvalidElf(
|
||||
string("Cannot read this ELF file: ") + path);
|
||||
|
||||
root_die = unique_ptr<core::root_die>(
|
||||
new core::root_die(fileno(handle)));
|
||||
// TODO catch exceptions here ^^^^ and throw NoDebugData/InvalidElf
|
||||
|
||||
pc_offset = memory_object.mem_region.begin - memory_object.offset;
|
||||
}
|
||||
//std::ifstream file_in(elf_path);
|
||||
//root_die = dwarf::core::root_die(fileno(file_in));
|
||||
// FIXME this ^ deserves some checks. But this looks tedious.
|
||||
|
||||
GElf_Ehdr ehdr;
|
||||
GElf_Ehdr *ret = gelf_getehdr(root_die.get_elf(), &ehdr);
|
||||
assert(ret != 0);
|
||||
elf_machine = ehdr.e_machine;
|
||||
//GElf_Ehdr ehdr;
|
||||
//GElf_Ehdr *ret = gelf_getehdr(root_die.get_elf(), &ehdr);
|
||||
//assert(ret != 0);
|
||||
//elf_machine = ehdr.e_machine;
|
||||
}
|
||||
|
||||
DwarfInterpret& DwarfInterpret::acquire() {
|
||||
if(DwarfInterpret::instance == nullptr)
|
||||
throw NotInstanciated();
|
||||
return *(DwarfInterpret::instance);
|
||||
auto find_it = DwarfInterpret::instances.find(-1);
|
||||
if(find_it == DwarfInterpret::instances.end()) {
|
||||
MemoryMap::MapEntry anon_map_entry;
|
||||
anon_map_entry.offset = 0;
|
||||
anon_map_entry.mem_region.begin = 0;
|
||||
anon_map_entry.mem_region.end = 0;
|
||||
DwarfInterpret* instance = new DwarfInterpret(anon_map_entry);
|
||||
DwarfInterpret::instances.insert(make_pair(-1,
|
||||
unique_ptr<DwarfInterpret>(instance)));
|
||||
return *instance;
|
||||
}
|
||||
return *(find_it->second);
|
||||
}
|
||||
|
||||
DwarfInterpret& DwarfInterpret::instanciate(const std::string& elf_path) {
|
||||
if(DwarfInterpret::instance != nullptr)
|
||||
throw AlreadyInstanciated();
|
||||
DwarfInterpret::instance = std::unique_ptr<DwarfInterpret>(
|
||||
new DwarfInterpret(elf_path));
|
||||
return *(DwarfInterpret::instance);
|
||||
DwarfInterpret& DwarfInterpret::acquire(uintptr_t pc) {
|
||||
try {
|
||||
size_t entry_id = DwarfInterpret::memory_map.id_of_address(pc);
|
||||
auto find_it = DwarfInterpret::instances.find(entry_id);
|
||||
if(find_it == DwarfInterpret::instances.end()) {
|
||||
// Instanciate it
|
||||
MemoryMap::MapEntry map_entry =
|
||||
DwarfInterpret::memory_map[entry_id];
|
||||
DwarfInterpret* instance = new DwarfInterpret(map_entry);
|
||||
DwarfInterpret::instances.insert(make_pair(entry_id,
|
||||
unique_ptr<DwarfInterpret>(instance)));
|
||||
return *instance;
|
||||
}
|
||||
|
||||
// Already instanciated, return it
|
||||
return *(find_it->second);
|
||||
} catch(const std::out_of_range& e) {
|
||||
ostringstream exn_text;
|
||||
exn_text << "No ELF mapped section found for memory address 0x"
|
||||
<< hex << pc;
|
||||
throw(NoElfForPC(exn_text.str()));
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -66,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<reg_content_t>(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:
|
||||
|
@ -96,17 +172,30 @@ 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 {
|
||||
DwarfInterpret* responsible = get_responsible_instance(cur_pc);
|
||||
if(responsible != nullptr)
|
||||
return responsible->get_return_address(cur_pc);
|
||||
|
||||
const core::Cie& cie = *cie_at(cur_pc);
|
||||
const DwarfRow& row = dwarf_row_at(cur_pc);
|
||||
|
||||
return interpret_dw_register(row, cie.get_return_address_register_rule());
|
||||
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(),
|
||||
ctx);
|
||||
cerr << "Return address from 0x" << hex << cur_pc << ": "
|
||||
<< "0x" << translated_ra << endl;
|
||||
return translated_ra;
|
||||
}
|
||||
|
||||
uintptr_t DwarfInterpret::get_self_return_address() const {
|
||||
|
@ -125,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.
|
||||
|
@ -176,10 +320,22 @@ const core::FrameSection::fde_iterator DwarfInterpret::fde_at(
|
|||
uintptr_t pc
|
||||
) const
|
||||
{
|
||||
const core::FrameSection& fs = root_die.get_frame_section();
|
||||
const auto& fde_it = fs.find_fde_for_pc(pc);
|
||||
if(fde_it == fs.fde_end())
|
||||
throw NotFound(std::string("FDE at pc=") + std::to_string(pc));
|
||||
DwarfInterpret* responsible = get_responsible_instance(pc);
|
||||
if(responsible != nullptr)
|
||||
return responsible->fde_at(pc);
|
||||
|
||||
cerr << "Extracting FDE with ";
|
||||
map_entry.dump(cerr);
|
||||
|
||||
const core::FrameSection& fs = root_die->get_frame_section();
|
||||
uintptr_t translated_pc = pc - pc_offset;
|
||||
const auto& fde_it = fs.find_fde_for_pc(translated_pc);
|
||||
if(fde_it == fs.fde_end()) {
|
||||
ostringstream exn_text;
|
||||
exn_text << "FDE at pc=0x" << hex << pc
|
||||
<< " (0x" << translated_pc << " in ELF)";
|
||||
throw NotFound(exn_text.str());
|
||||
}
|
||||
|
||||
return fde_it;
|
||||
}
|
||||
|
@ -188,25 +344,40 @@ const core::FrameSection::cie_iterator DwarfInterpret::cie_at(
|
|||
uintptr_t pc
|
||||
) const
|
||||
{
|
||||
DwarfInterpret* responsible = get_responsible_instance(pc);
|
||||
if(responsible != nullptr)
|
||||
return responsible->cie_at(pc);
|
||||
|
||||
const core::Fde& fde = *fde_at(pc); // let NotFound escape if it occurs
|
||||
const auto& cie_it = fde.find_cie();
|
||||
if(cie_it == root_die.get_frame_section().cie_end()) // Should not happen…?
|
||||
if(cie_it == root_die->get_frame_section().cie_end()) //Should not happen…?
|
||||
throw NotFound(std::string("CIE matching FDE at pc=")
|
||||
+ std::to_string(pc));
|
||||
|
||||
return cie_it;
|
||||
}
|
||||
|
||||
const DwarfInterpret::DwarfRow& DwarfInterpret::dwarf_row_at(
|
||||
DwarfInterpret::DwarfRow DwarfInterpret::dwarf_row_at(
|
||||
uintptr_t pc
|
||||
) const
|
||||
{
|
||||
DwarfInterpret* responsible = get_responsible_instance(pc);
|
||||
if(responsible != nullptr)
|
||||
return responsible->dwarf_row_at(pc);
|
||||
|
||||
const core::Fde& fde = *fde_at(pc);
|
||||
uintptr_t translated_pc = pc - pc_offset;
|
||||
auto decoded = fde.decode();
|
||||
const auto& row_it = decoded.rows.find(pc);
|
||||
const auto& row_it = decoded.rows.find(translated_pc);
|
||||
if(row_it == decoded.rows.end())
|
||||
throw NotFound(std::string("Dwarf row in this FDE at pc=")
|
||||
+ std::to_string(pc));
|
||||
|
||||
return row_it->second;
|
||||
}
|
||||
|
||||
DwarfInterpret* DwarfInterpret::get_responsible_instance(uintptr_t pc) const {
|
||||
if(map_entry.mem_region.begin <= pc && pc < map_entry.mem_region.end)
|
||||
return nullptr;
|
||||
return &acquire(pc);
|
||||
}
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
#include "MemoryMap.hpp"
|
||||
#include <dwarfinterpret/MemoryMap.hpp>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
void MemoryMap::MapEntry::dump(ostream& os) const {
|
||||
os << "MapEntry: "
|
||||
<< hex << mem_region.begin << '-' << mem_region.end
|
||||
<< " offset " << offset
|
||||
<< " offset " << hex << offset
|
||||
<< dec
|
||||
<< " - <" << pathname << ">"
|
||||
<< endl;
|
||||
|
@ -24,23 +25,33 @@ MemoryMap::MemoryMap() {
|
|||
|
||||
string line;
|
||||
while(getline(handle, line).good()) {
|
||||
cerr << "## " << line << endl;
|
||||
istringstream lstream(line);
|
||||
MapEntry entry;
|
||||
uintptr_t discard;
|
||||
|
||||
/* Line format:
|
||||
* ============
|
||||
* (cf man 5 proc for details)
|
||||
*
|
||||
* beg_addr-end_addr [perms] offset [dev] [inode] pathname
|
||||
* [x]: x, ignored here.
|
||||
*/
|
||||
|
||||
lstream >> std::hex;
|
||||
lstream >> entry.mem_region.begin;
|
||||
lstream.ignore(1);
|
||||
lstream >> entry.mem_region.end;
|
||||
lstream.ignore(6);
|
||||
lstream >> discard;
|
||||
lstream.ignore(7);
|
||||
lstream >> entry.offset;
|
||||
lstream >> entry.pathname;
|
||||
lstream >> entry.mem_region.begin; // beg_addr
|
||||
lstream.ignore(1); // ignore `-`
|
||||
lstream >> entry.mem_region.end; // end_addr
|
||||
lstream.ignore(6); // ignore ` [perms] ` (perms is 4ch long)
|
||||
lstream >> entry.offset; // offset
|
||||
lstream.ignore(7); // ignore device
|
||||
lstream >> discard; // ignore inode
|
||||
lstream >> entry.pathname; // pathname
|
||||
|
||||
rev_addr_map.insert(make_pair(entry.mem_region, map_entries.size()));
|
||||
map_entries.push_back(entry);
|
||||
}
|
||||
cerr << endl;
|
||||
|
||||
handle.close();
|
||||
}
|
||||
|
@ -77,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;
|
||||
|
|
40
src/StackDump.cpp
Normal file
40
src/StackDump.cpp
Normal file
|
@ -0,0 +1,40 @@
|
|||
#include <dwarfinterpret/StackDump.hpp>
|
||||
|
||||
#include <dwarfinterpret/MemoryMap.hpp>
|
||||
#include <cassert>
|
||||
#include <iostream> // FIXME
|
||||
#include <cstring>
|
||||
|
||||
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<cell_t>(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;
|
||||
}
|
1
test/.gitignore
vendored
Normal file
1
test/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
*.bin
|
20
test/Makefile
Normal file
20
test/Makefile
Normal file
|
@ -0,0 +1,20 @@
|
|||
CXX=g++
|
||||
CXXFLAGS=-Wall -Wextra -O0 -g --std=c++14
|
||||
CXXLIBS=-ldwarfpp -ldwarf -lelf -lc++fileno -ldwarfinterpret
|
||||
CXXDIRS=-I../include -L../lib
|
||||
|
||||
|
||||
OBJS = $(SRC:.cpp=.o)
|
||||
|
||||
###############################################################################
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
%.bin: %.o
|
||||
$(CXX) $(CXXFLAGS) $(CXXDIRS) $(CXXLIBS) $< -o "$@"
|
||||
|
||||
%.o: %.cpp
|
||||
$(CXX) $(CXXFLAGS) $(CXXDIRS) -c "$<" -o "$@"
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(TARGET)
|
48
test/dump_my_stack.cpp
Normal file
48
test/dump_my_stack.cpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
#include <cstdio>
|
||||
#include <DwarfInterpret.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
volatile int optim_stopper = 0;
|
||||
|
||||
void dump_my_stack() {
|
||||
DwarfInterpret& dw = DwarfInterpret::acquire();
|
||||
|
||||
DwarfInterpret::UnwindContext unw_context =
|
||||
DwarfInterpret::get_current_unwind_context();
|
||||
MemoryMap mmap;
|
||||
|
||||
while(true) {
|
||||
printf(">> PC = %lX ", unw_context.rip);
|
||||
MemoryMap::MapEntry cur_map_entry =
|
||||
mmap[mmap.id_of_address(unw_context.rip)];
|
||||
uintptr_t inelf_pc = unw_context.rip
|
||||
- cur_map_entry.mem_region.begin + cur_map_entry.offset;
|
||||
printf("(in ELF: %lX) <<\n", inelf_pc);
|
||||
fflush(stdout);
|
||||
unw_context = dw.unwind_context(unw_context);
|
||||
}
|
||||
}
|
||||
|
||||
void fill_my_stack(int stack_depth) {
|
||||
if(stack_depth == 0)
|
||||
dump_my_stack();
|
||||
|
||||
fill_my_stack(stack_depth - 1);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
MemoryMap mmap;
|
||||
cout << "Dumping memory map… (" << mmap.size() << " entries)" << endl;
|
||||
for(const auto& entry: mmap) {
|
||||
entry.dump(cout);
|
||||
}
|
||||
cout << "End memory map" << endl;
|
||||
int stack_depth = 5;
|
||||
if(argc >= 2)
|
||||
stack_depth = atoi(argv[1]);
|
||||
|
||||
fill_my_stack(stack_depth);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in a new issue