dwarfinterpret/include/dwarfinterpret/DwarfInterpret.hpp
Théophile Bastian c4895ed0d1 Make context acquisition behave correctly wrt. calling site
It now actually returns the context at the call site of the function,
instead of somewhere inside DwarfInterpret
2018-04-06 16:26:24 +02:00

181 lines
6.1 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"
#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 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;
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>;
};