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