diff --git a/src/binary/module_base.hpp b/src/binary/module_base.hpp new file mode 100644 index 0000000..2dfccc2 --- /dev/null +++ b/src/binary/module_base.hpp @@ -0,0 +1,103 @@ +#ifndef IMAGE_MODULE_BASE_HPP +#define IMAGE_MODULE_BASE_HPP + +#include "../utils/common.hpp" +#include "../utils/utils.hpp" + +#include +#include +#include +#include + +#if IS_LINUX || IS_APPLE + #include + #include + #if IS_APPLE + #include "mach-o.hpp" + #else + #include "elf.hpp" + #endif +#elif IS_WINDOWS + #include + #include "pe.hpp" +#endif + +namespace cpptrace { +namespace detail { + #if IS_LINUX + inline std::uintptr_t get_module_image_base(const std::string& object_path) { + static std::mutex mutex; + std::lock_guard lock(mutex); + static std::unordered_map cache; + auto it = cache.find(object_path); + if(it == cache.end()) { + // arguably it'd be better to release the lock while computing this, but also arguably it's good to not + // have two threads try to do the same computation + auto base = elf_get_module_image_base(object_path); + cache.insert(it, {object_path, base}); + return base; + } else { + return it->second; + } + } + #elif IS_APPLE + inline std::uintptr_t get_module_image_base(const std::string& object_path) { + // We have to parse the Mach-O to find the offset of the text section..... + // I don't know how addresses are handled if there is more than one __TEXT load command. I'm assuming for + // now that there is only one, and I'm using only the first section entry within that load command. + static std::mutex mutex; + std::lock_guard lock(mutex); + static std::unordered_map cache; + auto it = cache.find(object_path); + if(it == cache.end()) { + // arguably it'd be better to release the lock while computing this, but also arguably it's good to not + // have two threads try to do the same computation + auto base = mach_o(object_path).get_text_vmaddr(); + cache.insert(it, {object_path, base}); + return base; + } else { + return it->second; + } + } + #else // Windows + inline std::string get_module_name(HMODULE handle) { + static std::mutex mutex; + std::lock_guard lock(mutex); + static std::unordered_map cache; + auto it = cache.find(handle); + if(it == cache.end()) { + char path[MAX_PATH]; + if(GetModuleFileNameA(handle, path, sizeof(path))) { + ///std::fprintf(stderr, "path: %s base: %p\n", path, handle); + cache.insert(it, {handle, path}); + return path; + } else { + std::fprintf(stderr, "%s\n", std::system_error(GetLastError(), std::system_category()).what()); + cache.insert(it, {handle, ""}); + return ""; + } + } else { + return it->second; + } + } + + inline std::uintptr_t get_module_image_base(const std::string& object_path) { + static std::mutex mutex; + std::lock_guard lock(mutex); + static std::unordered_map cache; + auto it = cache.find(object_path); + if(it == cache.end()) { + // arguably it'd be better to release the lock while computing this, but also arguably it's good to not + // have two threads try to do the same computation + auto base = pe_get_module_image_base(object_path); + cache.insert(it, {object_path, base}); + return base; + } else { + return it->second; + } + } + #endif +} +} + +#endif diff --git a/src/binary/object.hpp b/src/binary/object.hpp index eeb9333..a4cf17f 100644 --- a/src/binary/object.hpp +++ b/src/binary/object.hpp @@ -3,6 +3,7 @@ #include "../utils/common.hpp" #include "../utils/utils.hpp" +#include "module_base.hpp" #include #include @@ -12,58 +13,16 @@ #if IS_LINUX || IS_APPLE #include #include - #if IS_APPLE - #include "mach-o.hpp" - #else - #include "elf.hpp" - #endif #ifdef CPPTRACE_HAS_DL_FIND_OBJECT #include #endif #elif IS_WINDOWS #include - #include "pe.hpp" #endif namespace cpptrace { namespace detail { #if IS_LINUX || IS_APPLE - #if !IS_APPLE - inline std::uintptr_t get_module_image_base(const std::string& object_path) { - static std::mutex mutex; - std::lock_guard lock(mutex); - static std::unordered_map cache; - auto it = cache.find(object_path); - if(it == cache.end()) { - // arguably it'd be better to release the lock while computing this, but also arguably it's good to not - // have two threads try to do the same computation - auto base = elf_get_module_image_base(object_path); - cache.insert(it, {object_path, base}); - return base; - } else { - return it->second; - } - } - #else - inline std::uintptr_t get_module_image_base(const std::string& object_path) { - // We have to parse the Mach-O to find the offset of the text section..... - // I don't know how addresses are handled if there is more than one __TEXT load command. I'm assuming for - // now that there is only one, and I'm using only the first section entry within that load command. - static std::mutex mutex; - std::lock_guard lock(mutex); - static std::unordered_map cache; - auto it = cache.find(object_path); - if(it == cache.end()) { - // arguably it'd be better to release the lock while computing this, but also arguably it's good to not - // have two threads try to do the same computation - auto base = mach_o(object_path).get_text_vmaddr(); - cache.insert(it, {object_path, base}); - return base; - } else { - return it->second; - } - } - #endif #ifdef CPPTRACE_HAS_DL_FIND_OBJECT inline object_frame get_frame_object_info(frame_ptr address) { // Use _dl_find_object when we can, it's orders of magnitude faster @@ -109,43 +68,6 @@ namespace detail { } #endif #else - inline std::string get_module_name(HMODULE handle) { - static std::mutex mutex; - std::lock_guard lock(mutex); - static std::unordered_map cache; - auto it = cache.find(handle); - if(it == cache.end()) { - char path[MAX_PATH]; - if(GetModuleFileNameA(handle, path, sizeof(path))) { - ///std::fprintf(stderr, "path: %s base: %p\n", path, handle); - cache.insert(it, {handle, path}); - return path; - } else { - std::fprintf(stderr, "%s\n", std::system_error(GetLastError(), std::system_category()).what()); - cache.insert(it, {handle, ""}); - return ""; - } - } else { - return it->second; - } - } - - inline std::uintptr_t get_module_image_base(const std::string& object_path) { - static std::mutex mutex; - std::lock_guard lock(mutex); - static std::unordered_map cache; - auto it = cache.find(object_path); - if(it == cache.end()) { - // arguably it'd be better to release the lock while computing this, but also arguably it's good to not - // have two threads try to do the same computation - auto base = pe_get_module_image_base(object_path); - cache.insert(it, {object_path, base}); - return base; - } else { - return it->second; - } - } - inline object_frame get_frame_object_info(frame_ptr address) { object_frame frame; frame.raw_address = address; @@ -159,8 +81,8 @@ namespace detail { )) { frame.object_path = get_module_name(handle); frame.object_address = address - - reinterpret_cast(handle) - + get_module_image_base(frame.object_path); + - reinterpret_cast(handle) + + get_module_image_base(frame.object_path); } else { std::fprintf(stderr, "%s\n", std::system_error(GetLastError(), std::system_category()).what()); }