Compare commits
3 commits
18d3655890
...
e5b76b3fb1
Author | SHA1 | Date | |
---|---|---|---|
Théophile Bastian | e5b76b3fb1 | ||
Théophile Bastian | 36096575ed | ||
Théophile Bastian | c4895ed0d1 |
2
Doxyfile
2
Doxyfile
|
@ -791,7 +791,7 @@ WARN_LOGFILE =
|
||||||
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
|
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
|
||||||
# Note: If this tag is empty the current directory is searched.
|
# Note: If this tag is empty the current directory is searched.
|
||||||
|
|
||||||
INPUT = "include/" "src/"
|
INPUT = "include/dwarfinterpret/" "src/"
|
||||||
|
|
||||||
# This tag can be used to specify the character encoding of the source files
|
# This tag can be used to specify the character encoding of the source files
|
||||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
||||||
|
|
2
Makefile
2
Makefile
|
@ -1,6 +1,6 @@
|
||||||
LIB_DIR=lib
|
LIB_DIR=lib
|
||||||
TARGET=$(LIB_DIR)/libdwarfinterpret.so
|
TARGET=$(LIB_DIR)/libdwarfinterpret.so
|
||||||
SRC=src/DwarfInterpret.cpp src/MemoryMap.cpp src/StackDump.cpp
|
SRC=src/DwarfInterpret.cpp src/MemoryMap.cpp
|
||||||
|
|
||||||
INCLUDE_DIR=include
|
INCLUDE_DIR=include
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
#include <dwarfpp/root.hpp>
|
#include <dwarfpp/root.hpp>
|
||||||
|
|
||||||
#include "MemoryMap.hpp"
|
#include "MemoryMap.hpp"
|
||||||
#include "StackDump.hpp"
|
|
||||||
|
|
||||||
#define OF_WHAT_EXCEPTION(cl_name) \
|
#define OF_WHAT_EXCEPTION(cl_name) \
|
||||||
cl_name: public WhatException { \
|
cl_name: public WhatException { \
|
||||||
|
@ -93,12 +92,16 @@ class DwarfInterpret {
|
||||||
typedef uintptr_t reg_content_t;
|
typedef uintptr_t reg_content_t;
|
||||||
|
|
||||||
|
|
||||||
/// An unwind context, holding registers
|
/** An unwind context, holding registers.
|
||||||
|
*
|
||||||
|
* The registers kept here should be enough for most of the unwinding
|
||||||
|
* processes. To fully emulate libunwind, we would have to save every
|
||||||
|
* register that is overwritten and not caller-saved.
|
||||||
|
*
|
||||||
|
* You'll most probably want to instanciate such a structure using
|
||||||
|
* #DwarfInterpret::get_current_unwind_context. */
|
||||||
struct UnwindContext {
|
struct UnwindContext {
|
||||||
UnwindContext(const StackDump& dump): stack(dump) {}
|
|
||||||
|
|
||||||
// Let's pretend this is enough
|
// Let's pretend this is enough
|
||||||
StackDump stack;
|
|
||||||
uintptr_t rip;
|
uintptr_t rip;
|
||||||
uintptr_t rsp;
|
uintptr_t rsp;
|
||||||
uintptr_t rbp;
|
uintptr_t rbp;
|
||||||
|
@ -141,17 +144,32 @@ class DwarfInterpret {
|
||||||
const UnwindContext& ctx
|
const UnwindContext& ctx
|
||||||
) const;
|
) const;
|
||||||
|
|
||||||
/** Get the return address at a given program counter, assuming the
|
/** Get the current UnwindContext (from the caller's point of view)
|
||||||
* correct registers are stored */
|
*
|
||||||
uintptr_t get_return_address(uintptr_t cur_pc) const;
|
* **WARNING!** This context will be valid within the call frame of the
|
||||||
|
* caller, and within descendant calls from this function, but will
|
||||||
/** Get the return address of the current program point */
|
* most probably be invalid and result in weird behaviours if there is
|
||||||
uintptr_t get_self_return_address() const;
|
* only a single `return` made!
|
||||||
|
*
|
||||||
/// Get the current program counter
|
* This is supposed to work:
|
||||||
static uintptr_t get_current_pc();
|
* ```
|
||||||
|
* void foo() {
|
||||||
/// Get the current UnwindContext (from the caller's point of view)
|
* UnwindContext context = get_current_unwind_context();
|
||||||
|
* // have fun with context
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* While this will probably crash:
|
||||||
|
* ```
|
||||||
|
* UnwindContext wrap_get_context() {
|
||||||
|
* return f(get_current_unwind_context());
|
||||||
|
* }
|
||||||
|
* void foo() {
|
||||||
|
* UnwindContext context = wrap_get_context();
|
||||||
|
* // have fun with context
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
static UnwindContext get_current_unwind_context();
|
static UnwindContext get_current_unwind_context();
|
||||||
|
|
||||||
/// Unwinds once the given context
|
/// Unwinds once the given context
|
||||||
|
@ -161,15 +179,12 @@ class DwarfInterpret {
|
||||||
DwarfInterpret(const MemoryMap::MapEntry& memory_object);
|
DwarfInterpret(const MemoryMap::MapEntry& memory_object);
|
||||||
|
|
||||||
DwarfRegister get_column(const DwarfRow& row, int column) const;
|
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(
|
const dwarf::core::FrameSection::fde_iterator fde_at(
|
||||||
uintptr_t pc) const;
|
uintptr_t pc) const;
|
||||||
const dwarf::core::FrameSection::cie_iterator cie_at(
|
const dwarf::core::FrameSection::cie_iterator cie_at(
|
||||||
uintptr_t pc) const;
|
uintptr_t pc) const;
|
||||||
|
|
||||||
uintptr_t get_caller_pc() const;
|
|
||||||
|
|
||||||
/** Get the #DwarfInterpret instance responsible for the given PC, or
|
/** Get the #DwarfInterpret instance responsible for the given PC, or
|
||||||
* nullptr if the current instance is responsible. */
|
* nullptr if the current instance is responsible. */
|
||||||
DwarfInterpret* get_responsible_instance(uintptr_t pc) const;
|
DwarfInterpret* get_responsible_instance(uintptr_t pc) const;
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
#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
|
|
||||||
};
|
|
|
@ -24,6 +24,15 @@ using namespace std;
|
||||||
using namespace dwarf;
|
using namespace dwarf;
|
||||||
|
|
||||||
|
|
||||||
|
#define get_cpu_register(reg_id, dest) {\
|
||||||
|
ucontext_t context; \
|
||||||
|
if(getcontext(&context) != 0) \
|
||||||
|
throw DwarfInterpret::FailedGetContext(); \
|
||||||
|
(dest) = context.uc_mcontext.gregs[(reg_id)]; \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
MemoryMap DwarfInterpret::memory_map;
|
MemoryMap DwarfInterpret::memory_map;
|
||||||
map<int, unique_ptr<DwarfInterpret> > DwarfInterpret::instances;
|
map<int, unique_ptr<DwarfInterpret> > DwarfInterpret::instances;
|
||||||
|
|
||||||
|
@ -150,7 +159,7 @@ DwarfInterpret::reg_content_t DwarfInterpret::interpret_dw_register(
|
||||||
interpret_dw_register(row, DW_FRAME_CFA_COL3, ctx);
|
interpret_dw_register(row, DW_FRAME_CFA_COL3, ctx);
|
||||||
int cfa_offset = reg.saved_at_offset_from_cfa_r();
|
int cfa_offset = reg.saved_at_offset_from_cfa_r();
|
||||||
reg_content_t addr = cfa_loc + cfa_offset;
|
reg_content_t addr = cfa_loc + cfa_offset;
|
||||||
reg_content_t value = ctx.stack.deref<reg_content_t>(addr);
|
reg_content_t value = *((reg_content_t*) addr);
|
||||||
cerr << "@@ Interpreting CFA offset: CFA is " << hex
|
cerr << "@@ Interpreting CFA offset: CFA is " << hex
|
||||||
<< cfa_loc << " + offset " << dec << cfa_offset << hex
|
<< cfa_loc << " + offset " << dec << cfa_offset << hex
|
||||||
<< " = " << addr
|
<< " = " << addr
|
||||||
|
@ -179,53 +188,11 @@ DwarfInterpret::reg_content_t DwarfInterpret::interpret_dw_register(
|
||||||
return interpret_dw_register(row, get_column(row, reg_id), ctx);
|
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);
|
|
||||||
|
|
||||||
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 {
|
|
||||||
// Aaaaand now we have to get_return_address thrice.
|
|
||||||
return get_return_address(get_caller_pc());
|
|
||||||
}
|
|
||||||
|
|
||||||
uintptr_t DwarfInterpret::get_current_pc() {
|
|
||||||
// Assumes the PC is stored in REG_RIP **AND** the PC we want is this
|
|
||||||
// function's return address
|
|
||||||
|
|
||||||
DwarfInterpret& dw = DwarfInterpret::acquire();
|
|
||||||
reg_content_t pc_here = dw.get_cpu_register(REG_RIP);
|
|
||||||
fprintf(stderr, "PC=%p <inside>\n", pc_here);
|
|
||||||
fprintf(stderr, "actual PC=%p <inside>\n", __builtin_return_address(0));
|
|
||||||
return dw.get_return_address(pc_here);
|
|
||||||
}
|
|
||||||
|
|
||||||
DwarfInterpret::UnwindContext DwarfInterpret::get_current_unwind_context() {
|
DwarfInterpret::UnwindContext DwarfInterpret::get_current_unwind_context() {
|
||||||
// FIXME for now this returns SOME unwind context (actually, the unwind
|
UnwindContext ctx;
|
||||||
// context snapshot naively taken from inside this function). Unwinding
|
get_cpu_register(REG_RIP, ctx.rip);
|
||||||
// it some number of times should yield the expected context
|
get_cpu_register(REG_RSP, ctx.rsp);
|
||||||
|
get_cpu_register(REG_RBP, ctx.rbp);
|
||||||
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
|
cerr << "CREATING CONTEXT. %rsp=0x" << hex
|
||||||
<< ctx.rsp
|
<< ctx.rsp
|
||||||
|
@ -233,7 +200,9 @@ DwarfInterpret::UnwindContext DwarfInterpret::get_current_unwind_context() {
|
||||||
<< ", %rip=0x" << ctx.rip
|
<< ", %rip=0x" << ctx.rip
|
||||||
<< dec << endl;
|
<< dec << endl;
|
||||||
|
|
||||||
return ctx;
|
DwarfInterpret& dw = DwarfInterpret::acquire(ctx.rip);
|
||||||
|
|
||||||
|
return dw.unwind_context(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
DwarfInterpret::UnwindContext DwarfInterpret::unwind_context(
|
DwarfInterpret::UnwindContext DwarfInterpret::unwind_context(
|
||||||
|
@ -246,7 +215,7 @@ DwarfInterpret::UnwindContext DwarfInterpret::unwind_context(
|
||||||
|
|
||||||
DwarfRow cur_row = dwarf_row_at(ctx.rip);
|
DwarfRow cur_row = dwarf_row_at(ctx.rip);
|
||||||
const core::Cie& cie = *cie_at(ctx.rip);
|
const core::Cie& cie = *cie_at(ctx.rip);
|
||||||
UnwindContext new_context(ctx.stack);
|
UnwindContext new_context;
|
||||||
cerr << "Obtaining previous context as reg "
|
cerr << "Obtaining previous context as reg "
|
||||||
<< cie.get_return_address_register_rule()
|
<< cie.get_return_address_register_rule()
|
||||||
<< " at current IP = "
|
<< " at current IP = "
|
||||||
|
@ -269,13 +238,6 @@ DwarfInterpret::UnwindContext DwarfInterpret::unwind_context(
|
||||||
return new_context;
|
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.
|
|
||||||
|
|
||||||
return get_return_address(DwarfInterpret::get_current_pc());
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Key>
|
template<typename Key>
|
||||||
static typename std::set<std::pair<int, Key> >::const_iterator find_column(
|
static typename std::set<std::pair<int, Key> >::const_iterator find_column(
|
||||||
const std::set<std::pair<int, Key> >& set,
|
const std::set<std::pair<int, Key> >& set,
|
||||||
|
@ -305,17 +267,6 @@ DwarfInterpret::DwarfRegister DwarfInterpret::get_column(
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
DwarfInterpret::reg_content_t DwarfInterpret::get_cpu_register(
|
|
||||||
int reg_id) const
|
|
||||||
{
|
|
||||||
ucontext_t context;
|
|
||||||
if(getcontext(&context) != 0)
|
|
||||||
throw FailedGetContext();
|
|
||||||
|
|
||||||
// Let the user deal with reg_id correctness
|
|
||||||
return context.uc_mcontext.gregs[reg_id];
|
|
||||||
}
|
|
||||||
|
|
||||||
const core::FrameSection::fde_iterator DwarfInterpret::fde_at(
|
const core::FrameSection::fde_iterator DwarfInterpret::fde_at(
|
||||||
uintptr_t pc
|
uintptr_t pc
|
||||||
) const
|
) const
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
#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;
|
|
||||||
}
|
|
|
@ -4,8 +4,6 @@ CXXLIBS=-ldwarfpp -ldwarf -lelf -lc++fileno -ldwarfinterpret
|
||||||
CXXDIRS=-I../include -L../lib
|
CXXDIRS=-I../include -L../lib
|
||||||
|
|
||||||
|
|
||||||
OBJS = $(SRC:.cpp=.o)
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
all: $(TARGET)
|
all: $(TARGET)
|
||||||
|
@ -13,8 +11,13 @@ all: $(TARGET)
|
||||||
%.bin: %.o
|
%.bin: %.o
|
||||||
$(CXX) $(CXXFLAGS) $(CXXDIRS) $(CXXLIBS) $< -o "$@"
|
$(CXX) $(CXXFLAGS) $(CXXDIRS) $(CXXLIBS) $< -o "$@"
|
||||||
|
|
||||||
|
dump_my_stack.bin: dump_my_stack.o
|
||||||
|
$(CXX) $(CXXFLAGS) $(CXXDIRS) $(CXXLIBS) -ldl -rdynamic $< -o "$@"
|
||||||
|
|
||||||
%.o: %.cpp
|
%.o: %.cpp
|
||||||
$(CXX) $(CXXFLAGS) $(CXXDIRS) -c "$<" -o "$@"
|
$(CXX) $(CXXFLAGS) $(CXXDIRS) -c "$<" -o "$@"
|
||||||
|
|
||||||
|
.PRECIOUS: %.o
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(OBJS) $(TARGET)
|
rm -f *.o *.bin
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <dlfcn.h>
|
||||||
#include <DwarfInterpret.hpp>
|
#include <DwarfInterpret.hpp>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
@ -13,12 +14,19 @@ void dump_my_stack() {
|
||||||
MemoryMap mmap;
|
MemoryMap mmap;
|
||||||
|
|
||||||
while(true) {
|
while(true) {
|
||||||
|
Dl_info dl_inf;
|
||||||
|
int dl_rc = dladdr((void *) unw_context.rip, &dl_inf);
|
||||||
|
|
||||||
printf(">> PC = %lX ", unw_context.rip);
|
printf(">> PC = %lX ", unw_context.rip);
|
||||||
MemoryMap::MapEntry cur_map_entry =
|
MemoryMap::MapEntry cur_map_entry =
|
||||||
mmap[mmap.id_of_address(unw_context.rip)];
|
mmap[mmap.id_of_address(unw_context.rip)];
|
||||||
uintptr_t inelf_pc = unw_context.rip
|
uintptr_t inelf_pc = unw_context.rip
|
||||||
- cur_map_entry.mem_region.begin + cur_map_entry.offset;
|
- cur_map_entry.mem_region.begin + cur_map_entry.offset;
|
||||||
printf("(in ELF: %lX) <<\n", inelf_pc);
|
|
||||||
|
printf("(in ELF: 0x%lX, func %s, path %s) <<\n",
|
||||||
|
inelf_pc,
|
||||||
|
(dl_rc && dl_inf.dli_sname) ? dl_inf.dli_sname : "(no symbol)",
|
||||||
|
cur_map_entry.pathname.c_str());
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
unw_context = dw.unwind_context(unw_context);
|
unw_context = dw.unwind_context(unw_context);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue