xref: /aosp_15_r20/external/google-breakpad/src/client/ios/handler/ios_exception_minidump_generator.mm (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
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