dwarfinterpret/src/DwarfInterpret.cpp

295 lines
9.4 KiB
C++

#include <dwarfinterpret/DwarfInterpret.hpp>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <vector>
#include <set>
#include <fileno.hpp>
#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 <gelf.h>
#include <ucontext.h>
#include "register_map.hpp"
using namespace std;
using namespace dwarf;
MemoryMap DwarfInterpret::memory_map;
map<int, unique_ptr<DwarfInterpret> > DwarfInterpret::instances;
DwarfInterpret::DwarfInterpret(const MemoryMap::MapEntry& memory_object)
: 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);
//root_die = dwarf::core::root_die(fileno(file_in));
// FIXME this ^ deserves some checks. But this looks tedious.
//GElf_Ehdr ehdr;
//GElf_Ehdr *ret = gelf_getehdr(root_die.get_elf(), &ehdr);
//assert(ret != 0);
//elf_machine = ehdr.e_machine;
}
DwarfInterpret& DwarfInterpret::acquire() {
auto find_it = DwarfInterpret::instances.find(-1);
if(find_it == DwarfInterpret::instances.end()) {
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::acquire(uintptr_t pc) {
try {
size_t entry_id = DwarfInterpret::memory_map.id_of_address(pc);
auto find_it = DwarfInterpret::instances.find(entry_id);
if(find_it == DwarfInterpret::instances.end()) {
// 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(
const DwarfInterpret::DwarfRow& row,
const DwarfInterpret::DwarfRegister& reg
) const
{
switch(reg.k) {
case core::FrameSection::register_def::INDETERMINATE:
case core::FrameSection::register_def::UNDEFINED:
throw ValuelessRegister();
case core::FrameSection::register_def::REGISTER: {
ostringstream exprs;
int dwarf_regnum = reg.register_plus_offset_r().first;
assert(dwarf_regnum != DW_FRAME_CFA_COL3);
int cpu_regnum = REG_DWARF_TO_UCONTEXT[dwarf_regnum];
reg_content_t* cpu_content =
(reg_content_t*) get_cpu_register(cpu_regnum);
reg_content_t* addr =
cpu_content + reg.register_plus_offset_r().second;
return *addr;
} break;
case core::FrameSection::register_def::SAVED_AT_OFFSET_FROM_CFA: {
reg_content_t* cfa_loc =
(reg_content_t*) interpret_dw_register(row, DW_FRAME_CFA_COL3);
reg_content_t* addr = cfa_loc;
return *addr;
} break;
case core::FrameSection::register_def::SAVED_AT_EXPR:
throw NotImplemented();
break;
case core::FrameSection::register_def::VAL_IS_OFFSET_FROM_CFA:
case core::FrameSection::register_def::VAL_OF_EXPR:
default:
throw NotImplemented();
break;
}
}
DwarfInterpret::reg_content_t DwarfInterpret::interpret_dw_register(
const DwarfInterpret::DwarfRow& row,
int reg_id
) const
{
return interpret_dw_register(row, get_column(row, reg_id));
}
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);
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 {
// 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);
}
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>
static typename std::set<std::pair<int, Key> >::const_iterator find_column(
const std::set<std::pair<int, Key> >& set,
int key)
{
if(set.empty())
return set.end();
Key some_key_val = set.begin()->second;
auto it = set.upper_bound(make_pair(key - 1, some_key_val));
if(it == set.end())
return set.end();
while(it != set.end() && it->first != key)
++it;
if(it == set.end() || it->first != key)
return set.end();
return it;
}
DwarfInterpret::DwarfRegister DwarfInterpret::get_column(
const DwarfInterpret::DwarfRow& row,
int column
) const
{
auto it = find_column<DwarfRegister>(row, column);
if(it == row.end())
throw std::out_of_range("No such column");
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(
uintptr_t pc
) const
{
DwarfInterpret* responsible = get_responsible_instance(pc);
if(responsible != nullptr)
return responsible->fde_at(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;
}
const core::FrameSection::cie_iterator DwarfInterpret::cie_at(
uintptr_t pc
) 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 auto& cie_it = fde.find_cie();
if(cie_it == root_die->get_frame_section().cie_end()) //Should not happen…?
throw NotFound(std::string("CIE matching FDE at pc=")
+ std::to_string(pc));
return cie_it;
}
const DwarfInterpret::DwarfRow& DwarfInterpret::dwarf_row_at(
uintptr_t pc
) const
{
DwarfInterpret* responsible = get_responsible_instance(pc);
if(responsible != nullptr)
return responsible->dwarf_row_at(pc);
const core::Fde& fde = *fde_at(pc);
uintptr_t translated_pc = pc - pc_offset;
auto decoded = fde.decode();
const auto& row_it = decoded.rows.find(translated_pc);
if(row_it == decoded.rows.end())
throw NotFound(std::string("Dwarf row in this FDE at pc=")
+ std::to_string(pc));
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);
}