1// Copyright (c) 2012, Google Inc. 2// All rights reserved. 3// 4// Redistribution and use in source and binary forms, with or without 5// modification, are permitted provided that the following conditions are 6// met: 7// 8// * Redistributions of source code must retain the above copyright 9// notice, this list of conditions and the following disclaimer. 10// * Redistributions in binary form must reproduce the above 11// copyright notice, this list of conditions and the following disclaimer 12// in the documentation and/or other materials provided with the 13// distribution. 14// * Neither the name of Google Inc. nor the names of its 15// contributors may be used to endorse or promote products derived from 16// this software without specific prior written permission. 17// 18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 30#include "client/ios/handler/ios_exception_minidump_generator.h" 31 32#include <pthread.h> 33 34#include "google_breakpad/common/minidump_cpu_arm.h" 35#include "google_breakpad/common/minidump_cpu_arm64.h" 36#include "google_breakpad/common/minidump_exception_mac.h" 37#include "client/minidump_file_writer-inl.h" 38#include "common/scoped_ptr.h" 39 40#if defined(HAS_ARM_SUPPORT) && defined(HAS_ARM64_SUPPORT) 41#error "This file should be compiled for only one architecture at a time" 42#endif 43 44namespace { 45 46const int kExceptionType = EXC_SOFTWARE; 47const int kExceptionCode = MD_EXCEPTION_CODE_MAC_NS_EXCEPTION; 48 49#if defined(HAS_ARM_SUPPORT) || defined(HAS_ARM64_SUPPORT) 50const uintptr_t kExpectedFinalFp = sizeof(uintptr_t); 51const uintptr_t kExpectedFinalSp = 0; 52 53// Append the given value to the sp position of the stack represented 54// by memory. 55void AppendToMemory(uint8_t *memory, uintptr_t sp, uintptr_t data) { 56 memcpy(memory + sp, &data, sizeof(data)); 57} 58#endif 59 60} // namespace 61 62namespace google_breakpad { 63 64IosExceptionMinidumpGenerator::IosExceptionMinidumpGenerator( 65 NSException *exception) 66 : MinidumpGenerator(mach_task_self(), 0) { 67 return_addresses_ = [[exception callStackReturnAddresses] retain]; 68 SetExceptionInformation(kExceptionType, 69 kExceptionCode, 70 0, 71 pthread_mach_thread_np(pthread_self())); 72} 73 74IosExceptionMinidumpGenerator::~IosExceptionMinidumpGenerator() { 75 [return_addresses_ release]; 76} 77 78bool IosExceptionMinidumpGenerator::WriteCrashingContext( 79 MDLocationDescriptor *register_location) { 80#ifdef HAS_ARM_SUPPORT 81 return WriteCrashingContextARM(register_location); 82#elif defined(HAS_ARM64_SUPPORT) 83 return WriteCrashingContextARM64(register_location); 84#else 85 assert(false); 86 return false; 87#endif 88} 89 90#ifdef HAS_ARM_SUPPORT 91bool IosExceptionMinidumpGenerator::WriteCrashingContextARM( 92 MDLocationDescriptor *register_location) { 93 TypedMDRVA<MDRawContextARM> context(&writer_); 94 if (!context.Allocate()) 95 return false; 96 *register_location = context.location(); 97 MDRawContextARM *context_ptr = context.get(); 98 memset(context_ptr, 0, sizeof(MDRawContextARM)); 99 context_ptr->context_flags = MD_CONTEXT_ARM_FULL; 100 context_ptr->iregs[MD_CONTEXT_ARM_REG_IOS_FP] = kExpectedFinalFp; // FP 101 context_ptr->iregs[MD_CONTEXT_ARM_REG_SP] = kExpectedFinalSp; // SP 102 context_ptr->iregs[MD_CONTEXT_ARM_REG_LR] = GetLRFromException(); // LR 103 context_ptr->iregs[MD_CONTEXT_ARM_REG_PC] = GetPCFromException(); // PC 104 return true; 105} 106#endif 107 108#ifdef HAS_ARM64_SUPPORT 109bool IosExceptionMinidumpGenerator::WriteCrashingContextARM64( 110 MDLocationDescriptor *register_location) { 111 TypedMDRVA<MDRawContextARM64> context(&writer_); 112 if (!context.Allocate()) 113 return false; 114 *register_location = context.location(); 115 MDRawContextARM64 *context_ptr = context.get(); 116 memset(context_ptr, 0, sizeof(*context_ptr)); 117 context_ptr->context_flags = MD_CONTEXT_ARM64_FULL; 118 context_ptr->iregs[MD_CONTEXT_ARM64_REG_FP] = kExpectedFinalFp; // FP 119 context_ptr->iregs[MD_CONTEXT_ARM64_REG_SP] = kExpectedFinalSp; // SP 120 context_ptr->iregs[MD_CONTEXT_ARM64_REG_LR] = GetLRFromException(); // LR 121 context_ptr->iregs[MD_CONTEXT_ARM64_REG_PC] = GetPCFromException(); // PC 122 return true; 123} 124#endif 125 126uintptr_t IosExceptionMinidumpGenerator::GetPCFromException() { 127 return [[return_addresses_ objectAtIndex:0] unsignedIntegerValue]; 128} 129 130uintptr_t IosExceptionMinidumpGenerator::GetLRFromException() { 131 return [[return_addresses_ objectAtIndex:1] unsignedIntegerValue]; 132} 133 134bool IosExceptionMinidumpGenerator::WriteExceptionStream( 135 MDRawDirectory *exception_stream) { 136#if defined(HAS_ARM_SUPPORT) || defined(HAS_ARM64_SUPPORT) 137 TypedMDRVA<MDRawExceptionStream> exception(&writer_); 138 139 if (!exception.Allocate()) 140 return false; 141 142 exception_stream->stream_type = MD_EXCEPTION_STREAM; 143 exception_stream->location = exception.location(); 144 MDRawExceptionStream *exception_ptr = exception.get(); 145 exception_ptr->thread_id = pthread_mach_thread_np(pthread_self()); 146 147 // This naming is confusing, but it is the proper translation from 148 // mach naming to minidump naming. 149 exception_ptr->exception_record.exception_code = kExceptionType; 150 exception_ptr->exception_record.exception_flags = kExceptionCode; 151 152 if (!WriteCrashingContext(&exception_ptr->thread_context)) 153 return false; 154 155 exception_ptr->exception_record.exception_address = GetPCFromException(); 156 return true; 157#else 158 return MinidumpGenerator::WriteExceptionStream(exception_stream); 159#endif 160} 161 162bool IosExceptionMinidumpGenerator::WriteThreadStream(mach_port_t thread_id, 163 MDRawThread *thread) { 164#if defined(HAS_ARM_SUPPORT) || defined(HAS_ARM64_SUPPORT) 165 if (pthread_mach_thread_np(pthread_self()) != thread_id) 166 return MinidumpGenerator::WriteThreadStream(thread_id, thread); 167 168 size_t frame_count = [return_addresses_ count]; 169 if (frame_count == 0) 170 return false; 171 UntypedMDRVA memory(&writer_); 172 size_t pointer_size = sizeof(uintptr_t); 173 size_t frame_record_size = 2 * pointer_size; 174 size_t stack_size = frame_record_size * (frame_count - 1) + pointer_size; 175 if (!memory.Allocate(stack_size)) 176 return false; 177 scoped_array<uint8_t> stack_memory(new uint8_t[stack_size]); 178 uintptr_t sp = stack_size - pointer_size; 179 uintptr_t fp = 0; 180 uintptr_t lr = 0; 181 for (size_t current_frame = frame_count - 1; 182 current_frame > 0; 183 --current_frame) { 184 AppendToMemory(stack_memory.get(), sp, lr); 185 sp -= pointer_size; 186 AppendToMemory(stack_memory.get(), sp, fp); 187 fp = sp; 188 sp -= pointer_size; 189 lr = [[return_addresses_ objectAtIndex:current_frame] unsignedIntegerValue]; 190 } 191 if (!memory.Copy(stack_memory.get(), stack_size)) 192 return false; 193 assert(sp == kExpectedFinalSp); 194 assert(fp == kExpectedFinalFp); 195 assert(lr == GetLRFromException()); 196 thread->stack.start_of_memory_range = sp; 197 thread->stack.memory = memory.location(); 198 memory_blocks_.push_back(thread->stack); 199 200 if (!WriteCrashingContext(&thread->thread_context)) 201 return false; 202 203 thread->thread_id = thread_id; 204 return true; 205#else 206 return MinidumpGenerator::WriteThreadStream(thread_id, thread); 207#endif 208} 209 210} // namespace google_breakpad 211