diff options
Diffstat (limited to 'toolkit/crashreporter/breakpad-client/mac/handler/dynamic_images.h')
-rw-r--r-- | toolkit/crashreporter/breakpad-client/mac/handler/dynamic_images.h | 386 |
1 files changed, 386 insertions, 0 deletions
diff --git a/toolkit/crashreporter/breakpad-client/mac/handler/dynamic_images.h b/toolkit/crashreporter/breakpad-client/mac/handler/dynamic_images.h new file mode 100644 index 0000000000..e225c00b88 --- /dev/null +++ b/toolkit/crashreporter/breakpad-client/mac/handler/dynamic_images.h @@ -0,0 +1,386 @@ +// Copyright (c) 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// dynamic_images.h +// +// Implements most of the function of the dyld API, but allowing an +// arbitrary task to be introspected, unlike the dyld API which +// only allows operation on the current task. The current implementation +// is limited to use by 32-bit tasks. + +#ifndef CLIENT_MAC_HANDLER_DYNAMIC_IMAGES_H__ +#define CLIENT_MAC_HANDLER_DYNAMIC_IMAGES_H__ + +#include <mach/mach.h> +#include <mach-o/dyld.h> +#include <mach-o/loader.h> +#include <sys/types.h> + +#include <string> +#include <vector> + +#include "mach_vm_compat.h" + +namespace google_breakpad { + +using std::string; +using std::vector; + +//============================================================================== +// The memory layout of this struct matches the dyld_image_info struct +// defined in "dyld_gdb.h" in the darwin source. +typedef struct dyld_image_info32 { + uint32_t load_address_; // struct mach_header* + uint32_t file_path_; // char* + uint32_t file_mod_date_; +} dyld_image_info32; + +typedef struct dyld_image_info64 { + uint64_t load_address_; // struct mach_header* + uint64_t file_path_; // char* + uint64_t file_mod_date_; +} dyld_image_info64; + +//============================================================================== +// This is as defined in "dyld_gdb.h" in the darwin source. +// _dyld_all_image_infos (in dyld) is a structure of this type +// which will be used to determine which dynamic code has been loaded. +typedef struct dyld_all_image_infos32 { + uint32_t version; // == 1 in Mac OS X 10.4 + uint32_t infoArrayCount; + uint32_t infoArray; // const struct dyld_image_info* + uint32_t notification; + bool processDetachedFromSharedRegion; + // Only in version 2 (Mac OS X 10.6, iPhoneOS 2.0) and later + const struct mach_header* dyldImageLoadAddress; + uint32_t padding[14]; + // Only in version 12 (Mac OS X 10.7, iOS 4.3) and later + uint32_t sharedCacheSlide; + uint32_t padding1[6]; + // Only in version 15 (macOS 10.12, iOS 10.0) and later + const char* dyldPath; +} dyld_all_image_infos32; + +typedef struct dyld_all_image_infos64 { + uint32_t version; // == 1 in Mac OS X 10.4 + uint32_t infoArrayCount; + uint64_t infoArray; // const struct dyld_image_info* + uint64_t notification; + bool processDetachedFromSharedRegion; + // Only in version 2 (Mac OS X 10.6, iPhoneOS 2.0) and later + const struct mach_header_64* dyldImageLoadAddress; + uint64_t padding[14]; + // Only in version 12 (Mac OS X 10.7, iOS 4.3) and later + uint64_t sharedCacheSlide; + uint64_t padding1[4]; + // Only in version 15 (macOS 10.12, iOS 10.0) and later + const char* dyldPath; +} dyld_all_image_infos64; + +// some typedefs to isolate 64/32 bit differences +#ifdef __LP64__ +typedef mach_header_64 breakpad_mach_header; +typedef segment_command_64 breakpad_mach_segment_command; +#else +typedef mach_header breakpad_mach_header; +typedef segment_command breakpad_mach_segment_command; +#endif + +// Bit in mach_header.flags that indicates whether or not the image is in the +// dyld shared cache. The dyld shared cache is a single image into which +// commonly used system dylibs and frameworks are incorporated. dyld maps it +// into every process at load time. The component images all have the same +// slide. +#define MH_SHAREDCACHE 0x80000000 + +// Helper functions to deal with 32-bit/64-bit Mach-O differences. +class DynamicImage; +template<typename MachBits> +bool FindTextSection(DynamicImage& image); + +template<typename MachBits> +uint32_t GetFileTypeFromHeader(DynamicImage& image); + +//============================================================================== +// Represents a single dynamically loaded mach-o image +class DynamicImage { + public: + DynamicImage(uint8_t *header, // data is copied + size_t header_size, // includes load commands + uint64_t load_address, + string file_path, + uintptr_t image_mod_date, + mach_port_t task, + cpu_type_t cpu_type, + cpu_subtype_t cpu_subtype, + ptrdiff_t shared_cache_slide, + bool is_dyld) + : header_(header, header + header_size), + header_size_(header_size), + load_address_(load_address), + vmaddr_(0), + vmsize_(0), + slide_(0), + crash_info_(), + version_(0), + file_path_(file_path), + file_mod_date_(image_mod_date), + is_dyld_(is_dyld), + task_(task), + cpu_type_(cpu_type), + cpu_subtype_(cpu_subtype), + shared_cache_slide_(shared_cache_slide) { + CalculateMemoryAndVersionInfo(); + } + + // Size of mach_header plus load commands + size_t GetHeaderSize() const {return header_.size();} + + // Full path to mach-o binary + string GetFilePath() {return file_path_;} + + uint64_t GetModDate() const {return file_mod_date_;} + + // Actual address where the image was loaded + uint64_t GetLoadAddress() const {return load_address_;} + + // Address where the image should be loaded + mach_vm_address_t GetVMAddr() const {return vmaddr_;} + + bool GetInDyldSharedCache() + {return (shared_cache_slide_ && (slide_ == shared_cache_slide_));} + + bool GetIsDyld() {return is_dyld_;} + + // Difference between GetLoadAddress() and GetVMAddr() + ptrdiff_t GetVMAddrSlide() const {return slide_;} + + // Size of the image + mach_vm_size_t GetVMSize() const {return vmsize_;} + + // Returns the address of the locally cached __DATA,__crash_info section. + // The vector will be empty if the image doesn't have a __crash_info + // section. But even if the vector isn't empty, its contents may be "empty" + // of useful data (see definition of crashreporter_annotations_t in + // mach_vm_compat.h). + mach_vm_address_t GetCrashInfo() const { + return reinterpret_cast<mach_vm_address_t>(&crash_info_[0]); + } + + // Size of the locally cached __DATA,__crash_info section. This will be zero + // if the vector is empty. But even if it's non-zero, the __crash_info + // section of which it's a copy may be empty of useful data. + size_t GetCrashInfoSize() const {return crash_info_.size();} + + // Task owning this loaded image + mach_port_t GetTask() {return task_;} + + // CPU type of the task and the image + cpu_type_t GetCPUType() {return cpu_type_;} + + // CPU subtype of the image + cpu_type_t GetCPUSubtype() {return cpu_subtype_;} + + // filetype from the Mach-O header. + uint32_t GetFileType(); + + // Return true if the task is a 64-bit architecture. + bool Is64Bit() { return (GetCPUType() & CPU_ARCH_ABI64) == CPU_ARCH_ABI64; } + + uint32_t GetVersion() {return version_;} + // For sorting + bool operator<(const DynamicImage &inInfo) { + return GetLoadAddress() < inInfo.GetLoadAddress(); + } + + // Sanity checking + bool IsValid() {return GetVMSize() != 0;} + + private: + DynamicImage(const DynamicImage &); + DynamicImage &operator=(const DynamicImage &); + + friend class DynamicImages; + template<typename MachBits> + friend bool FindTextSection(DynamicImage& image); + template<typename MachBits> + friend uint32_t GetFileTypeFromHeader(DynamicImage& image); + + // Initializes vmaddr_, vmsize_, and slide_ + void CalculateMemoryAndVersionInfo(); + + const vector<uint8_t> header_; // our local copy of the header + size_t header_size_; // mach_header plus load commands + uint64_t load_address_; // base address image is mapped into + mach_vm_address_t vmaddr_; + mach_vm_size_t vmsize_; + ptrdiff_t slide_; + vector<uint8_t> crash_info_; + uint32_t version_; // Dylib version + string file_path_; // path dyld used to load the image + uintptr_t file_mod_date_; // time_t of image file + bool is_dyld_; // Is image file dyld itself? + + mach_port_t task_; + cpu_type_t cpu_type_; // CPU type of task_ and image + cpu_subtype_t cpu_subtype_; // CPU subtype of image + ptrdiff_t shared_cache_slide_; // Task's shared cache slide +}; + +//============================================================================== +// DynamicImageRef is just a simple wrapper for a pointer to +// DynamicImage. The reason we use it instead of a simple typedef is so +// that we can use stl::sort() on a vector of DynamicImageRefs +// and simple class pointers can't implement operator<(). +// +class DynamicImageRef { + public: + explicit DynamicImageRef(DynamicImage *inP) : p(inP) {} + // The copy constructor is required by STL + DynamicImageRef(const DynamicImageRef &inRef) : p(inRef.p) {} + + bool operator<(const DynamicImageRef &inRef) const { + return (*const_cast<DynamicImageRef*>(this)->p) + < (*const_cast<DynamicImageRef&>(inRef).p); + } + + bool operator==(const DynamicImageRef &inInfo) const { + return (*const_cast<DynamicImageRef*>(this)->p).GetLoadAddress() == + (*const_cast<DynamicImageRef&>(inInfo)).GetLoadAddress(); + } + + // Be just like DynamicImage* + DynamicImage *operator->() {return p;} + operator DynamicImage*() {return p;} + + private: + DynamicImage *p; +}; + +// Helper function to deal with 32-bit/64-bit Mach-O differences. +class DynamicImages; +template<typename MachBits> +void ReadOneImageInfo(DynamicImages& images, uint64_t image_address, + uint64_t file_path_address, uint64_t file_mod_date, + uint64_t shared_cache_slide, bool is_dyld); +template<typename MachBits> +void ReadImageInfo(DynamicImages& images, uint64_t image_list_address); + +//============================================================================== +// An object of type DynamicImages may be created to allow introspection of +// an arbitrary task's dynamically loaded mach-o binaries. This makes the +// assumption that the current task has send rights to the target task. +class DynamicImages { + public: + explicit DynamicImages(mach_port_t task); + + ~DynamicImages() { + for (int i = 0; i < GetImageCount(); ++i) { + delete image_list_[i]; + } + } + + // Returns the number of dynamically loaded mach-o images. + int GetImageCount() const {return static_cast<int>(image_list_.size());} + + // Returns an individual image. + DynamicImage *GetImage(int i) { + if (i < (int)image_list_.size()) { + return image_list_[i]; + } + return NULL; + } + + // Returns the image corresponding to the main executable. + DynamicImage *GetExecutableImage(); + int GetExecutableImageIndex(); + + // Returns the task which we're looking at. + mach_port_t GetTask() const {return task_;} + + // CPU type of the task + cpu_type_t GetCPUType() {return cpu_type_;} + + // Return true if the task is a 64-bit architecture. + bool Is64Bit() { return (GetCPUType() & CPU_ARCH_ABI64) == CPU_ARCH_ABI64; } + + // Determine the CPU type of the task being dumped. + static cpu_type_t DetermineTaskCPUType(task_t task); + + // Get the native CPU type of this task. + static cpu_type_t GetNativeCPUType() { +#if defined(__i386__) + return CPU_TYPE_I386; +#elif defined(__x86_64__) + return CPU_TYPE_X86_64; +#elif defined(__ppc__) + return CPU_TYPE_POWERPC; +#elif defined(__ppc64__) + return CPU_TYPE_POWERPC64; +#elif defined(__arm__) + return CPU_TYPE_ARM; +#elif defined(__aarch64__) + return CPU_TYPE_ARM64; +#else +#error "GetNativeCPUType not implemented for this architecture" +#endif + } + + private: + template<typename MachBits> + friend void ReadOneImageInfo(DynamicImages& images, uint64_t image_address, + uint64_t file_path_address, uint64_t file_mod_date, + uint64_t shared_cache_slide, bool is_dyld); + template<typename MachBits> + friend void ReadImageInfo(DynamicImages& images, uint64_t image_list_address); + + bool IsOurTask() {return task_ == mach_task_self();} + + // Initialization + void ReadImageInfoForTask(); + uint64_t GetDyldAllImageInfosPointer(); + + mach_port_t task_; + cpu_type_t cpu_type_; // CPU type of task_ + vector<DynamicImageRef> image_list_; +}; + +// Fill bytes with the contents of memory at a particular +// location in another task. +kern_return_t ReadTaskMemory(task_port_t target_task, + const uint64_t address, + size_t length, + vector<uint8_t> &bytes); + +std::string ReadTaskString(task_port_t target_task, + const uint64_t address); + +} // namespace google_breakpad + +#endif // CLIENT_MAC_HANDLER_DYNAMIC_IMAGES_H__ |