xref: /aosp_15_r20/external/google-breakpad/src/client/mac/handler/minidump_generator.cc (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
1 // Copyright 2006 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 #ifdef HAVE_CONFIG_H
30 #include <config.h>  // Must come first
31 #endif
32 
33 #include <algorithm>
34 #include <cstdio>
35 
36 #include <mach/host_info.h>
37 #include <mach/machine.h>
38 #include <mach/vm_statistics.h>
39 #include <mach-o/dyld.h>
40 #include <mach-o/loader.h>
41 #include <sys/sysctl.h>
42 #include <sys/resource.h>
43 
44 #include <CoreFoundation/CoreFoundation.h>
45 
46 #include "client/mac/handler/minidump_generator.h"
47 
48 #if defined(HAS_ARM_SUPPORT) || defined(HAS_ARM64_SUPPORT)
49 #include <mach/arm/thread_status.h>
50 #endif
51 #ifdef HAS_PPC_SUPPORT
52 #include <mach/ppc/thread_status.h>
53 #endif
54 #ifdef HAS_X86_SUPPORT
55 #include <mach/i386/thread_status.h>
56 #endif
57 
58 #include "client/minidump_file_writer-inl.h"
59 #include "common/mac/file_id.h"
60 #include "common/mac/macho_id.h"
61 #include "common/mac/string_utilities.h"
62 
63 using MacStringUtils::ConvertToString;
64 using MacStringUtils::IntegerValueAtIndex;
65 
66 namespace google_breakpad {
67 
68 using mach_o::FileID;
69 
70 #if defined(__LP64__) && __LP64__
71 #define LC_SEGMENT_ARCH LC_SEGMENT_64
72 #else
73 #define LC_SEGMENT_ARCH LC_SEGMENT
74 #endif
75 
76 // constructor when generating from within the crashed process
MinidumpGenerator()77 MinidumpGenerator::MinidumpGenerator()
78     : writer_(),
79       exception_type_(0),
80       exception_code_(0),
81       exception_subcode_(0),
82       exception_thread_(0),
83       crashing_task_(mach_task_self()),
84       handler_thread_(mach_thread_self()),
85       cpu_type_(DynamicImages::GetNativeCPUType()),
86       task_context_(NULL),
87       dynamic_images_(NULL),
88       memory_blocks_(&allocator_) {
89   GatherSystemInformation();
90 }
91 
92 // constructor when generating from a different process than the
93 // crashed process
MinidumpGenerator(mach_port_t crashing_task,mach_port_t handler_thread)94 MinidumpGenerator::MinidumpGenerator(mach_port_t crashing_task,
95                                      mach_port_t handler_thread)
96     : writer_(),
97       exception_type_(0),
98       exception_code_(0),
99       exception_subcode_(0),
100       exception_thread_(0),
101       crashing_task_(crashing_task),
102       handler_thread_(handler_thread),
103       cpu_type_(DynamicImages::GetNativeCPUType()),
104       task_context_(NULL),
105       dynamic_images_(NULL),
106       memory_blocks_(&allocator_) {
107   if (crashing_task != mach_task_self()) {
108     dynamic_images_ = new DynamicImages(crashing_task_);
109     cpu_type_ = dynamic_images_->GetCPUType();
110   } else {
111     dynamic_images_ = NULL;
112     cpu_type_ = DynamicImages::GetNativeCPUType();
113   }
114 
115   GatherSystemInformation();
116 }
117 
~MinidumpGenerator()118 MinidumpGenerator::~MinidumpGenerator() {
119   delete dynamic_images_;
120 }
121 
122 char MinidumpGenerator::build_string_[16];
123 int MinidumpGenerator::os_major_version_ = 0;
124 int MinidumpGenerator::os_minor_version_ = 0;
125 int MinidumpGenerator::os_build_number_ = 0;
126 
127 // static
GatherSystemInformation()128 void MinidumpGenerator::GatherSystemInformation() {
129   // If this is non-zero, then we've already gathered the information
130   if (os_major_version_)
131     return;
132 
133   // This code extracts the version and build information from the OS
134   CFStringRef vers_path =
135     CFSTR("/System/Library/CoreServices/SystemVersion.plist");
136   CFURLRef sys_vers =
137     CFURLCreateWithFileSystemPath(NULL,
138                                   vers_path,
139                                   kCFURLPOSIXPathStyle,
140                                   false);
141   CFReadStreamRef read_stream = CFReadStreamCreateWithFile(NULL, sys_vers);
142   CFRelease(sys_vers);
143   if (!read_stream) {
144     return;
145   }
146   if (!CFReadStreamOpen(read_stream)) {
147     CFRelease(read_stream);
148     return;
149   }
150   CFMutableDataRef data = NULL;
151   while (true) {
152     // Actual data file tests: Mac at 480 bytes and iOS at 413 bytes.
153     const CFIndex kMaxBufferLength = 1024;
154     UInt8 data_bytes[kMaxBufferLength];
155     CFIndex num_bytes_read =
156       CFReadStreamRead(read_stream, data_bytes, kMaxBufferLength);
157     if (num_bytes_read < 0) {
158       if (data) {
159         CFRelease(data);
160         data = NULL;
161       }
162       break;
163     } else if (num_bytes_read == 0) {
164       break;
165     } else if (!data) {
166       data = CFDataCreateMutable(NULL, 0);
167     }
168     CFDataAppendBytes(data, data_bytes, num_bytes_read);
169   }
170   CFReadStreamClose(read_stream);
171   CFRelease(read_stream);
172   if (!data) {
173     return;
174   }
175   CFDictionaryRef list =
176       static_cast<CFDictionaryRef>(CFPropertyListCreateWithData(
177           NULL, data, kCFPropertyListImmutable, NULL, NULL));
178   CFRelease(data);
179   if (!list) {
180     return;
181   }
182   CFStringRef build_version = static_cast<CFStringRef>
183     (CFDictionaryGetValue(list, CFSTR("ProductBuildVersion")));
184   CFStringRef product_version = static_cast<CFStringRef>
185     (CFDictionaryGetValue(list, CFSTR("ProductVersion")));
186   string build_str = ConvertToString(build_version);
187   string product_str = ConvertToString(product_version);
188 
189   CFRelease(list);
190 
191   strlcpy(build_string_, build_str.c_str(), sizeof(build_string_));
192 
193   // Parse the string that looks like "10.4.8"
194   os_major_version_ = IntegerValueAtIndex(product_str, 0);
195   os_minor_version_ = IntegerValueAtIndex(product_str, 1);
196   os_build_number_ = IntegerValueAtIndex(product_str, 2);
197 }
198 
SetTaskContext(breakpad_ucontext_t * task_context)199 void MinidumpGenerator::SetTaskContext(breakpad_ucontext_t* task_context) {
200   task_context_ = task_context;
201 }
202 
UniqueNameInDirectory(const string & dir,string * unique_name)203 string MinidumpGenerator::UniqueNameInDirectory(const string& dir,
204                                                 string* unique_name) {
205   CFUUIDRef uuid = CFUUIDCreate(NULL);
206   CFStringRef uuid_cfstr = CFUUIDCreateString(NULL, uuid);
207   CFRelease(uuid);
208   string file_name(ConvertToString(uuid_cfstr));
209   CFRelease(uuid_cfstr);
210   string path(dir);
211 
212   // Ensure that the directory (if non-empty) has a trailing slash so that
213   // we can append the file name and have a valid pathname.
214   if (!dir.empty()) {
215     if (dir.at(dir.size() - 1) != '/')
216       path.append(1, '/');
217   }
218 
219   path.append(file_name);
220   path.append(".dmp");
221 
222   if (unique_name)
223     *unique_name = file_name;
224 
225   return path;
226 }
227 
Write(const char * path)228 bool MinidumpGenerator::Write(const char* path) {
229   WriteStreamFN writers[] = {
230     &MinidumpGenerator::WriteThreadListStream,
231     &MinidumpGenerator::WriteMemoryListStream,
232     &MinidumpGenerator::WriteSystemInfoStream,
233     &MinidumpGenerator::WriteModuleListStream,
234     &MinidumpGenerator::WriteMiscInfoStream,
235     &MinidumpGenerator::WriteBreakpadInfoStream,
236     // Exception stream needs to be the last entry in this array as it may
237     // be omitted in the case where the minidump is written without an
238     // exception.
239     &MinidumpGenerator::WriteExceptionStream,
240   };
241   bool result = false;
242 
243   // If opening was successful, create the header, directory, and call each
244   // writer.  The destructor for the TypedMDRVAs will cause the data to be
245   // flushed.  The destructor for the MinidumpFileWriter will close the file.
246   if (writer_.Open(path)) {
247     TypedMDRVA<MDRawHeader> header(&writer_);
248     TypedMDRVA<MDRawDirectory> dir(&writer_);
249 
250     if (!header.Allocate())
251       return false;
252 
253     int writer_count = static_cast<int>(sizeof(writers) / sizeof(writers[0]));
254 
255     // If we don't have exception information, don't write out the
256     // exception stream
257     if (!exception_thread_ && !exception_type_)
258       --writer_count;
259 
260     // Add space for all writers
261     if (!dir.AllocateArray(writer_count))
262       return false;
263 
264     MDRawHeader* header_ptr = header.get();
265     header_ptr->signature = MD_HEADER_SIGNATURE;
266     header_ptr->version = MD_HEADER_VERSION;
267     time(reinterpret_cast<time_t*>(&(header_ptr->time_date_stamp)));
268     header_ptr->stream_count = writer_count;
269     header_ptr->stream_directory_rva = dir.position();
270 
271     MDRawDirectory local_dir;
272     result = true;
273     for (int i = 0; (result) && (i < writer_count); ++i) {
274       result = (this->*writers[i])(&local_dir);
275 
276       if (result)
277         dir.CopyIndex(i, &local_dir);
278     }
279   }
280   return result;
281 }
282 
CalculateStackSize(mach_vm_address_t start_addr)283 size_t MinidumpGenerator::CalculateStackSize(mach_vm_address_t start_addr) {
284   mach_vm_address_t stack_region_base = start_addr;
285   mach_vm_size_t stack_region_size;
286   natural_t nesting_level = 0;
287   vm_region_submap_info_64 submap_info;
288   mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64;
289 
290   vm_region_recurse_info_t region_info;
291   region_info = reinterpret_cast<vm_region_recurse_info_t>(&submap_info);
292 
293   if (start_addr == 0) {
294     return 0;
295   }
296 
297   kern_return_t result =
298     mach_vm_region_recurse(crashing_task_, &stack_region_base,
299                            &stack_region_size, &nesting_level,
300                            region_info, &info_count);
301 
302   if (result != KERN_SUCCESS || start_addr < stack_region_base) {
303     // Failure or stack corruption, since mach_vm_region had to go
304     // higher in the process address space to find a valid region.
305     return 0;
306   }
307 
308   unsigned int tag = submap_info.user_tag;
309 
310   // If the user tag is VM_MEMORY_STACK, look for more readable regions with
311   // the same tag placed immediately above the computed stack region. Under
312   // some circumstances, the stack for thread 0 winds up broken up into
313   // multiple distinct abutting regions. This can happen for several reasons,
314   // including user code that calls setrlimit(RLIMIT_STACK, ...) or changes
315   // the access on stack pages by calling mprotect.
316   if (tag == VM_MEMORY_STACK) {
317     while (true) {
318       mach_vm_address_t next_region_base = stack_region_base +
319                                            stack_region_size;
320       mach_vm_address_t proposed_next_region_base = next_region_base;
321       mach_vm_size_t next_region_size;
322       nesting_level = 0;
323       info_count = VM_REGION_SUBMAP_INFO_COUNT_64;
324       result = mach_vm_region_recurse(crashing_task_, &next_region_base,
325                                       &next_region_size, &nesting_level,
326                                       region_info, &info_count);
327       if (result != KERN_SUCCESS ||
328           next_region_base != proposed_next_region_base ||
329           submap_info.user_tag != tag ||
330           (submap_info.protection & VM_PROT_READ) == 0) {
331         break;
332       }
333 
334       stack_region_size += next_region_size;
335     }
336   }
337 
338   return stack_region_base + stack_region_size - start_addr;
339 }
340 
WriteStackFromStartAddress(mach_vm_address_t start_addr,MDMemoryDescriptor * stack_location)341 bool MinidumpGenerator::WriteStackFromStartAddress(
342     mach_vm_address_t start_addr,
343     MDMemoryDescriptor* stack_location) {
344   UntypedMDRVA memory(&writer_);
345 
346   bool result = false;
347   size_t size = CalculateStackSize(start_addr);
348 
349   if (size == 0) {
350       // In some situations the stack address for the thread can come back 0.
351       // In these cases we skip over the threads in question and stuff the
352       // stack with a clearly borked value.
353       start_addr = 0xDEADBEEF;
354       size = 16;
355       if (!memory.Allocate(size))
356         return false;
357 
358       unsigned long long dummy_stack[2];  // Fill dummy stack with 16 bytes of
359                                           // junk.
360       dummy_stack[0] = 0xDEADBEEF;
361       dummy_stack[1] = 0xDEADBEEF;
362 
363       result = memory.Copy(dummy_stack, size);
364   } else {
365 
366     if (!memory.Allocate(size))
367       return false;
368 
369     if (dynamic_images_) {
370       vector<uint8_t> stack_memory;
371       if (ReadTaskMemory(crashing_task_,
372                          start_addr,
373                          size,
374                          stack_memory) != KERN_SUCCESS) {
375         return false;
376       }
377 
378       result = memory.Copy(&stack_memory[0], size);
379     } else {
380       result = memory.Copy(reinterpret_cast<const void*>(start_addr), size);
381     }
382   }
383 
384   stack_location->start_of_memory_range = start_addr;
385   stack_location->memory = memory.location();
386 
387   return result;
388 }
389 
WriteStack(breakpad_thread_state_data_t state,MDMemoryDescriptor * stack_location)390 bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state,
391                                    MDMemoryDescriptor* stack_location) {
392   switch (cpu_type_) {
393 #ifdef HAS_ARM_SUPPORT
394     case CPU_TYPE_ARM:
395       return WriteStackARM(state, stack_location);
396 #endif
397 #ifdef HAS_ARM64_SUPPORT
398     case CPU_TYPE_ARM64:
399       return WriteStackARM64(state, stack_location);
400 #endif
401 #ifdef HAS_PPC_SUPPORT
402     case CPU_TYPE_POWERPC:
403       return WriteStackPPC(state, stack_location);
404     case CPU_TYPE_POWERPC64:
405       return WriteStackPPC64(state, stack_location);
406 #endif
407 #ifdef HAS_X86_SUPPORT
408     case CPU_TYPE_I386:
409       return WriteStackX86(state, stack_location);
410     case CPU_TYPE_X86_64:
411       return WriteStackX86_64(state, stack_location);
412 #endif
413     default:
414       return false;
415   }
416 }
417 
WriteContext(breakpad_thread_state_data_t state,MDLocationDescriptor * register_location)418 bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
419                                      MDLocationDescriptor* register_location) {
420   switch (cpu_type_) {
421 #ifdef HAS_ARM_SUPPORT
422     case CPU_TYPE_ARM:
423       return WriteContextARM(state, register_location);
424 #endif
425 #ifdef HAS_ARM64_SUPPORT
426     case CPU_TYPE_ARM64:
427       return WriteContextARM64(state, register_location);
428 #endif
429 #ifdef HAS_PPC_SUPPORT
430     case CPU_TYPE_POWERPC:
431       return WriteContextPPC(state, register_location);
432     case CPU_TYPE_POWERPC64:
433       return WriteContextPPC64(state, register_location);
434 #endif
435 #ifdef HAS_X86_SUPPORT
436     case CPU_TYPE_I386:
437       return WriteContextX86(state, register_location);
438     case CPU_TYPE_X86_64:
439       return WriteContextX86_64(state, register_location);
440 #endif
441     default:
442       return false;
443   }
444 }
445 
CurrentPCForStack(breakpad_thread_state_data_t state)446 uint64_t MinidumpGenerator::CurrentPCForStack(
447     breakpad_thread_state_data_t state) {
448   switch (cpu_type_) {
449 #ifdef HAS_ARM_SUPPORT
450     case CPU_TYPE_ARM:
451       return CurrentPCForStackARM(state);
452 #endif
453 #ifdef HAS_ARM64_SUPPORT
454     case CPU_TYPE_ARM64:
455       return CurrentPCForStackARM64(state);
456 #endif
457 #ifdef HAS_PPC_SUPPORT
458     case CPU_TYPE_POWERPC:
459       return CurrentPCForStackPPC(state);
460     case CPU_TYPE_POWERPC64:
461       return CurrentPCForStackPPC64(state);
462 #endif
463 #ifdef HAS_X86_SUPPORT
464     case CPU_TYPE_I386:
465       return CurrentPCForStackX86(state);
466     case CPU_TYPE_X86_64:
467       return CurrentPCForStackX86_64(state);
468 #endif
469     default:
470       assert(0 && "Unknown CPU type!");
471       return 0;
472   }
473 }
474 
475 #ifdef HAS_ARM_SUPPORT
WriteStackARM(breakpad_thread_state_data_t state,MDMemoryDescriptor * stack_location)476 bool MinidumpGenerator::WriteStackARM(breakpad_thread_state_data_t state,
477                                       MDMemoryDescriptor* stack_location) {
478   arm_thread_state_t* machine_state =
479       reinterpret_cast<arm_thread_state_t*>(state);
480   mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, sp);
481   return WriteStackFromStartAddress(start_addr, stack_location);
482 }
483 
484 uint64_t
CurrentPCForStackARM(breakpad_thread_state_data_t state)485 MinidumpGenerator::CurrentPCForStackARM(breakpad_thread_state_data_t state) {
486   arm_thread_state_t* machine_state =
487       reinterpret_cast<arm_thread_state_t*>(state);
488 
489   return REGISTER_FROM_THREADSTATE(machine_state, pc);
490 }
491 
WriteContextARM(breakpad_thread_state_data_t state,MDLocationDescriptor * register_location)492 bool MinidumpGenerator::WriteContextARM(breakpad_thread_state_data_t state,
493                                         MDLocationDescriptor* register_location)
494 {
495   TypedMDRVA<MDRawContextARM> context(&writer_);
496   arm_thread_state_t* machine_state =
497       reinterpret_cast<arm_thread_state_t*>(state);
498 
499   if (!context.Allocate())
500     return false;
501 
502   *register_location = context.location();
503   MDRawContextARM* context_ptr = context.get();
504   context_ptr->context_flags = MD_CONTEXT_ARM_FULL;
505 
506 #define AddGPR(a) context_ptr->iregs[a] = REGISTER_FROM_THREADSTATE(machine_state, r[a])
507 
508   context_ptr->iregs[13] = REGISTER_FROM_THREADSTATE(machine_state, sp);
509   context_ptr->iregs[14] = REGISTER_FROM_THREADSTATE(machine_state, lr);
510   context_ptr->iregs[15] = REGISTER_FROM_THREADSTATE(machine_state, pc);
511   context_ptr->cpsr = REGISTER_FROM_THREADSTATE(machine_state, cpsr);
512 
513   AddGPR(0);
514   AddGPR(1);
515   AddGPR(2);
516   AddGPR(3);
517   AddGPR(4);
518   AddGPR(5);
519   AddGPR(6);
520   AddGPR(7);
521   AddGPR(8);
522   AddGPR(9);
523   AddGPR(10);
524   AddGPR(11);
525   AddGPR(12);
526 #undef AddGPR
527 
528   return true;
529 }
530 #endif
531 
532 #ifdef HAS_ARM64_SUPPORT
WriteStackARM64(breakpad_thread_state_data_t state,MDMemoryDescriptor * stack_location)533 bool MinidumpGenerator::WriteStackARM64(breakpad_thread_state_data_t state,
534                                         MDMemoryDescriptor* stack_location) {
535   arm_thread_state64_t* machine_state =
536       reinterpret_cast<arm_thread_state64_t*>(state);
537   mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, sp);
538   return WriteStackFromStartAddress(start_addr, stack_location);
539 }
540 
541 uint64_t
CurrentPCForStackARM64(breakpad_thread_state_data_t state)542 MinidumpGenerator::CurrentPCForStackARM64(breakpad_thread_state_data_t state) {
543   arm_thread_state64_t* machine_state =
544       reinterpret_cast<arm_thread_state64_t*>(state);
545 
546   return REGISTER_FROM_THREADSTATE(machine_state, pc);
547 }
548 
549 bool
WriteContextARM64(breakpad_thread_state_data_t state,MDLocationDescriptor * register_location)550 MinidumpGenerator::WriteContextARM64(breakpad_thread_state_data_t state,
551                                      MDLocationDescriptor* register_location)
552 {
553   TypedMDRVA<MDRawContextARM64_Old> context(&writer_);
554   arm_thread_state64_t* machine_state =
555       reinterpret_cast<arm_thread_state64_t*>(state);
556 
557   if (!context.Allocate())
558     return false;
559 
560   *register_location = context.location();
561   MDRawContextARM64_Old* context_ptr = context.get();
562   context_ptr->context_flags = MD_CONTEXT_ARM64_FULL_OLD;
563 
564 #define AddGPR(a)                                                              \
565   context_ptr->iregs[a] = ARRAY_REGISTER_FROM_THREADSTATE(machine_state, x, a)
566 
567   context_ptr->iregs[29] = REGISTER_FROM_THREADSTATE(machine_state, fp);
568   context_ptr->iregs[30] = REGISTER_FROM_THREADSTATE(machine_state, lr);
569   context_ptr->iregs[31] = REGISTER_FROM_THREADSTATE(machine_state, sp);
570   context_ptr->iregs[32] = REGISTER_FROM_THREADSTATE(machine_state, pc);
571   context_ptr->cpsr = REGISTER_FROM_THREADSTATE(machine_state, cpsr);
572 
573   AddGPR(0);
574   AddGPR(1);
575   AddGPR(2);
576   AddGPR(3);
577   AddGPR(4);
578   AddGPR(5);
579   AddGPR(6);
580   AddGPR(7);
581   AddGPR(8);
582   AddGPR(9);
583   AddGPR(10);
584   AddGPR(11);
585   AddGPR(12);
586   AddGPR(13);
587   AddGPR(14);
588   AddGPR(15);
589   AddGPR(16);
590   AddGPR(17);
591   AddGPR(18);
592   AddGPR(19);
593   AddGPR(20);
594   AddGPR(21);
595   AddGPR(22);
596   AddGPR(23);
597   AddGPR(24);
598   AddGPR(25);
599   AddGPR(26);
600   AddGPR(27);
601   AddGPR(28);
602 #undef AddGPR
603 
604   return true;
605 }
606 #endif
607 
608 #ifdef HAS_PCC_SUPPORT
WriteStackPPC(breakpad_thread_state_data_t state,MDMemoryDescriptor * stack_location)609 bool MinidumpGenerator::WriteStackPPC(breakpad_thread_state_data_t state,
610                                       MDMemoryDescriptor* stack_location) {
611   ppc_thread_state_t* machine_state =
612       reinterpret_cast<ppc_thread_state_t*>(state);
613   mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, r1);
614   return WriteStackFromStartAddress(start_addr, stack_location);
615 }
616 
WriteStackPPC64(breakpad_thread_state_data_t state,MDMemoryDescriptor * stack_location)617 bool MinidumpGenerator::WriteStackPPC64(breakpad_thread_state_data_t state,
618                                         MDMemoryDescriptor* stack_location) {
619   ppc_thread_state64_t* machine_state =
620       reinterpret_cast<ppc_thread_state64_t*>(state);
621   mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, r1);
622   return WriteStackFromStartAddress(start_addr, stack_location);
623 }
624 
625 uint64_t
CurrentPCForStackPPC(breakpad_thread_state_data_t state)626 MinidumpGenerator::CurrentPCForStackPPC(breakpad_thread_state_data_t state) {
627   ppc_thread_state_t* machine_state =
628       reinterpret_cast<ppc_thread_state_t*>(state);
629 
630   return REGISTER_FROM_THREADSTATE(machine_state, srr0);
631 }
632 
633 uint64_t
CurrentPCForStackPPC64(breakpad_thread_state_data_t state)634 MinidumpGenerator::CurrentPCForStackPPC64(breakpad_thread_state_data_t state) {
635   ppc_thread_state64_t* machine_state =
636       reinterpret_cast<ppc_thread_state64_t*>(state);
637 
638   return REGISTER_FROM_THREADSTATE(machine_state, srr0);
639 }
640 
WriteContextPPC(breakpad_thread_state_data_t state,MDLocationDescriptor * register_location)641 bool MinidumpGenerator::WriteContextPPC(breakpad_thread_state_data_t state,
642                                         MDLocationDescriptor* register_location)
643 {
644   TypedMDRVA<MDRawContextPPC> context(&writer_);
645   ppc_thread_state_t* machine_state =
646       reinterpret_cast<ppc_thread_state_t*>(state);
647 
648   if (!context.Allocate())
649     return false;
650 
651   *register_location = context.location();
652   MDRawContextPPC* context_ptr = context.get();
653   context_ptr->context_flags = MD_CONTEXT_PPC_BASE;
654 
655 #define AddReg(a) context_ptr->a = static_cast<__typeof__(context_ptr->a)>( \
656     REGISTER_FROM_THREADSTATE(machine_state, a))
657 #define AddGPR(a) context_ptr->gpr[a] = \
658     static_cast<__typeof__(context_ptr->a)>( \
659     REGISTER_FROM_THREADSTATE(machine_state, r ## a)
660 
661   AddReg(srr0);
662   AddReg(cr);
663   AddReg(xer);
664   AddReg(ctr);
665   AddReg(lr);
666   AddReg(vrsave);
667 
668   AddGPR(0);
669   AddGPR(1);
670   AddGPR(2);
671   AddGPR(3);
672   AddGPR(4);
673   AddGPR(5);
674   AddGPR(6);
675   AddGPR(7);
676   AddGPR(8);
677   AddGPR(9);
678   AddGPR(10);
679   AddGPR(11);
680   AddGPR(12);
681   AddGPR(13);
682   AddGPR(14);
683   AddGPR(15);
684   AddGPR(16);
685   AddGPR(17);
686   AddGPR(18);
687   AddGPR(19);
688   AddGPR(20);
689   AddGPR(21);
690   AddGPR(22);
691   AddGPR(23);
692   AddGPR(24);
693   AddGPR(25);
694   AddGPR(26);
695   AddGPR(27);
696   AddGPR(28);
697   AddGPR(29);
698   AddGPR(30);
699   AddGPR(31);
700   AddReg(mq);
701 #undef AddReg
702 #undef AddGPR
703 
704   return true;
705 }
706 
WriteContextPPC64(breakpad_thread_state_data_t state,MDLocationDescriptor * register_location)707 bool MinidumpGenerator::WriteContextPPC64(
708     breakpad_thread_state_data_t state,
709     MDLocationDescriptor* register_location) {
710   TypedMDRVA<MDRawContextPPC64> context(&writer_);
711   ppc_thread_state64_t* machine_state =
712       reinterpret_cast<ppc_thread_state64_t*>(state);
713 
714   if (!context.Allocate())
715     return false;
716 
717   *register_location = context.location();
718   MDRawContextPPC64* context_ptr = context.get();
719   context_ptr->context_flags = MD_CONTEXT_PPC_BASE;
720 
721 #define AddReg(a) context_ptr->a = static_cast<__typeof__(context_ptr->a)>( \
722     REGISTER_FROM_THREADSTATE(machine_state, a))
723 #define AddGPR(a) context_ptr->gpr[a] = \
724     static_cast<__typeof__(context_ptr->a)>( \
725     REGISTER_FROM_THREADSTATE(machine_state, r ## a)
726 
727   AddReg(srr0);
728   AddReg(cr);
729   AddReg(xer);
730   AddReg(ctr);
731   AddReg(lr);
732   AddReg(vrsave);
733 
734   AddGPR(0);
735   AddGPR(1);
736   AddGPR(2);
737   AddGPR(3);
738   AddGPR(4);
739   AddGPR(5);
740   AddGPR(6);
741   AddGPR(7);
742   AddGPR(8);
743   AddGPR(9);
744   AddGPR(10);
745   AddGPR(11);
746   AddGPR(12);
747   AddGPR(13);
748   AddGPR(14);
749   AddGPR(15);
750   AddGPR(16);
751   AddGPR(17);
752   AddGPR(18);
753   AddGPR(19);
754   AddGPR(20);
755   AddGPR(21);
756   AddGPR(22);
757   AddGPR(23);
758   AddGPR(24);
759   AddGPR(25);
760   AddGPR(26);
761   AddGPR(27);
762   AddGPR(28);
763   AddGPR(29);
764   AddGPR(30);
765   AddGPR(31);
766 #undef AddReg
767 #undef AddGPR
768 
769   return true;
770 }
771 
772 #endif
773 
774 #ifdef HAS_X86_SUPPORT
WriteStackX86(breakpad_thread_state_data_t state,MDMemoryDescriptor * stack_location)775 bool MinidumpGenerator::WriteStackX86(breakpad_thread_state_data_t state,
776                                    MDMemoryDescriptor* stack_location) {
777   i386_thread_state_t* machine_state =
778       reinterpret_cast<i386_thread_state_t*>(state);
779 
780   mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, esp);
781   return WriteStackFromStartAddress(start_addr, stack_location);
782 }
783 
WriteStackX86_64(breakpad_thread_state_data_t state,MDMemoryDescriptor * stack_location)784 bool MinidumpGenerator::WriteStackX86_64(breakpad_thread_state_data_t state,
785                                          MDMemoryDescriptor* stack_location) {
786   x86_thread_state64_t* machine_state =
787       reinterpret_cast<x86_thread_state64_t*>(state);
788 
789   mach_vm_address_t start_addr = static_cast<mach_vm_address_t>(
790       REGISTER_FROM_THREADSTATE(machine_state, rsp));
791   return WriteStackFromStartAddress(start_addr, stack_location);
792 }
793 
794 uint64_t
CurrentPCForStackX86(breakpad_thread_state_data_t state)795 MinidumpGenerator::CurrentPCForStackX86(breakpad_thread_state_data_t state) {
796   i386_thread_state_t* machine_state =
797       reinterpret_cast<i386_thread_state_t*>(state);
798 
799   return REGISTER_FROM_THREADSTATE(machine_state, eip);
800 }
801 
802 uint64_t
CurrentPCForStackX86_64(breakpad_thread_state_data_t state)803 MinidumpGenerator::CurrentPCForStackX86_64(breakpad_thread_state_data_t state) {
804   x86_thread_state64_t* machine_state =
805       reinterpret_cast<x86_thread_state64_t*>(state);
806 
807   return REGISTER_FROM_THREADSTATE(machine_state, rip);
808 }
809 
WriteContextX86(breakpad_thread_state_data_t state,MDLocationDescriptor * register_location)810 bool MinidumpGenerator::WriteContextX86(breakpad_thread_state_data_t state,
811                                         MDLocationDescriptor* register_location)
812 {
813   TypedMDRVA<MDRawContextX86> context(&writer_);
814   i386_thread_state_t* machine_state =
815       reinterpret_cast<i386_thread_state_t*>(state);
816 
817   if (!context.Allocate())
818     return false;
819 
820   *register_location = context.location();
821   MDRawContextX86* context_ptr = context.get();
822 
823 #define AddReg(a) context_ptr->a = static_cast<__typeof__(context_ptr->a)>( \
824     REGISTER_FROM_THREADSTATE(machine_state, a))
825 
826   context_ptr->context_flags = MD_CONTEXT_X86;
827   AddReg(eax);
828   AddReg(ebx);
829   AddReg(ecx);
830   AddReg(edx);
831   AddReg(esi);
832   AddReg(edi);
833   AddReg(ebp);
834   AddReg(esp);
835 
836   AddReg(cs);
837   AddReg(ds);
838   AddReg(ss);
839   AddReg(es);
840   AddReg(fs);
841   AddReg(gs);
842   AddReg(eflags);
843 
844   AddReg(eip);
845 #undef AddReg
846 
847   return true;
848 }
849 
WriteContextX86_64(breakpad_thread_state_data_t state,MDLocationDescriptor * register_location)850 bool MinidumpGenerator::WriteContextX86_64(
851     breakpad_thread_state_data_t state,
852     MDLocationDescriptor* register_location) {
853   TypedMDRVA<MDRawContextAMD64> context(&writer_);
854   x86_thread_state64_t* machine_state =
855       reinterpret_cast<x86_thread_state64_t*>(state);
856 
857   if (!context.Allocate())
858     return false;
859 
860   *register_location = context.location();
861   MDRawContextAMD64* context_ptr = context.get();
862 
863 #define AddReg(a) context_ptr->a = static_cast<__typeof__(context_ptr->a)>( \
864     REGISTER_FROM_THREADSTATE(machine_state, a))
865 
866   context_ptr->context_flags = MD_CONTEXT_AMD64;
867   AddReg(rax);
868   AddReg(rbx);
869   AddReg(rcx);
870   AddReg(rdx);
871   AddReg(rdi);
872   AddReg(rsi);
873   AddReg(rbp);
874   AddReg(rsp);
875   AddReg(r8);
876   AddReg(r9);
877   AddReg(r10);
878   AddReg(r11);
879   AddReg(r12);
880   AddReg(r13);
881   AddReg(r14);
882   AddReg(r15);
883   AddReg(rip);
884   // according to AMD's software developer guide, bits above 18 are
885   // not used in the flags register.  Since the minidump format
886   // specifies 32 bits for the flags register, we can truncate safely
887   // with no loss.
888   context_ptr->eflags = static_cast<uint32_t>(REGISTER_FROM_THREADSTATE(machine_state, rflags));
889   AddReg(cs);
890   AddReg(fs);
891   AddReg(gs);
892 #undef AddReg
893 
894   return true;
895 }
896 #endif
897 
GetThreadState(thread_act_t target_thread,thread_state_t state,mach_msg_type_number_t * count)898 bool MinidumpGenerator::GetThreadState(thread_act_t target_thread,
899                                        thread_state_t state,
900                                        mach_msg_type_number_t* count) {
901   if (task_context_ && target_thread == mach_thread_self()) {
902     switch (cpu_type_) {
903 #ifdef HAS_ARM_SUPPORT
904       case CPU_TYPE_ARM:
905         size_t final_size =
906             std::min(static_cast<size_t>(*count), sizeof(arm_thread_state_t));
907         memcpy(state, &task_context_->breakpad_uc_mcontext->__ss, final_size);
908         *count = static_cast<mach_msg_type_number_t>(final_size);
909         return true;
910 #endif
911 #ifdef HAS_ARM64_SUPPORT
912       case CPU_TYPE_ARM64: {
913         size_t final_size =
914             std::min(static_cast<size_t>(*count), sizeof(arm_thread_state64_t));
915         memcpy(state, &task_context_->breakpad_uc_mcontext->__ss, final_size);
916         *count = static_cast<mach_msg_type_number_t>(final_size);
917         return true;
918       }
919 #endif
920 #ifdef HAS_X86_SUPPORT
921     case CPU_TYPE_I386:
922     case CPU_TYPE_X86_64: {
923         size_t state_size = cpu_type_ == CPU_TYPE_I386 ?
924             sizeof(i386_thread_state_t) : sizeof(x86_thread_state64_t);
925         size_t final_size =
926             std::min(static_cast<size_t>(*count), state_size);
927         memcpy(state, &task_context_->breakpad_uc_mcontext->__ss, final_size);
928         *count = static_cast<mach_msg_type_number_t>(final_size);
929         return true;
930       }
931 #endif
932     }
933   }
934 
935   thread_state_flavor_t flavor;
936   switch (cpu_type_) {
937 #ifdef HAS_ARM_SUPPORT
938     case CPU_TYPE_ARM:
939       flavor = ARM_THREAD_STATE;
940       break;
941 #endif
942 #ifdef HAS_ARM64_SUPPORT
943     case CPU_TYPE_ARM64:
944       flavor = ARM_THREAD_STATE64;
945       break;
946 #endif
947 #ifdef HAS_PPC_SUPPORT
948     case CPU_TYPE_POWERPC:
949       flavor = PPC_THREAD_STATE;
950       break;
951     case CPU_TYPE_POWERPC64:
952       flavor = PPC_THREAD_STATE64;
953       break;
954 #endif
955 #ifdef HAS_X86_SUPPORT
956     case CPU_TYPE_I386:
957       flavor = i386_THREAD_STATE;
958       break;
959     case CPU_TYPE_X86_64:
960       flavor = x86_THREAD_STATE64;
961       break;
962 #endif
963     default:
964       return false;
965   }
966   return thread_get_state(target_thread, flavor,
967                           state, count) == KERN_SUCCESS;
968 }
969 
WriteThreadStream(mach_port_t thread_id,MDRawThread * thread)970 bool MinidumpGenerator::WriteThreadStream(mach_port_t thread_id,
971                                           MDRawThread* thread) {
972   breakpad_thread_state_data_t state;
973   mach_msg_type_number_t state_count
974       = static_cast<mach_msg_type_number_t>(sizeof(state));
975 
976   if (GetThreadState(thread_id, state, &state_count)) {
977     if (!WriteStack(state, &thread->stack))
978       return false;
979 
980     memory_blocks_.push_back(thread->stack);
981 
982     if (!WriteContext(state, &thread->thread_context))
983       return false;
984 
985     thread->thread_id = thread_id;
986   } else {
987     return false;
988   }
989 
990   return true;
991 }
992 
WriteThreadListStream(MDRawDirectory * thread_list_stream)993 bool MinidumpGenerator::WriteThreadListStream(
994     MDRawDirectory* thread_list_stream) {
995   TypedMDRVA<MDRawThreadList> list(&writer_);
996   thread_act_port_array_t threads_for_task;
997   mach_msg_type_number_t thread_count;
998   int non_generator_thread_count;
999 
1000   if (task_threads(crashing_task_, &threads_for_task, &thread_count))
1001     return false;
1002 
1003   // Don't include the generator thread
1004   if (handler_thread_ != MACH_PORT_NULL)
1005     non_generator_thread_count = thread_count - 1;
1006   else
1007     non_generator_thread_count = thread_count;
1008   if (!list.AllocateObjectAndArray(non_generator_thread_count,
1009                                    sizeof(MDRawThread)))
1010     return false;
1011 
1012   thread_list_stream->stream_type = MD_THREAD_LIST_STREAM;
1013   thread_list_stream->location = list.location();
1014 
1015   list.get()->number_of_threads = non_generator_thread_count;
1016 
1017   MDRawThread thread;
1018   int thread_idx = 0;
1019 
1020   for (unsigned int i = 0; i < thread_count; ++i) {
1021     memset(&thread, 0, sizeof(MDRawThread));
1022 
1023     if (threads_for_task[i] != handler_thread_) {
1024       if (!WriteThreadStream(threads_for_task[i], &thread))
1025         return false;
1026 
1027       list.CopyIndexAfterObject(thread_idx++, &thread, sizeof(MDRawThread));
1028     }
1029   }
1030 
1031   return true;
1032 }
1033 
WriteMemoryListStream(MDRawDirectory * memory_list_stream)1034 bool MinidumpGenerator::WriteMemoryListStream(
1035     MDRawDirectory* memory_list_stream) {
1036   TypedMDRVA<MDRawMemoryList> list(&writer_);
1037 
1038   // If the dump has an exception, include some memory around the
1039   // instruction pointer.
1040   const size_t kIPMemorySize = 256;  // bytes
1041   bool have_ip_memory = false;
1042   MDMemoryDescriptor ip_memory_d;
1043   if (exception_thread_ && exception_type_) {
1044     breakpad_thread_state_data_t state;
1045     mach_msg_type_number_t stateCount
1046       = static_cast<mach_msg_type_number_t>(sizeof(state));
1047 
1048     if (GetThreadState(exception_thread_, state, &stateCount)) {
1049       uint64_t ip = CurrentPCForStack(state);
1050       // Bound it to the upper and lower bounds of the region
1051       // it's contained within. If it's not in a known memory region,
1052       // don't bother trying to write it.
1053       mach_vm_address_t addr = static_cast<vm_address_t>(ip);
1054       mach_vm_size_t size;
1055       natural_t nesting_level = 0;
1056       vm_region_submap_info_64 info;
1057       mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64;
1058       vm_region_recurse_info_t recurse_info;
1059       recurse_info = reinterpret_cast<vm_region_recurse_info_t>(&info);
1060 
1061       kern_return_t ret =
1062         mach_vm_region_recurse(crashing_task_,
1063                                &addr,
1064                                &size,
1065                                &nesting_level,
1066                                recurse_info,
1067                                &info_count);
1068       if (ret == KERN_SUCCESS && ip >= addr && ip < (addr + size)) {
1069         // Try to get 128 bytes before and after the IP, but
1070         // settle for whatever's available.
1071         ip_memory_d.start_of_memory_range =
1072           std::max(uintptr_t(addr),
1073                    uintptr_t(ip - (kIPMemorySize / 2)));
1074         uintptr_t end_of_range =
1075           std::min(uintptr_t(ip + (kIPMemorySize / 2)),
1076                    uintptr_t(addr + size));
1077         uintptr_t range_diff = end_of_range -
1078             static_cast<uintptr_t>(ip_memory_d.start_of_memory_range);
1079         ip_memory_d.memory.data_size = static_cast<uint32_t>(range_diff);
1080         have_ip_memory = true;
1081         // This needs to get appended to the list even though
1082         // the memory bytes aren't filled in yet so the entire
1083         // list can be written first. The memory bytes will get filled
1084         // in after the memory list is written.
1085         memory_blocks_.push_back(ip_memory_d);
1086       }
1087     }
1088   }
1089 
1090   // Now fill in the memory list and write it.
1091   size_t memory_count = memory_blocks_.size();
1092   if (!list.AllocateObjectAndArray(memory_count,
1093                                    sizeof(MDMemoryDescriptor)))
1094     return false;
1095 
1096   memory_list_stream->stream_type = MD_MEMORY_LIST_STREAM;
1097   memory_list_stream->location = list.location();
1098 
1099   list.get()->number_of_memory_ranges = static_cast<uint32_t>(memory_count);
1100 
1101   unsigned int i;
1102   for (i = 0; i < memory_count; ++i) {
1103     list.CopyIndexAfterObject(i, &memory_blocks_[i],
1104                               sizeof(MDMemoryDescriptor));
1105   }
1106 
1107   if (have_ip_memory) {
1108     // Now read the memory around the instruction pointer.
1109     UntypedMDRVA ip_memory(&writer_);
1110     if (!ip_memory.Allocate(ip_memory_d.memory.data_size))
1111       return false;
1112 
1113     if (dynamic_images_) {
1114       // Out-of-process.
1115       vector<uint8_t> memory;
1116       if (ReadTaskMemory(crashing_task_,
1117                          ip_memory_d.start_of_memory_range,
1118                          ip_memory_d.memory.data_size,
1119                          memory) != KERN_SUCCESS) {
1120         return false;
1121       }
1122 
1123       ip_memory.Copy(&memory[0], ip_memory_d.memory.data_size);
1124     } else {
1125       // In-process, just copy from local memory.
1126       ip_memory.Copy(
1127         reinterpret_cast<const void*>(ip_memory_d.start_of_memory_range),
1128         ip_memory_d.memory.data_size);
1129     }
1130 
1131     ip_memory_d.memory = ip_memory.location();
1132     // Write this again now that the data location is filled in.
1133     list.CopyIndexAfterObject(i - 1, &ip_memory_d,
1134                               sizeof(MDMemoryDescriptor));
1135   }
1136 
1137   return true;
1138 }
1139 
1140 bool
WriteExceptionStream(MDRawDirectory * exception_stream)1141 MinidumpGenerator::WriteExceptionStream(MDRawDirectory* exception_stream) {
1142   TypedMDRVA<MDRawExceptionStream> exception(&writer_);
1143 
1144   if (!exception.Allocate())
1145     return false;
1146 
1147   exception_stream->stream_type = MD_EXCEPTION_STREAM;
1148   exception_stream->location = exception.location();
1149   MDRawExceptionStream* exception_ptr = exception.get();
1150   exception_ptr->thread_id = exception_thread_;
1151 
1152   // This naming is confusing, but it is the proper translation from
1153   // mach naming to minidump naming.
1154   exception_ptr->exception_record.exception_code = exception_type_;
1155   exception_ptr->exception_record.exception_flags = exception_code_;
1156 
1157   breakpad_thread_state_data_t state;
1158   mach_msg_type_number_t state_count
1159       = static_cast<mach_msg_type_number_t>(sizeof(state));
1160 
1161   if (!GetThreadState(exception_thread_, state, &state_count))
1162     return false;
1163 
1164   if (!WriteContext(state, &exception_ptr->thread_context))
1165     return false;
1166 
1167   if (exception_type_ == EXC_BAD_ACCESS)
1168     exception_ptr->exception_record.exception_address = exception_subcode_;
1169   else
1170     exception_ptr->exception_record.exception_address = CurrentPCForStack(state);
1171 
1172   return true;
1173 }
1174 
WriteSystemInfoStream(MDRawDirectory * system_info_stream)1175 bool MinidumpGenerator::WriteSystemInfoStream(
1176     MDRawDirectory* system_info_stream) {
1177   TypedMDRVA<MDRawSystemInfo> info(&writer_);
1178 
1179   if (!info.Allocate())
1180     return false;
1181 
1182   system_info_stream->stream_type = MD_SYSTEM_INFO_STREAM;
1183   system_info_stream->location = info.location();
1184 
1185   // CPU Information
1186   uint32_t number_of_processors;
1187   size_t len = sizeof(number_of_processors);
1188   sysctlbyname("hw.ncpu", &number_of_processors, &len, NULL, 0);
1189   MDRawSystemInfo* info_ptr = info.get();
1190 
1191   switch (cpu_type_) {
1192 #ifdef HAS_ARM_SUPPORT
1193     case CPU_TYPE_ARM:
1194       info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_ARM;
1195       break;
1196 #endif
1197 #ifdef HAS_ARM64_SUPPORT
1198     case CPU_TYPE_ARM64:
1199       info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_ARM64_OLD;
1200       break;
1201 #endif
1202 #ifdef HAS_PPC_SUPPORT
1203     case CPU_TYPE_POWERPC:
1204     case CPU_TYPE_POWERPC64:
1205       info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_PPC;
1206       break;
1207 #endif
1208 #ifdef HAS_X86_SUPPORT
1209     case CPU_TYPE_I386:
1210     case CPU_TYPE_X86_64:
1211       if (cpu_type_ == CPU_TYPE_I386)
1212         info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_X86;
1213       else
1214         info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_AMD64;
1215 #ifdef __i386__
1216       // ebx is used for PIC code, so we need
1217       // to preserve it.
1218 #define cpuid(op,eax,ebx,ecx,edx)      \
1219   asm ("pushl %%ebx   \n\t"            \
1220        "cpuid         \n\t"            \
1221        "movl %%ebx,%1 \n\t"            \
1222        "popl %%ebx"                    \
1223        : "=a" (eax),                   \
1224          "=g" (ebx),                   \
1225          "=c" (ecx),                   \
1226          "=d" (edx)                    \
1227        : "0" (op))
1228 #elif defined(__x86_64__)
1229 
1230 #define cpuid(op,eax,ebx,ecx,edx)      \
1231   asm ("cpuid         \n\t"            \
1232        : "=a" (eax),                   \
1233          "=b" (ebx),                   \
1234          "=c" (ecx),                   \
1235          "=d" (edx)                    \
1236        : "0" (op))
1237 #endif
1238 
1239 #if defined(__i386__) || defined(__x86_64__)
1240       int unused, unused2;
1241       // get vendor id
1242       cpuid(0, unused, info_ptr->cpu.x86_cpu_info.vendor_id[0],
1243             info_ptr->cpu.x86_cpu_info.vendor_id[2],
1244             info_ptr->cpu.x86_cpu_info.vendor_id[1]);
1245       // get version and feature info
1246       cpuid(1, info_ptr->cpu.x86_cpu_info.version_information, unused, unused2,
1247             info_ptr->cpu.x86_cpu_info.feature_information);
1248 
1249       // family
1250       info_ptr->processor_level =
1251         (info_ptr->cpu.x86_cpu_info.version_information & 0xF00) >> 8;
1252       // 0xMMSS (Model, Stepping)
1253       info_ptr->processor_revision = static_cast<uint16_t>(
1254           (info_ptr->cpu.x86_cpu_info.version_information & 0xF) |
1255           ((info_ptr->cpu.x86_cpu_info.version_information & 0xF0) << 4));
1256 
1257       // decode extended model info
1258       if (info_ptr->processor_level == 0xF ||
1259           info_ptr->processor_level == 0x6) {
1260         info_ptr->processor_revision |=
1261           ((info_ptr->cpu.x86_cpu_info.version_information & 0xF0000) >> 4);
1262       }
1263 
1264       // decode extended family info
1265       if (info_ptr->processor_level == 0xF) {
1266         info_ptr->processor_level +=
1267           ((info_ptr->cpu.x86_cpu_info.version_information & 0xFF00000) >> 20);
1268       }
1269 
1270 #endif  // __i386__ || __x86_64_
1271       break;
1272 #endif  // HAS_X86_SUPPORT
1273     default:
1274       info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_UNKNOWN;
1275       break;
1276   }
1277 
1278   info_ptr->number_of_processors = static_cast<uint8_t>(number_of_processors);
1279 #if TARGET_OS_IPHONE
1280   info_ptr->platform_id = MD_OS_IOS;
1281 #else
1282   info_ptr->platform_id = MD_OS_MAC_OS_X;
1283 #endif  // TARGET_OS_IPHONE
1284 
1285   MDLocationDescriptor build_string_loc;
1286 
1287   if (!writer_.WriteString(build_string_, 0,
1288                            &build_string_loc))
1289     return false;
1290 
1291   info_ptr->csd_version_rva = build_string_loc.rva;
1292   info_ptr->major_version = os_major_version_;
1293   info_ptr->minor_version = os_minor_version_;
1294   info_ptr->build_number = os_build_number_;
1295 
1296   return true;
1297 }
1298 
WriteModuleStream(unsigned int index,MDRawModule * module)1299 bool MinidumpGenerator::WriteModuleStream(unsigned int index,
1300                                           MDRawModule* module) {
1301   if (dynamic_images_) {
1302     // we're in a different process than the crashed process
1303     DynamicImage* image = dynamic_images_->GetImage(index);
1304 
1305     if (!image)
1306       return false;
1307 
1308     memset(module, 0, sizeof(MDRawModule));
1309 
1310     MDLocationDescriptor string_location;
1311 
1312     string name = image->GetFilePath();
1313     if (!writer_.WriteString(name.c_str(), 0, &string_location))
1314       return false;
1315 
1316     module->base_of_image = image->GetVMAddr() + image->GetVMAddrSlide();
1317     module->size_of_image = static_cast<uint32_t>(image->GetVMSize());
1318     module->module_name_rva = string_location.rva;
1319 
1320     // We'll skip the executable module, because they don't have
1321     // LC_ID_DYLIB load commands, and the crash processing server gets
1322     // version information from the Plist file, anyway.
1323     if (index != static_cast<uint32_t>(FindExecutableModule())) {
1324       module->version_info.signature = MD_VSFIXEDFILEINFO_SIGNATURE;
1325       module->version_info.struct_version |= MD_VSFIXEDFILEINFO_VERSION;
1326       // Convert MAC dylib version format, which is a 32 bit number, to the
1327       // format used by minidump.  The mac format is <16 bits>.<8 bits>.<8 bits>
1328       // so it fits nicely into the windows version with some massaging
1329       // The mapping is:
1330       //    1) upper 16 bits of MAC version go to lower 16 bits of product HI
1331       //    2) Next most significant 8 bits go to upper 16 bits of product LO
1332       //    3) Least significant 8 bits go to lower 16 bits of product LO
1333       uint32_t modVersion = image->GetVersion();
1334       module->version_info.file_version_hi = 0;
1335       module->version_info.file_version_hi = modVersion >> 16;
1336       module->version_info.file_version_lo |= (modVersion & 0xff00)  << 8;
1337       module->version_info.file_version_lo |= (modVersion & 0xff);
1338     }
1339 
1340     if (!WriteCVRecord(module, image->GetCPUType(), name.c_str(), false)) {
1341       return false;
1342     }
1343   } else {
1344     // Getting module info in the crashed process
1345     const breakpad_mach_header* header;
1346     header = (breakpad_mach_header*)_dyld_get_image_header(index);
1347     if (!header)
1348       return false;
1349 
1350 #ifdef __LP64__
1351     assert(header->magic == MH_MAGIC_64);
1352 
1353     if(header->magic != MH_MAGIC_64)
1354       return false;
1355 #else
1356     assert(header->magic == MH_MAGIC);
1357 
1358     if(header->magic != MH_MAGIC)
1359       return false;
1360 #endif
1361 
1362     int cpu_type = header->cputype;
1363     unsigned long slide = _dyld_get_image_vmaddr_slide(index);
1364     const char* name = _dyld_get_image_name(index);
1365     const struct load_command* cmd =
1366         reinterpret_cast<const struct load_command*>(header + 1);
1367 
1368     memset(module, 0, sizeof(MDRawModule));
1369 
1370     for (unsigned int i = 0; cmd && (i < header->ncmds); i++) {
1371       if (cmd->cmd == LC_SEGMENT_ARCH) {
1372 
1373         const breakpad_mach_segment_command* seg =
1374             reinterpret_cast<const breakpad_mach_segment_command*>(cmd);
1375 
1376         if (!strcmp(seg->segname, "__TEXT")) {
1377           MDLocationDescriptor string_location;
1378 
1379           if (!writer_.WriteString(name, 0, &string_location))
1380             return false;
1381 
1382           module->base_of_image = seg->vmaddr + slide;
1383           module->size_of_image = static_cast<uint32_t>(seg->vmsize);
1384           module->module_name_rva = string_location.rva;
1385 
1386           bool in_memory = false;
1387 #if TARGET_OS_IPHONE
1388           in_memory = true;
1389 #endif
1390           if (!WriteCVRecord(module, cpu_type, name, in_memory))
1391             return false;
1392 
1393           return true;
1394         }
1395       }
1396 
1397       cmd = reinterpret_cast<struct load_command*>((char*)cmd + cmd->cmdsize);
1398     }
1399   }
1400 
1401   return true;
1402 }
1403 
FindExecutableModule()1404 int MinidumpGenerator::FindExecutableModule() {
1405   if (dynamic_images_) {
1406     int index = dynamic_images_->GetExecutableImageIndex();
1407 
1408     if (index >= 0) {
1409       return index;
1410     }
1411   } else {
1412     int image_count = _dyld_image_count();
1413     const struct mach_header* header;
1414 
1415     for (int index = 0; index < image_count; ++index) {
1416       header = _dyld_get_image_header(index);
1417 
1418       if (header->filetype == MH_EXECUTE)
1419         return index;
1420     }
1421   }
1422 
1423   // failed - just use the first image
1424   return 0;
1425 }
1426 
WriteCVRecord(MDRawModule * module,int cpu_type,const char * module_path,bool in_memory)1427 bool MinidumpGenerator::WriteCVRecord(MDRawModule* module, int cpu_type,
1428                                       const char* module_path, bool in_memory) {
1429   TypedMDRVA<MDCVInfoPDB70> cv(&writer_);
1430 
1431   // Only return the last path component of the full module path
1432   const char* module_name = strrchr(module_path, '/');
1433 
1434   // Increment past the slash
1435   if (module_name)
1436     ++module_name;
1437   else
1438     module_name = "<Unknown>";
1439 
1440   size_t module_name_length = strlen(module_name);
1441 
1442   if (!cv.AllocateObjectAndArray(module_name_length + 1, sizeof(uint8_t)))
1443     return false;
1444 
1445   if (!cv.CopyIndexAfterObject(0, module_name, module_name_length))
1446     return false;
1447 
1448   module->cv_record = cv.location();
1449   MDCVInfoPDB70* cv_ptr = cv.get();
1450   cv_ptr->cv_signature = MD_CVINFOPDB70_SIGNATURE;
1451   cv_ptr->age = 0;
1452 
1453   // Get the module identifier
1454   unsigned char identifier[16];
1455   bool result = false;
1456   if (in_memory) {
1457     MacFileUtilities::MachoID macho(
1458         reinterpret_cast<void*>(module->base_of_image),
1459         static_cast<size_t>(module->size_of_image));
1460     result = macho.UUIDCommand(cpu_type, CPU_SUBTYPE_MULTIPLE, identifier);
1461     if (!result)
1462       result = macho.MD5(cpu_type, CPU_SUBTYPE_MULTIPLE, identifier);
1463   }
1464 
1465   if (!result) {
1466      FileID file_id(module_path);
1467      result = file_id.MachoIdentifier(cpu_type, CPU_SUBTYPE_MULTIPLE,
1468                                       identifier);
1469   }
1470 
1471   if (result) {
1472     cv_ptr->signature.data1 =
1473         static_cast<uint32_t>(identifier[0]) << 24 |
1474         static_cast<uint32_t>(identifier[1]) << 16 |
1475         static_cast<uint32_t>(identifier[2]) << 8 |
1476         static_cast<uint32_t>(identifier[3]);
1477     cv_ptr->signature.data2 =
1478         static_cast<uint16_t>(identifier[4] << 8) | identifier[5];
1479     cv_ptr->signature.data3 =
1480         static_cast<uint16_t>(identifier[6] << 8) | identifier[7];
1481     cv_ptr->signature.data4[0] = identifier[8];
1482     cv_ptr->signature.data4[1] = identifier[9];
1483     cv_ptr->signature.data4[2] = identifier[10];
1484     cv_ptr->signature.data4[3] = identifier[11];
1485     cv_ptr->signature.data4[4] = identifier[12];
1486     cv_ptr->signature.data4[5] = identifier[13];
1487     cv_ptr->signature.data4[6] = identifier[14];
1488     cv_ptr->signature.data4[7] = identifier[15];
1489   }
1490 
1491   return true;
1492 }
1493 
WriteModuleListStream(MDRawDirectory * module_list_stream)1494 bool MinidumpGenerator::WriteModuleListStream(
1495     MDRawDirectory* module_list_stream) {
1496   TypedMDRVA<MDRawModuleList> list(&writer_);
1497 
1498   uint32_t image_count = dynamic_images_ ?
1499       dynamic_images_->GetImageCount() :
1500       _dyld_image_count();
1501 
1502   if (!list.AllocateObjectAndArray(image_count, MD_MODULE_SIZE))
1503     return false;
1504 
1505   module_list_stream->stream_type = MD_MODULE_LIST_STREAM;
1506   module_list_stream->location = list.location();
1507   list.get()->number_of_modules = static_cast<uint32_t>(image_count);
1508 
1509   // Write out the executable module as the first one
1510   MDRawModule module;
1511   uint32_t executableIndex = FindExecutableModule();
1512 
1513   if (!WriteModuleStream(static_cast<unsigned>(executableIndex), &module)) {
1514     return false;
1515   }
1516 
1517   list.CopyIndexAfterObject(0, &module, MD_MODULE_SIZE);
1518   int destinationIndex = 1;  // Write all other modules after this one
1519 
1520   for (uint32_t i = 0; i < image_count; ++i) {
1521     if (i != executableIndex) {
1522       if (!WriteModuleStream(static_cast<unsigned>(i), &module)) {
1523         return false;
1524       }
1525 
1526       list.CopyIndexAfterObject(destinationIndex++, &module, MD_MODULE_SIZE);
1527     }
1528   }
1529 
1530   return true;
1531 }
1532 
WriteMiscInfoStream(MDRawDirectory * misc_info_stream)1533 bool MinidumpGenerator::WriteMiscInfoStream(MDRawDirectory* misc_info_stream) {
1534   TypedMDRVA<MDRawMiscInfo> info(&writer_);
1535 
1536   if (!info.Allocate())
1537     return false;
1538 
1539   misc_info_stream->stream_type = MD_MISC_INFO_STREAM;
1540   misc_info_stream->location = info.location();
1541 
1542   MDRawMiscInfo* info_ptr = info.get();
1543   info_ptr->size_of_info = static_cast<uint32_t>(sizeof(MDRawMiscInfo));
1544   info_ptr->flags1 = MD_MISCINFO_FLAGS1_PROCESS_ID |
1545     MD_MISCINFO_FLAGS1_PROCESS_TIMES |
1546     MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO;
1547 
1548   // Process ID
1549   info_ptr->process_id = getpid();
1550 
1551   // Times
1552   struct rusage usage;
1553   if (getrusage(RUSAGE_SELF, &usage) != -1) {
1554     // Omit the fractional time since the MDRawMiscInfo only wants seconds
1555     info_ptr->process_user_time =
1556         static_cast<uint32_t>(usage.ru_utime.tv_sec);
1557     info_ptr->process_kernel_time =
1558         static_cast<uint32_t>(usage.ru_stime.tv_sec);
1559   }
1560   int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID,
1561                  static_cast<int>(info_ptr->process_id) };
1562   uint mibsize = static_cast<uint>(sizeof(mib) / sizeof(mib[0]));
1563   struct kinfo_proc proc;
1564   size_t size = sizeof(proc);
1565   if (sysctl(mib, mibsize, &proc, &size, NULL, 0) == 0) {
1566     info_ptr->process_create_time =
1567         static_cast<uint32_t>(proc.kp_proc.p_starttime.tv_sec);
1568   }
1569 
1570   // Speed
1571   uint64_t speed;
1572   const uint64_t kOneMillion = 1000 * 1000;
1573   size = sizeof(speed);
1574   sysctlbyname("hw.cpufrequency_max", &speed, &size, NULL, 0);
1575   info_ptr->processor_max_mhz = static_cast<uint32_t>(speed / kOneMillion);
1576   info_ptr->processor_mhz_limit = static_cast<uint32_t>(speed / kOneMillion);
1577   size = sizeof(speed);
1578   sysctlbyname("hw.cpufrequency", &speed, &size, NULL, 0);
1579   info_ptr->processor_current_mhz = static_cast<uint32_t>(speed / kOneMillion);
1580 
1581   return true;
1582 }
1583 
WriteBreakpadInfoStream(MDRawDirectory * breakpad_info_stream)1584 bool MinidumpGenerator::WriteBreakpadInfoStream(
1585     MDRawDirectory* breakpad_info_stream) {
1586   TypedMDRVA<MDRawBreakpadInfo> info(&writer_);
1587 
1588   if (!info.Allocate())
1589     return false;
1590 
1591   breakpad_info_stream->stream_type = MD_BREAKPAD_INFO_STREAM;
1592   breakpad_info_stream->location = info.location();
1593   MDRawBreakpadInfo* info_ptr = info.get();
1594 
1595   if (exception_thread_ && exception_type_) {
1596     info_ptr->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID |
1597                          MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID;
1598     info_ptr->dump_thread_id = handler_thread_;
1599     info_ptr->requesting_thread_id = exception_thread_;
1600   } else {
1601     info_ptr->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID;
1602     info_ptr->dump_thread_id = handler_thread_;
1603     info_ptr->requesting_thread_id = 0;
1604   }
1605 
1606   return true;
1607 }
1608 
1609 }  // namespace google_breakpad
1610