From 13283d8e2a7e62f52607b5b89fec7d233f510493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Tue, 3 Apr 2018 12:23:15 +0200 Subject: [PATCH] Add MemoryMap to read /proc/[pid]/maps --- src/MemoryMap.cpp | 102 ++++++++++++++++++++++++++++++++++++++++++++++ src/MemoryMap.hpp | 96 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 198 insertions(+) create mode 100644 src/MemoryMap.cpp create mode 100644 src/MemoryMap.hpp diff --git a/src/MemoryMap.cpp b/src/MemoryMap.cpp new file mode 100644 index 0000000..e685be9 --- /dev/null +++ b/src/MemoryMap.cpp @@ -0,0 +1,102 @@ +#include "MemoryMap.hpp" + +#include +#include +#include +using namespace std; + +void MemoryMap::MapEntry::dump(ostream& os) const { + os << "MapEntry: " + << hex << mem_region.begin << '-' << mem_region.end + << " offset " << offset + << dec + << " - <" << pathname << ">" + << endl; +} + +MemoryMap::MemoryMap() { + int pid = getpid(); + ostringstream path; + path << "/proc/" << pid << "/maps"; + ifstream handle(path.str()); + if(handle.fail()) + throw ReadMapFailed(); + + string line; + while(getline(handle, line).good()) { + istringstream lstream(line); + MapEntry entry; + uintptr_t discard; + + 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; + + rev_addr_map.insert(make_pair(entry.mem_region, map_entries.size())); + map_entries.push_back(entry); + } + + handle.close(); +} + +const MemoryMap::MapEntry& MemoryMap::get_entry(size_t id) const { + try { + return map_entries.at(id); + } catch(const std::out_of_range& e) { + // Just here to remind one to throw a std::out_of_range if the + // underlying structure is changed at some point, and doesn't raise + // std::out_of_range anymore upon .at() + throw e; + } +} + +const MemoryMap::MapEntry& MemoryMap::operator[](size_t id) const { + return get_entry(id); +} + +size_t MemoryMap::id_of_address(uintptr_t addr) const { + /* I have found no nice operator< for MemoryRegions that work nice with + * both the internal consistency of map_entries and the search of the + * containing interval. + * We thus have two cases: + * - either we seek the range [a, b[ containing a, + * - or the range [a, b[ containing some c, a <= c < b. + * + * In the first case, if such a range exists, it is necessarily + * lower_bound(MemoryRegion(a, a)). + * In the second case, it is necessarily + * --lower_bound(MemoryRegion(c, c)). + */ + + MemoryRegion reg_index(addr, addr); + auto bound_it = rev_addr_map.lower_bound(reg_index); + + if(bound_it == rev_addr_map.end()) + throw std::out_of_range("No region containing this address"); + if(bound_it->first.contains(addr)) + return bound_it->second; + if(bound_it != rev_addr_map.begin()) { + --bound_it; + if(bound_it->first.contains(addr)) + return bound_it->second; + } + throw std::out_of_range("No region containing this address"); +} + +MemoryMap::iter_t MemoryMap::begin() const { + return map_entries.cbegin(); +} + +MemoryMap::iter_t MemoryMap::end() const { + return map_entries.cend(); +} + +size_t MemoryMap::size() const { + return map_entries.size(); +} diff --git a/src/MemoryMap.hpp b/src/MemoryMap.hpp new file mode 100644 index 0000000..dc3d215 --- /dev/null +++ b/src/MemoryMap.hpp @@ -0,0 +1,96 @@ +#pragma once + +/** MemoryMap: load, parse and make available the process' memory map + * + * This class allows the easy loading, parsing and reading of the process' + * memory map, that is, the correspondance between the various compile units + * and shared libraries' sections, and their mapping location in memory. + * + * This class is absolutely not portable and reads /proc/[pid]/maps. For more + * information, please read `man 5 proc`. + */ + +#include +#include +#include +#include +#include +#include + +class MemoryMap { + public: + /// Thrown when the constructor fails to read the map's data + class ReadMapFailed: public std::exception {}; + + struct MemoryRegion { + /** A contiguous memory region. */ + + MemoryRegion() = default; + MemoryRegion(uintptr_t begin, uintptr_t end) + : begin(begin), end(end) {} + + uintptr_t begin; ///< First address (incl.) in the region + uintptr_t end; ///< Past-the-end address for the region + + bool operator==(const MemoryRegion& oth) const { + return begin == oth.begin && end == oth.end; + } + + bool operator<(const MemoryRegion& oth) const { + if(begin < oth.begin) + return true; + return (begin == oth.begin && end < oth.end); + } + + /// Checks whether `addr` is in this region + bool contains(uintptr_t addr) const { + return begin <= addr && addr < end; + } + }; + + struct MapEntry { + std::string pathname; + MemoryRegion mem_region; + uintptr_t offset; + + /// Debug feature: dumps the MapEntry on the given stream + void dump(std::ostream& os) const; + }; + + /// An iterator to the map entries. The underlying type might change. + typedef std::vector::const_iterator iter_t; + + public: + /** Loads and constructs the map. + * + * \throws ReadMapFailed upon failure */ + MemoryMap(); + + /** Get the MapEntry for a given id + * + * \throws std::out_of_range if there is no such entry */ + const MapEntry& get_entry(size_t id) const; + + /// Synonymous for get_entry + const MapEntry& operator[](size_t id) const; + + /** Get the MapEntry id of the region containing addr + * + * \throws std::out_of_range if no such region is mapped */ + size_t id_of_address(uintptr_t addr) const; + + /// Get a constant iterator to the first map entry + iter_t begin() const; + + /// Get a constant iterator to a past-the-end iterator + iter_t end() const; + + /// Get the number of entries in the map + size_t size() const; + + private: + std::vector map_entries; + + /// Maps regions back to their id + std::map rev_addr_map; +};