diff options
author | ted.mielczarek@gmail.com <ted.mielczarek@gmail.com@4c0a9323-5329-0410-9bdc-e9ce6186880e> | 2010-12-15 21:55:56 +0000 |
---|---|---|
committer | ted.mielczarek@gmail.com <ted.mielczarek@gmail.com@4c0a9323-5329-0410-9bdc-e9ce6186880e> | 2010-12-15 21:55:56 +0000 |
commit | 0344a368deac6abaa280a298bcea9bb00a90df3f (patch) | |
tree | 48245f419549c51163ea1cffdc9ba35989001485 /src/client/mac/handler/dynamic_images.cc | |
parent | Allow writing on-request minidumps with an exception stream (diff) | |
download | breakpad-0344a368deac6abaa280a298bcea9bb00a90df3f.tar.xz |
Allow out-of-process minidump generation to work on processes of a different CPU architecture
R=mark at http://breakpad.appspot.com/241001
git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@746 4c0a9323-5329-0410-9bdc-e9ce6186880e
Diffstat (limited to 'src/client/mac/handler/dynamic_images.cc')
-rw-r--r-- | src/client/mac/handler/dynamic_images.cc | 502 |
1 files changed, 285 insertions, 217 deletions
diff --git a/src/client/mac/handler/dynamic_images.cc b/src/client/mac/handler/dynamic_images.cc index 1d5f1f9b..035afe7e 100644 --- a/src/client/mac/handler/dynamic_images.cc +++ b/src/client/mac/handler/dynamic_images.cc @@ -27,6 +27,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#include "client/mac/handler/dynamic_images.h" + extern "C" { // needed to compile on Leopard #include <mach-o/nlist.h> #include <stdlib.h> @@ -37,11 +39,17 @@ extern "C" { // needed to compile on Leopard #include <assert.h> #include <dlfcn.h> #include <mach/mach_vm.h> +#include <sys/sysctl.h> + #include <algorithm> -#include "client/mac/handler/dynamic_images.h" +#include <string> +#include <vector> namespace google_breakpad { +using std::string; +using std::vector; + //============================================================================== // Returns the size of the memory region containing |address| and the // number of bytes from |address| to the end of the region. @@ -51,7 +59,7 @@ namespace google_breakpad { // straddle two vm regions. // static mach_vm_size_t GetMemoryRegionSize(task_port_t target_task, - const void* address, + const uint64_t address, mach_vm_size_t *size_to_end) { mach_vm_address_t region_base = (mach_vm_address_t)address; mach_vm_size_t region_size; @@ -116,8 +124,8 @@ static mach_vm_size_t GetMemoryRegionSize(task_port_t target_task, // // Warning! This will not read any strings longer than kMaxStringLength-1 // -static void* ReadTaskString(task_port_t target_task, - const void* address) { +static string ReadTaskString(task_port_t target_task, + const uint64_t address) { // The problem is we don't know how much to read until we know how long // the string is. And we don't know how long the string is, until we've read // the memory! So, we'll try to read kMaxStringLength bytes @@ -129,147 +137,170 @@ static void* ReadTaskString(task_port_t target_task, mach_vm_size_t size_to_read = size_to_end > kMaxStringLength ? kMaxStringLength : size_to_end; - kern_return_t kr; - return ReadTaskMemory(target_task, address, (size_t)size_to_read, &kr); + vector<uint8_t> bytes; + if (ReadTaskMemory(target_task, address, (size_t)size_to_read, bytes) != + KERN_SUCCESS) + return string(); + + return string(reinterpret_cast<const char*>(&bytes[0])); } - return NULL; + return string(); } //============================================================================== -// Reads an address range from another task. A block of memory is malloced -// and should be freed by the caller. -void* ReadTaskMemory(task_port_t target_task, - const void* address, - size_t length, - kern_return_t *kr) { - void* result = NULL; +// Reads an address range from another task. The bytes read will be returned +// in bytes, which will be resized as necessary. +kern_return_t ReadTaskMemory(task_port_t target_task, + const uint64_t address, + size_t length, + vector<uint8_t> &bytes) { int systemPageSize = getpagesize(); // use the negative of the page size for the mask to find the page address - mach_vm_address_t page_address = - reinterpret_cast<mach_vm_address_t>(address) & (-systemPageSize); + mach_vm_address_t page_address = address & (-systemPageSize); mach_vm_address_t last_page_address = - (reinterpret_cast<mach_vm_address_t>(address) + length + - (systemPageSize - 1)) & (-systemPageSize); + (address + length + (systemPageSize - 1)) & (-systemPageSize); mach_vm_size_t page_size = last_page_address - page_address; uint8_t* local_start; uint32_t local_length; - kern_return_t r; - - r = mach_vm_read(target_task, - page_address, - page_size, - reinterpret_cast<vm_offset_t*>(&local_start), - &local_length); - - - if (kr != NULL) { - *kr = r; - } - - if (r == KERN_SUCCESS) { - result = malloc(length); - if (result != NULL) { - memcpy(result, - &local_start[(mach_vm_address_t)address - page_address], - length); - } - mach_vm_deallocate(mach_task_self(), (uintptr_t)local_start, local_length); - } - - return result; + kern_return_t r = mach_vm_read(target_task, + page_address, + page_size, + reinterpret_cast<vm_offset_t*>(&local_start), + &local_length); + + if (r != KERN_SUCCESS) + return r; + + bytes.resize(length); + memcpy(&bytes[0], + &local_start[(mach_vm_address_t)address - page_address], + length); + mach_vm_deallocate(mach_task_self(), (uintptr_t)local_start, local_length); + return KERN_SUCCESS; } #pragma mark - //============================================================================== -// Initializes vmaddr_, vmsize_, and slide_ -void DynamicImage::CalculateMemoryAndVersionInfo() { - breakpad_mach_header *header = GetMachHeader(); - - // unless we can process the header, ensure that calls to - // IsValid() will return false - vmaddr_ = 0; - vmsize_ = 0; - slide_ = 0; - version_ = 0; - - bool foundTextSection = false; - bool foundDylibIDCommand = false; +// Traits structs for specializing function templates to handle +// 32-bit/64-bit Mach-O files. +struct MachO32 { + typedef mach_header mach_header_type; + typedef segment_command mach_segment_command_type; + typedef dyld_image_info32 dyld_image_info; + typedef dyld_all_image_infos32 dyld_all_image_infos; + typedef struct nlist nlist_type; + static const uint32_t magic = MH_MAGIC; + static const uint32_t segment_load_command = LC_SEGMENT; +}; + +struct MachO64 { + typedef mach_header_64 mach_header_type; + typedef segment_command_64 mach_segment_command_type; + typedef dyld_image_info64 dyld_image_info; + typedef dyld_all_image_infos64 dyld_all_image_infos; + typedef struct nlist_64 nlist_type; + static const uint32_t magic = MH_MAGIC_64; + static const uint32_t segment_load_command = LC_SEGMENT_64; +}; + +template<typename MachBits> +bool FindTextSection(DynamicImage& image) { + typedef typename MachBits::mach_header_type mach_header_type; + typedef typename MachBits::mach_segment_command_type + mach_segment_command_type; -#if __LP64__ - if(header->magic != MH_MAGIC_64) { - return; - } -#else - if(header->magic != MH_MAGIC) { - return; - } -#endif + const mach_header_type* header = + reinterpret_cast<const mach_header_type*>(&image.header_[0]); -#ifdef __LP64__ - const uint32_t segmentLoadCommand = LC_SEGMENT_64; -#else - const uint32_t segmentLoadCommand = LC_SEGMENT; -#endif + if(header->magic != MachBits::magic) { + return false; + } const struct load_command *cmd = - reinterpret_cast<const struct load_command *>(header + 1); + reinterpret_cast<const struct load_command *>(header + 1); + bool found_text_section = false; + bool found_dylib_id_command = false; for (unsigned int i = 0; cmd && (i < header->ncmds); ++i) { - if (!foundTextSection) { - if (cmd->cmd == segmentLoadCommand) { - const breakpad_mach_segment_command *seg = - reinterpret_cast<const breakpad_mach_segment_command *>(cmd); + if (!found_text_section) { + if (cmd->cmd == MachBits::segment_load_command) { + const mach_segment_command_type *seg = + reinterpret_cast<const mach_segment_command_type *>(cmd); if (!strcmp(seg->segname, "__TEXT")) { - vmaddr_ = seg->vmaddr; - vmsize_ = seg->vmsize; - slide_ = 0; + image.vmaddr_ = seg->vmaddr; + image.vmsize_ = seg->vmsize; + image.slide_ = 0; - if (seg->fileoff == 0 && seg->filesize != 0) { - slide_ = (uintptr_t)GetLoadAddress() - (uintptr_t)seg->vmaddr; + if (seg->fileoff == 0 && seg->filesize != 0) { + image.slide_ = + (uintptr_t)image.GetLoadAddress() - (uintptr_t)seg->vmaddr; } - foundTextSection = true; + found_text_section = true; } } } - if (!foundDylibIDCommand) { + if (!found_dylib_id_command) { if (cmd->cmd == LC_ID_DYLIB) { const struct dylib_command *dc = reinterpret_cast<const struct dylib_command *>(cmd); - version_ = dc->dylib.current_version; - foundDylibIDCommand = true; + image.version_ = dc->dylib.current_version; + found_dylib_id_command = true; } } - if (foundDylibIDCommand && foundTextSection) { - return; + if (found_dylib_id_command && found_text_section) { + return true; } cmd = reinterpret_cast<const struct load_command *> - (reinterpret_cast<const char *>(cmd) + cmd->cmdsize); + (reinterpret_cast<const char *>(cmd) + cmd->cmdsize); } + return false; } -void DynamicImage::Print() { - const char *path = GetFilePath(); - if (!path) { - path = "(unknown)"; - } - printf("%p: %s\n", GetLoadAddress(), path); - breakpad_mach_header *header = GetMachHeader(); - MachHeader(*header).Print(); - printf("vmaddr\t\t: %p\n", reinterpret_cast<void*>(GetVMAddr())); - printf("vmsize\t\t: %llu\n", GetVMSize()); - printf("slide\t\t: %td\n", GetVMAddrSlide()); +//============================================================================== +// Initializes vmaddr_, vmsize_, and slide_ +void DynamicImage::CalculateMemoryAndVersionInfo() { + // unless we can process the header, ensure that calls to + // IsValid() will return false + vmaddr_ = 0; + vmsize_ = 0; + slide_ = 0; + version_ = 0; + + // The function template above does all the real work. + if (Is64Bit()) + FindTextSection<MachO64>(*this); + else + FindTextSection<MachO32>(*this); +} + +//============================================================================== +// The helper function template abstracts the 32/64-bit differences. +template<typename MachBits> +uint32_t GetFileTypeFromHeader(DynamicImage& image) { + typedef typename MachBits::mach_header_type mach_header_type; + + const mach_header_type* header = + reinterpret_cast<const mach_header_type*>(&image.header_[0]); + return header->filetype; +} + +uint32_t DynamicImage::GetFileType() { + if (Is64Bit()) + return GetFileTypeFromHeader<MachO64>(*this); + + return GetFileTypeFromHeader<MachO32>(*this); } #pragma mark - @@ -277,144 +308,158 @@ void DynamicImage::Print() { //============================================================================== // Loads information about dynamically loaded code in the given task. DynamicImages::DynamicImages(mach_port_t task) - : task_(task), image_list_() { + : task_(task), + cpu_type_(DetermineTaskCPUType(task)), + image_list_() { ReadImageInfoForTask(); } -void* DynamicImages::GetDyldAllImageInfosPointer() { - const char *imageSymbolName = "_dyld_all_image_infos"; - const char *dyldPath = "/usr/lib/dyld"; -#ifndef __LP64__ - struct nlist l[8]; - memset(l, 0, sizeof(l) ); - - // First we lookup the address of the "_dyld_all_image_infos" struct - // which lives in "dyld". This structure contains information about all - // of the loaded dynamic images. - struct nlist &list = l[0]; - list.n_un.n_name = const_cast<char *>(imageSymbolName); - nlist(dyldPath,&list); - if(list.n_value) { - return reinterpret_cast<void*>(list.n_value); - } - - return NULL; -#else - struct nlist_64 l[8]; - struct nlist_64 &list = l[0]; +template<typename MachBits> +static uint64_t LookupSymbol(const char* symbol_name, + const char* filename, + cpu_type_t cpu_type) { + typedef typename MachBits::nlist_type nlist_type; - memset(l, 0, sizeof(l) ); - - const char *symbolNames[2] = { imageSymbolName, "\0" }; - - int invalidEntriesCount = breakpad_nlist_64(dyldPath,&list,symbolNames); + nlist_type symbol_info[8] = {}; + const char *symbolNames[2] = { symbol_name, "\0" }; + nlist_type &list = symbol_info[0]; + int invalidEntriesCount = breakpad_nlist(filename, + &list, + symbolNames, + cpu_type); if(invalidEntriesCount != 0) { - return NULL; + return 0; } + assert(list.n_value); - return reinterpret_cast<void*>(list.n_value); -#endif + return list.n_value; +} + +uint64_t DynamicImages::GetDyldAllImageInfosPointer() { + const char *imageSymbolName = "_dyld_all_image_infos"; + const char *dyldPath = "/usr/lib/dyld"; + if (Is64Bit()) + return LookupSymbol<MachO64>(imageSymbolName, dyldPath, cpu_type_); + return LookupSymbol<MachO32>(imageSymbolName, dyldPath, cpu_type_); } + //============================================================================== // This code was written using dyld_debug.c (from Darwin) as a guide. -void DynamicImages::ReadImageInfoForTask() { - void *imageList = GetDyldAllImageInfosPointer(); - if (imageList) { - kern_return_t kr; - // Read the structure inside of dyld that contains information about - // loaded images. We're reading from the desired task's address space. - - // Here we make the assumption that dyld loaded at the same address in - // the crashed process vs. this one. This is an assumption made in - // "dyld_debug.c" and is said to be nearly always valid. - dyld_all_image_infos *dyldInfo = reinterpret_cast<dyld_all_image_infos*> - (ReadTaskMemory(task_, - reinterpret_cast<void*>(imageList), - sizeof(dyld_all_image_infos), &kr)); - - if (dyldInfo) { - // number of loaded images - int count = dyldInfo->infoArrayCount; - - // Read an array of dyld_image_info structures each containing - // information about a loaded image. - dyld_image_info *infoArray = reinterpret_cast<dyld_image_info*> - (ReadTaskMemory(task_, - dyldInfo->infoArray, - count*sizeof(dyld_image_info), &kr)); - - image_list_.reserve(count); - - for (int i = 0; i < count; ++i) { - dyld_image_info &info = infoArray[i]; - - // First read just the mach_header from the image in the task. - breakpad_mach_header *header = reinterpret_cast<breakpad_mach_header*> - (ReadTaskMemory(task_, - info.load_address_, - sizeof(breakpad_mach_header), &kr)); - - if (!header) - break; // bail on this dynamic image - - // Now determine the total amount we really want to read based on the - // size of the load commands. We need the header plus all of the - // load commands. - size_t header_size = - sizeof(breakpad_mach_header) + header->sizeofcmds; - - free(header); - - header = reinterpret_cast<breakpad_mach_header*> - (ReadTaskMemory(task_, info.load_address_, header_size, &kr)); - - // Read the file name from the task's memory space. - char *file_path = NULL; - if (info.file_path_) { - // Although we're reading kMaxStringLength bytes, it's copied in the - // the DynamicImage constructor below with the correct string length, - // so it's not really wasting memory. - file_path = reinterpret_cast<char*> - (ReadTaskString(task_, info.file_path_)); - } +template<typename MachBits> +void ReadImageInfo(DynamicImages& images, + uint64_t image_list_address) { + typedef typename MachBits::dyld_image_info dyld_image_info; + typedef typename MachBits::dyld_all_image_infos dyld_all_image_infos; + typedef typename MachBits::mach_header_type mach_header_type; + + // Read the structure inside of dyld that contains information about + // loaded images. We're reading from the desired task's address space. + + // Here we make the assumption that dyld loaded at the same address in + // the crashed process vs. this one. This is an assumption made in + // "dyld_debug.c" and is said to be nearly always valid. + vector<uint8_t> dyld_all_info_bytes; + if (ReadTaskMemory(images.task_, + image_list_address, + sizeof(dyld_all_image_infos), + dyld_all_info_bytes) != KERN_SUCCESS) + return; - // Create an object representing this image and add it to our list. - DynamicImage *new_image; - new_image = new DynamicImage(header, - header_size, - (breakpad_mach_header*)info.load_address_, - file_path, - info.file_mod_date_, - task_); - - if (new_image->IsValid()) { - image_list_.push_back(DynamicImageRef(new_image)); - } else { - delete new_image; - } + dyld_all_image_infos *dyldInfo = + reinterpret_cast<dyld_all_image_infos*>(&dyld_all_info_bytes[0]); - if (file_path) { - free(file_path); - } - } + // number of loaded images + int count = dyldInfo->infoArrayCount; - free(dyldInfo); - free(infoArray); + // Read an array of dyld_image_info structures each containing + // information about a loaded image. + vector<uint8_t> dyld_info_array_bytes; + if (ReadTaskMemory(images.task_, + dyldInfo->infoArray, + count * sizeof(dyld_image_info), + dyld_info_array_bytes) != KERN_SUCCESS) + return; - // sorts based on loading address - sort(image_list_.begin(), image_list_.end() ); - // remove duplicates - this happens in certain strange cases - // You can see it in DashboardClient when Google Gadgets plugin - // is installed. Apple's crash reporter log and gdb "info shared" - // both show the same library multiple times at the same address + dyld_image_info *infoArray = + reinterpret_cast<dyld_image_info*>(&dyld_info_array_bytes[0]); + images.image_list_.reserve(count); + + for (int i = 0; i < count; ++i) { + dyld_image_info &info = infoArray[i]; + + // First read just the mach_header from the image in the task. + vector<uint8_t> mach_header_bytes; + if (ReadTaskMemory(images.task_, + info.load_address_, + sizeof(mach_header_type), + mach_header_bytes) != KERN_SUCCESS) + continue; // bail on this dynamic image + + mach_header_type *header = + reinterpret_cast<mach_header_type*>(&mach_header_bytes[0]); + + // Now determine the total amount necessary to read the header + // plus all of the load commands. + size_t header_size = + sizeof(mach_header_type) + header->sizeofcmds; + + if (ReadTaskMemory(images.task_, + info.load_address_, + header_size, + mach_header_bytes) != KERN_SUCCESS) + continue; + + header = reinterpret_cast<mach_header_type*>(&mach_header_bytes[0]); + + // Read the file name from the task's memory space. + string file_path; + if (info.file_path_) { + // Although we're reading kMaxStringLength bytes, it's copied in the + // the DynamicImage constructor below with the correct string length, + // so it's not really wasting memory. + file_path = ReadTaskString(images.task_, info.file_path_); + } - vector<DynamicImageRef>::iterator it = unique(image_list_.begin(), - image_list_.end() ); - image_list_.erase(it, image_list_.end()); + // Create an object representing this image and add it to our list. + DynamicImage *new_image; + new_image = new DynamicImage(&mach_header_bytes[0], + header_size, + info.load_address_, + file_path, + info.file_mod_date_, + images.task_, + images.cpu_type_); + + if (new_image->IsValid()) { + images.image_list_.push_back(DynamicImageRef(new_image)); + } else { + delete new_image; + } } + + // sorts based on loading address + sort(images.image_list_.begin(), images.image_list_.end()); + // remove duplicates - this happens in certain strange cases + // You can see it in DashboardClient when Google Gadgets plugin + // is installed. Apple's crash reporter log and gdb "info shared" + // both show the same library multiple times at the same address + + vector<DynamicImageRef>::iterator it = unique(images.image_list_.begin(), + images.image_list_.end()); + images.image_list_.erase(it, images.image_list_.end()); +} + +void DynamicImages::ReadImageInfoForTask() { + uint64_t imageList = GetDyldAllImageInfosPointer(); + + if (imageList) { + if (Is64Bit()) + ReadImageInfo<MachO64>(*this, imageList); + else + ReadImageInfo<MachO32>(*this, imageList); } } @@ -436,7 +481,7 @@ int DynamicImages::GetExecutableImageIndex() { for (int i = 0; i < image_count; ++i) { DynamicImage *image = GetImage(i); - if (image->GetMachHeader()->filetype == MH_EXECUTE) { + if (image->GetFileType() == MH_EXECUTE) { return i; } } @@ -444,4 +489,27 @@ int DynamicImages::GetExecutableImageIndex() { return -1; } +//============================================================================== +// static +cpu_type_t DynamicImages::DetermineTaskCPUType(task_t task) { + if (task == mach_task_self()) + return GetNativeCPUType(); + + int mib[CTL_MAXNAME]; + size_t mibLen = CTL_MAXNAME; + int err = sysctlnametomib("sysctl.proc_cputype", mib, &mibLen); + if (err == 0) { + assert(mibLen < CTL_MAXNAME); + pid_for_task(task, &mib[mibLen]); + mibLen += 1; + + cpu_type_t cpu_type; + size_t cpuTypeSize = sizeof(cpu_type); + sysctl(mib, mibLen, &cpu_type, &cpuTypeSize, 0, 0); + return cpu_type; + } + + return GetNativeCPUType(); +} + } // namespace google_breakpad |