// Copyright (c) 2012, 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. #include "client/ios/handler/ios_exception_minidump_generator.h" #include #include "google_breakpad/common/minidump_cpu_arm.h" #include "google_breakpad/common/minidump_cpu_arm64.h" #include "google_breakpad/common/minidump_exception_mac.h" #include "client/minidump_file_writer-inl.h" #include "common/scoped_ptr.h" #if defined(HAS_ARM_SUPPORT) && defined(HAS_ARM64_SUPPORT) #error "This file should be compiled for only one architecture at a time" #endif namespace { const int kExceptionType = EXC_SOFTWARE; const int kExceptionCode = MD_EXCEPTION_CODE_MAC_NS_EXCEPTION; #if defined(HAS_ARM_SUPPORT) || defined(HAS_ARM64_SUPPORT) const uintptr_t kExpectedFinalFp = sizeof(uintptr_t); const uintptr_t kExpectedFinalSp = 0; // Append the given value to the sp position of the stack represented // by memory. void AppendToMemory(uint8_t *memory, uintptr_t sp, uintptr_t data) { memcpy(memory + sp, &data, sizeof(data)); } #endif } // namespace namespace google_breakpad { IosExceptionMinidumpGenerator::IosExceptionMinidumpGenerator( NSException *exception) : MinidumpGenerator(mach_task_self(), 0) { return_addresses_ = [[exception callStackReturnAddresses] retain]; SetExceptionInformation(kExceptionType, kExceptionCode, 0, pthread_mach_thread_np(pthread_self())); } IosExceptionMinidumpGenerator::~IosExceptionMinidumpGenerator() { [return_addresses_ release]; } bool IosExceptionMinidumpGenerator::WriteCrashingContext( MDLocationDescriptor *register_location) { #ifdef HAS_ARM_SUPPORT return WriteCrashingContextARM(register_location); #elif defined(HAS_ARM64_SUPPORT) return WriteCrashingContextARM64(register_location); #else assert(false); return false; #endif } #ifdef HAS_ARM_SUPPORT bool IosExceptionMinidumpGenerator::WriteCrashingContextARM( MDLocationDescriptor *register_location) { TypedMDRVA context(&writer_); if (!context.Allocate()) return false; *register_location = context.location(); MDRawContextARM *context_ptr = context.get(); memset(context_ptr, 0, sizeof(MDRawContextARM)); context_ptr->context_flags = MD_CONTEXT_ARM_FULL; context_ptr->iregs[MD_CONTEXT_ARM_REG_IOS_FP] = kExpectedFinalFp; // FP context_ptr->iregs[MD_CONTEXT_ARM_REG_SP] = kExpectedFinalSp; // SP context_ptr->iregs[MD_CONTEXT_ARM_REG_LR] = GetLRFromException(); // LR context_ptr->iregs[MD_CONTEXT_ARM_REG_PC] = GetPCFromException(); // PC return true; } #endif #ifdef HAS_ARM64_SUPPORT bool IosExceptionMinidumpGenerator::WriteCrashingContextARM64( MDLocationDescriptor *register_location) { TypedMDRVA context(&writer_); if (!context.Allocate()) return false; *register_location = context.location(); MDRawContextARM64_Old *context_ptr = context.get(); memset(context_ptr, 0, sizeof(*context_ptr)); context_ptr->context_flags = MD_CONTEXT_ARM64_FULL_OLD; context_ptr->iregs[MD_CONTEXT_ARM64_REG_FP] = kExpectedFinalFp; // FP context_ptr->iregs[MD_CONTEXT_ARM64_REG_SP] = kExpectedFinalSp; // SP context_ptr->iregs[MD_CONTEXT_ARM64_REG_LR] = GetLRFromException(); // LR context_ptr->iregs[MD_CONTEXT_ARM64_REG_PC] = GetPCFromException(); // PC return true; } #endif uintptr_t IosExceptionMinidumpGenerator::GetPCFromException() { return [[return_addresses_ objectAtIndex:0] unsignedIntegerValue]; } uintptr_t IosExceptionMinidumpGenerator::GetLRFromException() { return [[return_addresses_ objectAtIndex:1] unsignedIntegerValue]; } bool IosExceptionMinidumpGenerator::WriteExceptionStream( MDRawDirectory *exception_stream) { #if defined(HAS_ARM_SUPPORT) || defined(HAS_ARM64_SUPPORT) TypedMDRVA exception(&writer_); if (!exception.Allocate()) return false; exception_stream->stream_type = MD_EXCEPTION_STREAM; exception_stream->location = exception.location(); MDRawExceptionStream *exception_ptr = exception.get(); exception_ptr->thread_id = pthread_mach_thread_np(pthread_self()); // This naming is confusing, but it is the proper translation from // mach naming to minidump naming. exception_ptr->exception_record.exception_code = kExceptionType; exception_ptr->exception_record.exception_flags = kExceptionCode; if (!WriteCrashingContext(&exception_ptr->thread_context)) return false; exception_ptr->exception_record.exception_address = GetPCFromException(); return true; #else return MinidumpGenerator::WriteExceptionStream(exception_stream); #endif } bool IosExceptionMinidumpGenerator::WriteThreadStream(mach_port_t thread_id, MDRawThread *thread) { #if defined(HAS_ARM_SUPPORT) || defined(HAS_ARM64_SUPPORT) if (pthread_mach_thread_np(pthread_self()) != thread_id) return MinidumpGenerator::WriteThreadStream(thread_id, thread); size_t frame_count = [return_addresses_ count]; if (frame_count == 0) return false; UntypedMDRVA memory(&writer_); size_t pointer_size = sizeof(uintptr_t); size_t frame_record_size = 2 * pointer_size; size_t stack_size = frame_record_size * (frame_count - 1) + pointer_size; if (!memory.Allocate(stack_size)) return false; scoped_array stack_memory(new uint8_t[stack_size]); uintptr_t sp = stack_size - pointer_size; uintptr_t fp = 0; uintptr_t lr = 0; for (size_t current_frame = frame_count - 1; current_frame > 0; --current_frame) { AppendToMemory(stack_memory.get(), sp, lr); sp -= pointer_size; AppendToMemory(stack_memory.get(), sp, fp); fp = sp; sp -= pointer_size; lr = [[return_addresses_ objectAtIndex:current_frame] unsignedIntegerValue]; } if (!memory.Copy(stack_memory.get(), stack_size)) return false; assert(sp == kExpectedFinalSp); assert(fp == kExpectedFinalFp); assert(lr == GetLRFromException()); thread->stack.start_of_memory_range = sp; thread->stack.memory = memory.location(); memory_blocks_.push_back(thread->stack); if (!WriteCrashingContext(&thread->thread_context)) return false; thread->thread_id = thread_id; return true; #else return MinidumpGenerator::WriteThreadStream(thread_id, thread); #endif } } // namespace google_breakpad