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 44 namespace { 45 46 const int kExceptionType = EXC_SOFTWARE; 47 const int kExceptionCode = MD_EXCEPTION_CODE_MAC_NS_EXCEPTION; 48 49 #if defined(HAS_ARM_SUPPORT) || defined(HAS_ARM64_SUPPORT) 50 const uintptr_t kExpectedFinalFp = sizeof(uintptr_t); 51 const uintptr_t kExpectedFinalSp = 0; 52 53 // Append the given value to the sp position of the stack represented 54 // by memory. 55 void AppendToMemory(uint8_t *memory, uintptr_t sp, uintptr_t data) { 56 memcpy(memory + sp, &data, sizeof(data)); 57 } 58 #endif 59 60 } // namespace 61 62 namespace google_breakpad { 63 64 IosExceptionMinidumpGenerator::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 74 IosExceptionMinidumpGenerator::~IosExceptionMinidumpGenerator() { 75 [return_addresses_ release]; 76 } 77 78 bool 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 91 bool 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 109 bool 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 126 uintptr_t IosExceptionMinidumpGenerator::GetPCFromException() { 127 return [[return_addresses_ objectAtIndex:0] unsignedIntegerValue]; 128 } 129 130 uintptr_t IosExceptionMinidumpGenerator::GetLRFromException() { 131 return [[return_addresses_ objectAtIndex:1] unsignedIntegerValue]; 132 } 133 134 bool 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 162 bool 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