#pragma once #include #include #include #include #include #include #include #include #include #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 > 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 instance; dwarf::core::root_die root_die; int elf_machine; friend class std::unique_ptr; };