From cd7c1635aa2838a91643ab699d0dcecc341697e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Wed, 4 Apr 2018 14:42:50 +0200 Subject: [PATCH] Plug MemoryMap into DwarfInterpret --- include/dwarfinterpret/DwarfInterpret.hpp | 71 +++++++----- src/DwarfInterpret.cpp | 130 ++++++++++++++++++---- src/MemoryMap.cpp | 31 ++++-- 3 files changed, 173 insertions(+), 59 deletions(-) diff --git a/include/dwarfinterpret/DwarfInterpret.hpp b/include/dwarfinterpret/DwarfInterpret.hpp index 5fb342e..105b953 100644 --- a/include/dwarfinterpret/DwarfInterpret.hpp +++ b/include/dwarfinterpret/DwarfInterpret.hpp @@ -11,6 +11,8 @@ #include #include +#include "MemoryMap.hpp" + #define OF_WHAT_EXCEPTION(cl_name) \ cl_name: public WhatException { \ public:\ @@ -19,10 +21,12 @@ } 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`. + /** 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, … @@ -47,11 +51,18 @@ class DwarfInterpret { }; - /// Thrown when `acquire` is called before `instanciate` - class OF_WHAT_EXCEPTION(NotInstanciated); + /** 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 `instanciate` is called twice - class OF_WHAT_EXCEPTION(AlreadyInstanciated); + /** 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); /** Thrown when trying to get the value of a Dwarf register (column) * that is not defined or has, somehow, no value at this PC. */ @@ -80,21 +91,20 @@ class DwarfInterpret { 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. - */ + /** 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(); - /** 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); + /** Acquire the relevant instance of #DwarfInterpret for the given + * program counter. Instanciate it if needed. */ + static DwarfInterpret& acquire(uintptr_t pc); - /// Returns the ELF machine number for this ELF - int get_elf_machine() const { return elf_machine; } + /** Get the Dwarf registers row at the given PC. + * + * \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 * @@ -123,7 +133,7 @@ class DwarfInterpret { static uintptr_t get_current_pc(); private: - DwarfInterpret(const std::string& elf_path); + DwarfInterpret(const MemoryMap::MapEntry& memory_object); DwarfRegister get_column(const DwarfRow& row, int column) const; reg_content_t get_cpu_register(int reg_id) const; @@ -132,16 +142,27 @@ class DwarfInterpret { 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; + /** 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 std::unique_ptr instance; + static MemoryMap memory_map; - dwarf::core::root_die root_die; - int elf_machine; + // 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 > instances; + + MemoryMap::MapEntry map_entry; + + std::unique_ptr root_die; + + uintptr_t pc_offset; friend class std::unique_ptr; }; diff --git a/src/DwarfInterpret.cpp b/src/DwarfInterpret.cpp index 8fcfc7b..59e3bdf 100644 --- a/src/DwarfInterpret.cpp +++ b/src/DwarfInterpret.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -23,33 +23,80 @@ using namespace std; using namespace dwarf; -std::unique_ptr DwarfInterpret::instance(nullptr); +MemoryMap DwarfInterpret::memory_map; +map > DwarfInterpret::instances; -DwarfInterpret::DwarfInterpret(const std::string& elf_path) - : root_die(fileno(std::ifstream(elf_path))) +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( + 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; + //GElf_Ehdr ehdr; + //GElf_Ehdr *ret = gelf_getehdr(root_die.get_elf(), &ehdr); + //assert(ret != 0); + //elf_machine = ehdr.e_machine; } DwarfInterpret& DwarfInterpret::acquire() { - if(DwarfInterpret::instance == nullptr) - throw NotInstanciated(); - return *(DwarfInterpret::instance); + 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(instance))); + return *instance; + } + return *(find_it->second); } -DwarfInterpret& DwarfInterpret::instanciate(const std::string& elf_path) { - if(DwarfInterpret::instance != nullptr) - throw AlreadyInstanciated(); - DwarfInterpret::instance = std::unique_ptr( - new DwarfInterpret(elf_path)); - return *(DwarfInterpret::instance); +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(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( @@ -103,10 +150,18 @@ DwarfInterpret::reg_content_t DwarfInterpret::interpret_dw_register( } 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); - 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 { @@ -176,10 +231,22 @@ const core::FrameSection::fde_iterator DwarfInterpret::fde_at( uintptr_t pc ) const { - const core::FrameSection& fs = root_die.get_frame_section(); - const auto& fde_it = fs.find_fde_for_pc(pc); - if(fde_it == fs.fde_end()) - throw NotFound(std::string("FDE at pc=") + std::to_string(pc)); + 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; } @@ -188,9 +255,13 @@ 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…? + 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)); @@ -201,12 +272,23 @@ 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(pc); + 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); +} diff --git a/src/MemoryMap.cpp b/src/MemoryMap.cpp index e685be9..ba36e53 100644 --- a/src/MemoryMap.cpp +++ b/src/MemoryMap.cpp @@ -1,14 +1,15 @@ -#include "MemoryMap.hpp" +#include #include #include #include +#include using namespace std; void MemoryMap::MapEntry::dump(ostream& os) const { os << "MapEntry: " << hex << mem_region.begin << '-' << mem_region.end - << " offset " << offset + << " offset " << hex << offset << dec << " - <" << pathname << ">" << endl; @@ -24,23 +25,33 @@ MemoryMap::MemoryMap() { string line; while(getline(handle, line).good()) { + cerr << "## " << line << endl; istringstream lstream(line); MapEntry entry; 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 >> entry.mem_region.begin; - lstream.ignore(1); - lstream >> entry.mem_region.end; - lstream.ignore(6); - lstream >> discard; - lstream.ignore(7); - lstream >> entry.offset; - lstream >> entry.pathname; + lstream >> entry.mem_region.begin; // beg_addr + lstream.ignore(1); // ignore `-` + lstream >> entry.mem_region.end; // end_addr + lstream.ignore(6); // ignore ` [perms] ` (perms is 4ch long) + lstream >> entry.offset; // offset + lstream.ignore(7); // ignore device + lstream >> discard; // ignore inode + lstream >> entry.pathname; // pathname rev_addr_map.insert(make_pair(entry.mem_region, map_entries.size())); map_entries.push_back(entry); } + cerr << endl; handle.close(); }