Plug MemoryMap into DwarfInterpret
This commit is contained in:
parent
d893b9138a
commit
cd7c1635aa
3 changed files with 173 additions and 59 deletions
|
@ -11,6 +11,8 @@
|
||||||
#include <dwarfpp/frame.hpp>
|
#include <dwarfpp/frame.hpp>
|
||||||
#include <dwarfpp/root.hpp>
|
#include <dwarfpp/root.hpp>
|
||||||
|
|
||||||
|
#include "MemoryMap.hpp"
|
||||||
|
|
||||||
#define OF_WHAT_EXCEPTION(cl_name) \
|
#define OF_WHAT_EXCEPTION(cl_name) \
|
||||||
cl_name: public WhatException { \
|
cl_name: public WhatException { \
|
||||||
public:\
|
public:\
|
||||||
|
@ -19,10 +21,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
class DwarfInterpret {
|
class DwarfInterpret {
|
||||||
/** Singleton class holding a Dwarf interpret.
|
/** Singleton-ish 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
|
* There is an instance of this class per object path from the #MemoryMap.
|
||||||
* `acquire`.
|
* 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, …
|
public: // Types, sub-classes, …
|
||||||
|
@ -47,11 +51,18 @@ class DwarfInterpret {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// Thrown when `acquire` is called before `instanciate`
|
/** Thrown when trying to create an instance with an invalid ELF file:
|
||||||
class OF_WHAT_EXCEPTION(NotInstanciated);
|
* 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 `instanciate` is called twice
|
/** Thrown when trying to create an instance with an ELF file that
|
||||||
class OF_WHAT_EXCEPTION(AlreadyInstanciated);
|
* 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);
|
||||||
|
|
||||||
/** Thrown when trying to get the value of a Dwarf register (column)
|
/** Thrown when trying to get the value of a Dwarf register (column)
|
||||||
* that is not defined or has, somehow, no value at this PC. */
|
* that is not defined or has, somehow, no value at this PC. */
|
||||||
|
@ -80,21 +91,20 @@ class DwarfInterpret {
|
||||||
DwarfInterpret(DwarfInterpret const&) = delete;
|
DwarfInterpret(DwarfInterpret const&) = delete;
|
||||||
void operator=(DwarfInterpret const&) = delete;
|
void operator=(DwarfInterpret const&) = delete;
|
||||||
|
|
||||||
/** Acquire the instance of `DwarfInterpret`. The method #instanciate
|
/** Acquire the anonymous instance of DwarfInterpret, which works on
|
||||||
* must have been called beforehand.
|
* the empty memory range. Acts as a handle: passes every call to some
|
||||||
*
|
* other instance */
|
||||||
* \throws NotInstanciated whenever this condition is not met.
|
|
||||||
*/
|
|
||||||
static DwarfInterpret& acquire();
|
static DwarfInterpret& acquire();
|
||||||
|
|
||||||
/** Instanciate the instance of DwarfInterpret with the path to the
|
/** Acquire the relevant instance of #DwarfInterpret for the given
|
||||||
* program currently running (usually, argv[0])
|
* program counter. Instanciate it if needed. */
|
||||||
*
|
static DwarfInterpret& acquire(uintptr_t pc);
|
||||||
* \throws AlreadyInstanciated if this method is called twice. */
|
|
||||||
static DwarfInterpret& instanciate(const std::string& elf_path);
|
|
||||||
|
|
||||||
/// Returns the ELF machine number for this ELF
|
/** Get the Dwarf registers row at the given PC.
|
||||||
int get_elf_machine() const { return elf_machine; }
|
*
|
||||||
|
* \throws NotFound if for some reason, the Dwarf row cannot be
|
||||||
|
* accessed at this PC. */
|
||||||
|
const DwarfRow& dwarf_row_at(uintptr_t pc) const;
|
||||||
|
|
||||||
/** Retrieves the value pointed to by the given Dwarf register
|
/** Retrieves the value pointed to by the given Dwarf register
|
||||||
*
|
*
|
||||||
|
@ -123,7 +133,7 @@ class DwarfInterpret {
|
||||||
static uintptr_t get_current_pc();
|
static uintptr_t get_current_pc();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DwarfInterpret(const std::string& elf_path);
|
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;
|
reg_content_t get_cpu_register(int reg_id) const;
|
||||||
|
@ -132,16 +142,27 @@ class DwarfInterpret {
|
||||||
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;
|
||||||
const DwarfRow& dwarf_row_at(uintptr_t pc) const;
|
|
||||||
|
|
||||||
uintptr_t get_caller_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
|
private: // members
|
||||||
static std::unique_ptr<DwarfInterpret> instance;
|
static MemoryMap memory_map;
|
||||||
|
|
||||||
dwarf::core::root_die root_die;
|
// This map maps a `memory_map` memory region index to some instance of
|
||||||
int elf_machine;
|
// 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>;
|
friend class std::unique_ptr<DwarfInterpret>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include <DwarfInterpret.hpp>
|
#include <dwarfinterpret/DwarfInterpret.hpp>
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
@ -23,33 +23,80 @@ using namespace std;
|
||||||
using namespace dwarf;
|
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)
|
DwarfInterpret::DwarfInterpret(const MemoryMap::MapEntry& memory_object)
|
||||||
: root_die(fileno(std::ifstream(elf_path)))
|
: 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);
|
//std::ifstream file_in(elf_path);
|
||||||
//root_die = dwarf::core::root_die(fileno(file_in));
|
//root_die = dwarf::core::root_die(fileno(file_in));
|
||||||
// FIXME this ^ deserves some checks. But this looks tedious.
|
// FIXME this ^ deserves some checks. But this looks tedious.
|
||||||
|
|
||||||
GElf_Ehdr ehdr;
|
//GElf_Ehdr ehdr;
|
||||||
GElf_Ehdr *ret = gelf_getehdr(root_die.get_elf(), &ehdr);
|
//GElf_Ehdr *ret = gelf_getehdr(root_die.get_elf(), &ehdr);
|
||||||
assert(ret != 0);
|
//assert(ret != 0);
|
||||||
elf_machine = ehdr.e_machine;
|
//elf_machine = ehdr.e_machine;
|
||||||
}
|
}
|
||||||
|
|
||||||
DwarfInterpret& DwarfInterpret::acquire() {
|
DwarfInterpret& DwarfInterpret::acquire() {
|
||||||
if(DwarfInterpret::instance == nullptr)
|
auto find_it = DwarfInterpret::instances.find(-1);
|
||||||
throw NotInstanciated();
|
if(find_it == DwarfInterpret::instances.end()) {
|
||||||
return *(DwarfInterpret::instance);
|
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) {
|
DwarfInterpret& DwarfInterpret::acquire(uintptr_t pc) {
|
||||||
if(DwarfInterpret::instance != nullptr)
|
try {
|
||||||
throw AlreadyInstanciated();
|
size_t entry_id = DwarfInterpret::memory_map.id_of_address(pc);
|
||||||
DwarfInterpret::instance = std::unique_ptr<DwarfInterpret>(
|
auto find_it = DwarfInterpret::instances.find(entry_id);
|
||||||
new DwarfInterpret(elf_path));
|
if(find_it == DwarfInterpret::instances.end()) {
|
||||||
return *(DwarfInterpret::instance);
|
// 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(
|
DwarfInterpret::reg_content_t DwarfInterpret::interpret_dw_register(
|
||||||
|
@ -103,10 +150,18 @@ DwarfInterpret::reg_content_t DwarfInterpret::interpret_dw_register(
|
||||||
}
|
}
|
||||||
|
|
||||||
uintptr_t DwarfInterpret::get_return_address(uintptr_t cur_pc) const {
|
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 core::Cie& cie = *cie_at(cur_pc);
|
||||||
const DwarfRow& row = dwarf_row_at(cur_pc);
|
const DwarfRow& row = dwarf_row_at(cur_pc);
|
||||||
|
|
||||||
return interpret_dw_register(row, cie.get_return_address_register_rule());
|
uintptr_t translated_ra =
|
||||||
|
interpret_dw_register(row, cie.get_return_address_register_rule());
|
||||||
|
cerr << "Return address from 0x" << hex << cur_pc << ": "
|
||||||
|
<< "0x" << translated_ra << endl;
|
||||||
|
return translated_ra;
|
||||||
}
|
}
|
||||||
|
|
||||||
uintptr_t DwarfInterpret::get_self_return_address() const {
|
uintptr_t DwarfInterpret::get_self_return_address() const {
|
||||||
|
@ -176,10 +231,22 @@ const core::FrameSection::fde_iterator DwarfInterpret::fde_at(
|
||||||
uintptr_t pc
|
uintptr_t pc
|
||||||
) const
|
) const
|
||||||
{
|
{
|
||||||
const core::FrameSection& fs = root_die.get_frame_section();
|
DwarfInterpret* responsible = get_responsible_instance(pc);
|
||||||
const auto& fde_it = fs.find_fde_for_pc(pc);
|
if(responsible != nullptr)
|
||||||
if(fde_it == fs.fde_end())
|
return responsible->fde_at(pc);
|
||||||
throw NotFound(std::string("FDE at pc=") + std::to_string(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;
|
return fde_it;
|
||||||
}
|
}
|
||||||
|
@ -188,9 +255,13 @@ const core::FrameSection::cie_iterator DwarfInterpret::cie_at(
|
||||||
uintptr_t pc
|
uintptr_t pc
|
||||||
) const
|
) 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 core::Fde& fde = *fde_at(pc); // let NotFound escape if it occurs
|
||||||
const auto& cie_it = fde.find_cie();
|
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=")
|
throw NotFound(std::string("CIE matching FDE at pc=")
|
||||||
+ std::to_string(pc));
|
+ std::to_string(pc));
|
||||||
|
|
||||||
|
@ -201,12 +272,23 @@ const DwarfInterpret::DwarfRow& DwarfInterpret::dwarf_row_at(
|
||||||
uintptr_t pc
|
uintptr_t pc
|
||||||
) const
|
) const
|
||||||
{
|
{
|
||||||
|
DwarfInterpret* responsible = get_responsible_instance(pc);
|
||||||
|
if(responsible != nullptr)
|
||||||
|
return responsible->dwarf_row_at(pc);
|
||||||
|
|
||||||
const core::Fde& fde = *fde_at(pc);
|
const core::Fde& fde = *fde_at(pc);
|
||||||
|
uintptr_t translated_pc = pc - pc_offset;
|
||||||
auto decoded = fde.decode();
|
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())
|
if(row_it == decoded.rows.end())
|
||||||
throw NotFound(std::string("Dwarf row in this FDE at pc=")
|
throw NotFound(std::string("Dwarf row in this FDE at pc=")
|
||||||
+ std::to_string(pc));
|
+ std::to_string(pc));
|
||||||
|
|
||||||
return row_it->second;
|
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 <unistd.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <iostream>
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
void MemoryMap::MapEntry::dump(ostream& os) const {
|
void MemoryMap::MapEntry::dump(ostream& os) const {
|
||||||
os << "MapEntry: "
|
os << "MapEntry: "
|
||||||
<< hex << mem_region.begin << '-' << mem_region.end
|
<< hex << mem_region.begin << '-' << mem_region.end
|
||||||
<< " offset " << offset
|
<< " offset " << hex << offset
|
||||||
<< dec
|
<< dec
|
||||||
<< " - <" << pathname << ">"
|
<< " - <" << pathname << ">"
|
||||||
<< endl;
|
<< endl;
|
||||||
|
@ -24,23 +25,33 @@ MemoryMap::MemoryMap() {
|
||||||
|
|
||||||
string line;
|
string line;
|
||||||
while(getline(handle, line).good()) {
|
while(getline(handle, line).good()) {
|
||||||
|
cerr << "## " << line << endl;
|
||||||
istringstream lstream(line);
|
istringstream lstream(line);
|
||||||
MapEntry entry;
|
MapEntry entry;
|
||||||
uintptr_t discard;
|
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 >> std::hex;
|
||||||
lstream >> entry.mem_region.begin;
|
lstream >> entry.mem_region.begin; // beg_addr
|
||||||
lstream.ignore(1);
|
lstream.ignore(1); // ignore `-`
|
||||||
lstream >> entry.mem_region.end;
|
lstream >> entry.mem_region.end; // end_addr
|
||||||
lstream.ignore(6);
|
lstream.ignore(6); // ignore ` [perms] ` (perms is 4ch long)
|
||||||
lstream >> discard;
|
lstream >> entry.offset; // offset
|
||||||
lstream.ignore(7);
|
lstream.ignore(7); // ignore device
|
||||||
lstream >> entry.offset;
|
lstream >> discard; // ignore inode
|
||||||
lstream >> entry.pathname;
|
lstream >> entry.pathname; // pathname
|
||||||
|
|
||||||
rev_addr_map.insert(make_pair(entry.mem_region, map_entries.size()));
|
rev_addr_map.insert(make_pair(entry.mem_region, map_entries.size()));
|
||||||
map_entries.push_back(entry);
|
map_entries.push_back(entry);
|
||||||
}
|
}
|
||||||
|
cerr << endl;
|
||||||
|
|
||||||
handle.close();
|
handle.close();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue