dwarfinterpret/include/dwarfinterpret/DwarfInterpret.hpp

209 lines
7.2 KiB
C++

#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"
#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.
*
* 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 {
// Let's pretend this is enough
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 current UnwindContext (from the caller's point of view)
*
* **WARNING!** This context will be valid within the call frame of the
* caller, and within descendant calls from this function, but will
* most probably be invalid and result in weird behaviours if there is
* only a single `return` made!
*
* This is supposed to work:
* ```
* void foo() {
* 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();
/// 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;
const dwarf::core::FrameSection::fde_iterator fde_at(
uintptr_t pc) const;
const dwarf::core::FrameSection::cie_iterator cie_at(
uintptr_t 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>;
};