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