# HG changeset patch # User Steven Michaud # Date 1574199757 0 # Node ID 0c63dcd7a1c6ff0fab249cd8395f3841a6bf3215 # Parent c0530b497076c9e4472624a249ca69f7583664e1 Bug 1371390 - Pay attention to macho images' cpusubtype when creating minidumps (revised). r=gsvelto Differential Revision: https://phabricator.services.mozilla.com/D53820 diff --git a/toolkit/crashreporter/breakpad-client/mac/handler/dynamic_images.cc b/toolkit/crashreporter/breakpad-client/mac/handler/dynamic_images.cc --- a/toolkit/crashreporter/breakpad-client/mac/handler/dynamic_images.cc +++ b/toolkit/crashreporter/breakpad-client/mac/handler/dynamic_images.cc @@ -209,16 +209,23 @@ kern_return_t ReadTaskMemory(task_port_t &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 - +// 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 + //============================================================================== // 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; @@ -245,36 +252,42 @@ bool FindTextSection(DynamicImage& image const mach_header_type* header = reinterpret_cast(&image.header_[0]); if(header->magic != MachBits::magic) { return false; } + bool is_in_shared_cache = ((header->flags & MH_SHAREDCACHE) != 0); + if (is_in_shared_cache) { + image.slide_ = image.shared_cache_slide_; + } + const struct load_command *cmd = reinterpret_cast(header + 1); bool found_text_section = false; bool found_dylib_id_command = false; for (unsigned int i = 0; cmd && (i < header->ncmds); ++i) { if (!found_text_section) { if (cmd->cmd == MachBits::segment_load_command) { const mach_segment_command_type *seg = reinterpret_cast(cmd); + if (!is_in_shared_cache) { + if (seg->fileoff == 0 && seg->filesize != 0) { + image.slide_ = + (uintptr_t)image.GetLoadAddress() - (uintptr_t)seg->vmaddr; + } + } + if (!strcmp(seg->segname, "__TEXT")) { image.vmaddr_ = static_cast(seg->vmaddr); image.vmsize_ = static_cast(seg->vmsize); - image.slide_ = 0; - - if (seg->fileoff == 0 && seg->filesize != 0) { - image.slide_ = - (uintptr_t)image.GetLoadAddress() - (uintptr_t)seg->vmaddr; - } found_text_section = true; } } } if (!found_dylib_id_command) { if (cmd->cmd == LC_ID_DYLIB) { const struct dylib_command *dc = @@ -426,16 +439,18 @@ void ReadImageInfo(DynamicImages& images 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_bytes[0]); + cpu_subtype_t cpusubtype = (header->cpusubtype & ~CPU_SUBTYPE_MASK); + // 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, @@ -454,17 +469,19 @@ void ReadImageInfo(DynamicImages& images // 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, static_cast(info.file_mod_date_), images.task_, - images.cpu_type_); + images.cpu_type_, + cpusubtype, + dyldInfo->sharedCacheSlide); if (new_image->IsValid()) { images.image_list_.push_back(DynamicImageRef(new_image)); } else { delete new_image; } } diff --git a/toolkit/crashreporter/breakpad-client/mac/handler/dynamic_images.h b/toolkit/crashreporter/breakpad-client/mac/handler/dynamic_images.h --- a/toolkit/crashreporter/breakpad-client/mac/handler/dynamic_images.h +++ b/toolkit/crashreporter/breakpad-client/mac/handler/dynamic_images.h @@ -72,24 +72,30 @@ typedef struct dyld_image_info64 { // _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; + uint32_t padding[15]; + // Only in version 12 (Mac OS X 10.7, iOS 4.3) and later + uint32_t sharedCacheSlide; } 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; + uint64_t padding[15]; + // Only in version 12 (Mac OS X 10.7, iOS 4.3) and later + uint64_t sharedCacheSlide; } 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; @@ -109,28 +115,32 @@ uint32_t GetFileTypeFromHeader(DynamicIm 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_type_t cpu_type, + cpu_subtype_t cpu_subtype, + ptrdiff_t shared_cache_slide) : header_(header, header + header_size), header_size_(header_size), load_address_(load_address), vmaddr_(0), vmsize_(0), slide_(0), version_(0), file_path_(file_path), file_mod_date_(image_mod_date), task_(task), - cpu_type_(cpu_type) { + 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_;} @@ -147,19 +157,22 @@ class DynamicImage { ptrdiff_t GetVMAddrSlide() const {return slide_;} // Size of the image mach_vm_size_t GetVMSize() const {return vmsize_;} // Task owning this loaded image mach_port_t GetTask() {return task_;} - // CPU type of the 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 @@ -189,17 +202,19 @@ class DynamicImage { mach_vm_address_t vmaddr_; mach_vm_size_t vmsize_; ptrdiff_t slide_; 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 mach_port_t task_; - cpu_type_t cpu_type_; // CPU type of 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<(). // diff --git a/toolkit/crashreporter/breakpad-client/mac/handler/minidump_generator.cc b/toolkit/crashreporter/breakpad-client/mac/handler/minidump_generator.cc --- a/toolkit/crashreporter/breakpad-client/mac/handler/minidump_generator.cc +++ b/toolkit/crashreporter/breakpad-client/mac/handler/minidump_generator.cc @@ -1327,17 +1327,18 @@ bool MinidumpGenerator::WriteModuleStrea // 3) Least significant 8 bits go to lower 16 bits of product LO uint32_t modVersion = image->GetVersion(); module->version_info.file_version_hi = 0; module->version_info.file_version_hi = modVersion >> 16; module->version_info.file_version_lo |= (modVersion & 0xff00) << 8; module->version_info.file_version_lo |= (modVersion & 0xff); } - if (!WriteCVRecord(module, image->GetCPUType(), name.c_str(), false)) { + if (!WriteCVRecord(module, image->GetCPUType(), image->GetCPUSubtype(), + name.c_str(), false)) { return false; } } else { // Getting module info in the crashed process const breakpad_mach_header *header; header = (breakpad_mach_header*)_dyld_get_image_header(index); if (!header) return false; @@ -1350,16 +1351,17 @@ bool MinidumpGenerator::WriteModuleStrea #else assert(header->magic == MH_MAGIC); if(header->magic != MH_MAGIC) return false; #endif int cpu_type = header->cputype; + int cpu_subtype = (header->cpusubtype & ~CPU_SUBTYPE_MASK); unsigned long slide = _dyld_get_image_vmaddr_slide(index); const char* name = _dyld_get_image_name(index); const struct load_command *cmd = reinterpret_cast(header + 1); memset(module, 0, sizeof(MDRawModule)); for (unsigned int i = 0; cmd && (i < header->ncmds); i++) { @@ -1377,17 +1379,17 @@ bool MinidumpGenerator::WriteModuleStrea module->base_of_image = seg->vmaddr + slide; module->size_of_image = static_cast(seg->vmsize); module->module_name_rva = string_location.rva; bool in_memory = false; #if TARGET_OS_IPHONE in_memory = true; #endif - if (!WriteCVRecord(module, cpu_type, name, in_memory)) + if (!WriteCVRecord(module, cpu_type, cpu_subtype, name, in_memory)) return false; return true; } } cmd = reinterpret_cast((char *)cmd + cmd->cmdsize); } @@ -1414,17 +1416,17 @@ int MinidumpGenerator::FindExecutableMod return index; } } // failed - just use the first image return 0; } -bool MinidumpGenerator::WriteCVRecord(MDRawModule *module, int cpu_type, +bool MinidumpGenerator::WriteCVRecord(MDRawModule *module, int cpu_type, int cpu_subtype, const char *module_path, bool in_memory) { TypedMDRVA cv(&writer_); // Only return the last path component of the full module path const char *module_name = strrchr(module_path, '/'); // Increment past the slash if (module_name) @@ -1447,24 +1449,24 @@ bool MinidumpGenerator::WriteCVRecord(MD // Get the module identifier unsigned char identifier[16]; bool result = false; if (in_memory) { MacFileUtilities::MachoID macho(module_path, reinterpret_cast(module->base_of_image), static_cast(module->size_of_image)); - result = macho.UUIDCommand(cpu_type, CPU_SUBTYPE_MULTIPLE, identifier); + result = macho.UUIDCommand(cpu_type, cpu_subtype, identifier); if (!result) - result = macho.MD5(cpu_type, CPU_SUBTYPE_MULTIPLE, identifier); + result = macho.MD5(cpu_type, cpu_subtype, identifier); } if (!result) { FileID file_id(module_path); - result = file_id.MachoIdentifier(cpu_type, CPU_SUBTYPE_MULTIPLE, + result = file_id.MachoIdentifier(cpu_type, cpu_subtype, identifier); } if (result) { cv_ptr->signature.data1 = static_cast(identifier[0]) << 24 | static_cast(identifier[1]) << 16 | static_cast(identifier[2]) << 8 | diff --git a/toolkit/crashreporter/breakpad-client/mac/handler/minidump_generator.h b/toolkit/crashreporter/breakpad-client/mac/handler/minidump_generator.h --- a/toolkit/crashreporter/breakpad-client/mac/handler/minidump_generator.h +++ b/toolkit/crashreporter/breakpad-client/mac/handler/minidump_generator.h @@ -137,17 +137,17 @@ class MinidumpGenerator { bool GetThreadState(thread_act_t target_thread, thread_state_t state, mach_msg_type_number_t *count); bool WriteStackFromStartAddress(mach_vm_address_t start_addr, MDMemoryDescriptor *stack_location); bool WriteStack(breakpad_thread_state_data_t state, MDMemoryDescriptor *stack_location); bool WriteContext(breakpad_thread_state_data_t state, MDLocationDescriptor *register_location); - bool WriteCVRecord(MDRawModule *module, int cpu_type, + bool WriteCVRecord(MDRawModule *module, int cpu_type, int cpu_subtype, const char *module_path, bool in_memory); bool WriteModuleStream(unsigned int index, MDRawModule *module); size_t CalculateStackSize(mach_vm_address_t start_addr); int FindExecutableModule(); // Per-CPU implementations of these methods #ifdef HAS_ARM_SUPPORT bool WriteStackARM(breakpad_thread_state_data_t state, diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/macho_walker.cc b/toolkit/crashreporter/google-breakpad/src/common/mac/macho_walker.cc --- a/toolkit/crashreporter/google-breakpad/src/common/mac/macho_walker.cc +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/macho_walker.cc @@ -151,16 +151,18 @@ bool MachoWalker::FindHeader(cpu_type_t // header struct mach_header header; if (!ReadBytes(&header, sizeof(header), 0)) return false; if (magic == MH_CIGAM || magic == MH_CIGAM_64) breakpad_swap_mach_header(&header); + header.cpusubtype &= ~CPU_SUBTYPE_MASK; + if (cpu_type != header.cputype || (cpu_subtype != CPU_SUBTYPE_MULTIPLE && cpu_subtype != header.cpusubtype)) { return false; } offset = 0; return true; @@ -180,16 +182,18 @@ bool MachoWalker::FindHeader(cpu_type_t struct fat_arch arch; for (uint32_t i = 0; i < fat.nfat_arch; ++i) { if (!ReadBytes(&arch, sizeof(arch), offset)) return false; if (NXHostByteOrder() != NX_BigEndian) breakpad_swap_fat_arch(&arch, 1); + arch.cpusubtype &= ~CPU_SUBTYPE_MASK; + if (arch.cputype == cpu_type && (cpu_subtype == CPU_SUBTYPE_MULTIPLE || arch.cpusubtype == cpu_subtype)) { offset = arch.offset; return true; } offset += sizeof(arch);