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 <cstdio>
31 
32 #include <mach/host_info.h>
33 #include <mach/mach_vm.h>
34 #include <mach/vm_statistics.h>
35 #include <mach-o/dyld.h>
36 #include <mach-o/loader.h>
37 #include <sys/sysctl.h>
38 #include <sys/resource.h>
39 #include <mach/mach_vm.h>
40 
41 #include <CoreFoundation/CoreFoundation.h>
42 
43 #include "client/mac/handler/minidump_generator.h"
44 #include "client/minidump_file_writer-inl.h"
45 #include "common/mac/file_id.h"
46 #include "common/mac/string_utilities.h"
47 
48 using MacStringUtils::ConvertToString;
49 using MacStringUtils::IntegerValueAtIndex;
50 
51 namespace google_breakpad {
52 
53 // constructor when generating from within the crashed process
MinidumpGenerator()54 MinidumpGenerator::MinidumpGenerator()
55     : exception_type_(0),
56       exception_code_(0),
57       exception_subcode_(0),
58       exception_thread_(0),
59       crashing_task_(mach_task_self()),
60       handler_thread_(mach_thread_self()),
61       dynamic_images_(NULL) {
62   GatherSystemInformation();
63 }
64 
65 // constructor when generating from a different process than the
66 // crashed process
MinidumpGenerator(mach_port_t crashing_task,mach_port_t handler_thread)67 MinidumpGenerator::MinidumpGenerator(mach_port_t crashing_task,
68                                      mach_port_t handler_thread)
69     : exception_type_(0),
70       exception_code_(0),
71       exception_subcode_(0),
72       exception_thread_(0),
73       crashing_task_(crashing_task),
74       handler_thread_(handler_thread) {
75   if (crashing_task != mach_task_self()) {
76     dynamic_images_ = new DynamicImages(crashing_task_);
77   } else {
78     dynamic_images_ = NULL;
79   }
80 
81   GatherSystemInformation();
82 }
83 
~MinidumpGenerator()84 MinidumpGenerator::~MinidumpGenerator() {
85   delete dynamic_images_;
86 }
87 
88 char MinidumpGenerator::build_string_[16];
89 int MinidumpGenerator::os_major_version_ = 0;
90 int MinidumpGenerator::os_minor_version_ = 0;
91 int MinidumpGenerator::os_build_number_ = 0;
92 
93 // static
GatherSystemInformation()94 void MinidumpGenerator::GatherSystemInformation() {
95   // If this is non-zero, then we've already gathered the information
96   if (os_major_version_)
97     return;
98 
99   // This code extracts the version and build information from the OS
100   CFStringRef vers_path =
101     CFSTR("/System/Library/CoreServices/SystemVersion.plist");
102   CFURLRef sys_vers =
103     CFURLCreateWithFileSystemPath(NULL,
104                                   vers_path,
105                                   kCFURLPOSIXPathStyle,
106                                   false);
107   CFDataRef data;
108   SInt32 error;
109   CFURLCreateDataAndPropertiesFromResource(NULL, sys_vers, &data, NULL, NULL,
110                                            &error);
111 
112   if (!data)
113     return;
114 
115   CFDictionaryRef list = static_cast<CFDictionaryRef>
116     (CFPropertyListCreateFromXMLData(NULL, data, kCFPropertyListImmutable,
117                                      NULL));
118   if (!list)
119     return;
120 
121   CFStringRef build_version = static_cast<CFStringRef>
122     (CFDictionaryGetValue(list, CFSTR("ProductBuildVersion")));
123   CFStringRef product_version = static_cast<CFStringRef>
124     (CFDictionaryGetValue(list, CFSTR("ProductVersion")));
125   string build_str = ConvertToString(build_version);
126   string product_str = ConvertToString(product_version);
127 
128   CFRelease(list);
129   CFRelease(sys_vers);
130   CFRelease(data);
131 
132   strlcpy(build_string_, build_str.c_str(), sizeof(build_string_));
133 
134   // Parse the string that looks like "10.4.8"
135   os_major_version_ = IntegerValueAtIndex(product_str, 0);
136   os_minor_version_ = IntegerValueAtIndex(product_str, 1);
137   os_build_number_ = IntegerValueAtIndex(product_str, 2);
138 }
139 
UniqueNameInDirectory(const string & dir,string * unique_name)140 string MinidumpGenerator::UniqueNameInDirectory(const string &dir,
141                                                 string *unique_name) {
142   CFUUIDRef uuid = CFUUIDCreate(NULL);
143   CFStringRef uuid_cfstr = CFUUIDCreateString(NULL, uuid);
144   CFRelease(uuid);
145   string file_name(ConvertToString(uuid_cfstr));
146   CFRelease(uuid_cfstr);
147   string path(dir);
148 
149   // Ensure that the directory (if non-empty) has a trailing slash so that
150   // we can append the file name and have a valid pathname.
151   if (!dir.empty()) {
152     if (dir.at(dir.size() - 1) != '/')
153       path.append(1, '/');
154   }
155 
156   path.append(file_name);
157   path.append(".dmp");
158 
159   if (unique_name)
160     *unique_name = file_name;
161 
162   return path;
163 }
164 
Write(const char * path)165 bool MinidumpGenerator::Write(const char *path) {
166   WriteStreamFN writers[] = {
167     &MinidumpGenerator::WriteThreadListStream,
168     &MinidumpGenerator::WriteSystemInfoStream,
169     &MinidumpGenerator::WriteModuleListStream,
170     &MinidumpGenerator::WriteMiscInfoStream,
171     &MinidumpGenerator::WriteBreakpadInfoStream,
172     // Exception stream needs to be the last entry in this array as it may
173     // be omitted in the case where the minidump is written without an
174     // exception.
175     &MinidumpGenerator::WriteExceptionStream,
176   };
177   bool result = false;
178 
179   // If opening was successful, create the header, directory, and call each
180   // writer.  The destructor for the TypedMDRVAs will cause the data to be
181   // flushed.  The destructor for the MinidumpFileWriter will close the file.
182   if (writer_.Open(path)) {
183     TypedMDRVA<MDRawHeader> header(&writer_);
184     TypedMDRVA<MDRawDirectory> dir(&writer_);
185 
186     if (!header.Allocate())
187       return false;
188 
189     int writer_count = sizeof(writers) / sizeof(writers[0]);
190 
191     // If we don't have exception information, don't write out the
192     // exception stream
193     if (!exception_thread_ && !exception_type_)
194       --writer_count;
195 
196     // Add space for all writers
197     if (!dir.AllocateArray(writer_count))
198       return false;
199 
200     MDRawHeader *header_ptr = header.get();
201     header_ptr->signature = MD_HEADER_SIGNATURE;
202     header_ptr->version = MD_HEADER_VERSION;
203     time(reinterpret_cast<time_t *>(&(header_ptr->time_date_stamp)));
204     header_ptr->stream_count = writer_count;
205     header_ptr->stream_directory_rva = dir.position();
206 
207     MDRawDirectory local_dir;
208     result = true;
209     for (int i = 0; (result) && (i < writer_count); ++i) {
210       result = (this->*writers[i])(&local_dir);
211 
212       if (result)
213         dir.CopyIndex(i, &local_dir);
214     }
215   }
216   return result;
217 }
218 
CalculateStackSize(mach_vm_address_t start_addr)219 size_t MinidumpGenerator::CalculateStackSize(mach_vm_address_t start_addr) {
220   mach_vm_address_t stack_region_base = start_addr;
221   mach_vm_size_t stack_region_size;
222   natural_t nesting_level = 0;
223   vm_region_submap_info_64 submap_info;
224   mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64;
225 
226   vm_region_recurse_info_t region_info;
227   region_info = reinterpret_cast<vm_region_recurse_info_t>(&submap_info);
228 
229   if (start_addr == 0) {
230     return 0;
231   }
232 
233   kern_return_t result =
234     mach_vm_region_recurse(crashing_task_, &stack_region_base,
235                            &stack_region_size, &nesting_level,
236                            region_info,
237                            &info_count);
238 
239   if (start_addr < stack_region_base) {
240     // probably stack corruption, since mach_vm_region had to go
241     // higher in the process address space to find a valid region.
242     return 0;
243   }
244 
245 
246   if ((stack_region_base + stack_region_size) == TOP_OF_THREAD0_STACK) {
247     // The stack for thread 0 needs to extend all the way to
248     // 0xc0000000 on 32 bit and 00007fff5fc00000 on 64bit.  HOWEVER,
249     // for many processes, the stack is first created in one page
250     // below this, and is then later extended to a much larger size by
251     // creating a new VM region immediately below the initial page.
252 
253     // You can see this for yourself by running vmmap on a "hello,
254     // world" program
255 
256     // Because of the above, we'll add 4k to include the original
257     // stack frame page.
258     // This method of finding the stack region needs to be done in
259     // a better way; the breakpad issue 247 is tracking this.
260     stack_region_size += 0x1000;
261   }
262 
263   return result == KERN_SUCCESS ?
264     stack_region_base + stack_region_size - start_addr : 0;
265 }
266 
WriteStackFromStartAddress(mach_vm_address_t start_addr,MDMemoryDescriptor * stack_location)267 bool MinidumpGenerator::WriteStackFromStartAddress(
268     mach_vm_address_t start_addr,
269     MDMemoryDescriptor *stack_location) {
270   UntypedMDRVA memory(&writer_);
271 
272   bool result = false;
273   size_t size = CalculateStackSize(start_addr);
274 
275   if (size == 0) {
276       // In some situations the stack address for the thread can come back 0.
277       // In these cases we skip over the threads in question and stuff the
278       // stack with a clearly borked value.
279       start_addr = 0xDEADBEEF;
280       size = 16;
281       if (!memory.Allocate(size))
282         return false;
283 
284       unsigned long long dummy_stack[2];  // Fill dummy stack with 16 bytes of
285                                           // junk.
286       dummy_stack[0] = 0xDEADBEEF;
287       dummy_stack[1] = 0xDEADBEEF;
288 
289       result = memory.Copy(dummy_stack, size);
290   } else {
291 
292     if (!memory.Allocate(size))
293       return false;
294 
295     if (dynamic_images_) {
296 
297       kern_return_t kr;
298 
299       void *stack_memory = ReadTaskMemory(crashing_task_,
300                                           (void*)start_addr,
301                                           size,
302                                           &kr);
303 
304       if (stack_memory == NULL) {
305         return false;
306       }
307 
308       result = memory.Copy(stack_memory, size);
309       free(stack_memory);
310     } else {
311       result = memory.Copy(reinterpret_cast<const void *>(start_addr), size);
312     }
313   }
314 
315   stack_location->start_of_memory_range = start_addr;
316   stack_location->memory = memory.location();
317 
318   return result;
319 }
320 
321 #if TARGET_CPU_PPC || TARGET_CPU_PPC64
WriteStack(breakpad_thread_state_data_t state,MDMemoryDescriptor * stack_location)322 bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state,
323                                    MDMemoryDescriptor *stack_location) {
324   breakpad_thread_state_t *machine_state =
325     reinterpret_cast<breakpad_thread_state_t *>(state);
326   mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, r1);
327   return WriteStackFromStartAddress(start_addr, stack_location);
328 }
329 
330 u_int64_t
CurrentPCForStack(breakpad_thread_state_data_t state)331 MinidumpGenerator::CurrentPCForStack(breakpad_thread_state_data_t state) {
332   breakpad_thread_state_t *machine_state =
333     reinterpret_cast<breakpad_thread_state_t *>(state);
334 
335   return REGISTER_FROM_THREADSTATE(machine_state, srr0);
336 }
337 
WriteContext(breakpad_thread_state_data_t state,MDLocationDescriptor * register_location)338 bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
339                                      MDLocationDescriptor *register_location) {
340   TypedMDRVA<MinidumpContext> context(&writer_);
341   breakpad_thread_state_t *machine_state =
342     reinterpret_cast<breakpad_thread_state_t *>(state);
343 
344   if (!context.Allocate())
345     return false;
346 
347   *register_location = context.location();
348   MinidumpContext *context_ptr = context.get();
349   context_ptr->context_flags = MD_CONTEXT_PPC_BASE;
350 
351 #define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a)
352 #define AddGPR(a) context_ptr->gpr[a] = REGISTER_FROM_THREADSTATE(machine_state, r ## a)
353 
354   AddReg(srr0);
355   AddReg(cr);
356   AddReg(xer);
357   AddReg(ctr);
358   AddReg(lr);
359   AddReg(vrsave);
360 
361   AddGPR(0);
362   AddGPR(1);
363   AddGPR(2);
364   AddGPR(3);
365   AddGPR(4);
366   AddGPR(5);
367   AddGPR(6);
368   AddGPR(7);
369   AddGPR(8);
370   AddGPR(9);
371   AddGPR(10);
372   AddGPR(11);
373   AddGPR(12);
374   AddGPR(13);
375   AddGPR(14);
376   AddGPR(15);
377   AddGPR(16);
378   AddGPR(17);
379   AddGPR(18);
380   AddGPR(19);
381   AddGPR(20);
382   AddGPR(21);
383   AddGPR(22);
384   AddGPR(23);
385   AddGPR(24);
386   AddGPR(25);
387   AddGPR(26);
388   AddGPR(27);
389   AddGPR(28);
390   AddGPR(29);
391   AddGPR(30);
392   AddGPR(31);
393 
394 #if TARGET_CPU_PPC
395   /* The mq register  is only for PPC */
396   AddReg(mq);
397 #endif
398 
399 
400   return true;
401 }
402 
403 #elif TARGET_CPU_X86 || TARGET_CPU_X86_64
404 
WriteStack(breakpad_thread_state_data_t state,MDMemoryDescriptor * stack_location)405 bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state,
406                                    MDMemoryDescriptor *stack_location) {
407   breakpad_thread_state_t *machine_state =
408     reinterpret_cast<breakpad_thread_state_t *>(state);
409 
410 #if TARGET_CPU_X86_64
411   mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, rsp);
412 #else
413   mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, esp);
414 #endif
415   return WriteStackFromStartAddress(start_addr, stack_location);
416 }
417 
418 u_int64_t
CurrentPCForStack(breakpad_thread_state_data_t state)419 MinidumpGenerator::CurrentPCForStack(breakpad_thread_state_data_t state) {
420   breakpad_thread_state_t *machine_state =
421     reinterpret_cast<breakpad_thread_state_t *>(state);
422 
423 #if TARGET_CPU_X86_64
424   return REGISTER_FROM_THREADSTATE(machine_state, rip);
425 #else
426   return REGISTER_FROM_THREADSTATE(machine_state, eip);
427 #endif
428 }
429 
WriteContext(breakpad_thread_state_data_t state,MDLocationDescriptor * register_location)430 bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
431                                      MDLocationDescriptor *register_location) {
432   TypedMDRVA<MinidumpContext> context(&writer_);
433   breakpad_thread_state_t *machine_state =
434     reinterpret_cast<breakpad_thread_state_t *>(state);
435 
436   if (!context.Allocate())
437     return false;
438 
439   *register_location = context.location();
440   MinidumpContext *context_ptr = context.get();
441 
442 #define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a)
443 #if TARGET_CPU_X86
444   context_ptr->context_flags = MD_CONTEXT_X86;
445   AddReg(eax);
446   AddReg(ebx);
447   AddReg(ecx);
448   AddReg(edx);
449   AddReg(esi);
450   AddReg(edi);
451   AddReg(ebp);
452   AddReg(esp);
453 
454   AddReg(cs);
455   AddReg(ds);
456   AddReg(ss);
457   AddReg(es);
458   AddReg(fs);
459   AddReg(gs);
460   AddReg(eflags);
461 
462   AddReg(eip);
463 #else
464   context_ptr->context_flags = MD_CONTEXT_AMD64;
465   AddReg(rax);
466   AddReg(rbx);
467   AddReg(rcx);
468   AddReg(rdx);
469   AddReg(rdi);
470   AddReg(rsi);
471   AddReg(rbp);
472   AddReg(rsp);
473   AddReg(r8);
474   AddReg(r9);
475   AddReg(r10);
476   AddReg(r11);
477   AddReg(r12);
478   AddReg(r13);
479   AddReg(r14);
480   AddReg(r15);
481   AddReg(rip);
482   // according to AMD's software developer guide, bits above 18 are
483   // not used in the flags register.  Since the minidump format
484   // specifies 32 bits for the flags register, we can truncate safely
485   // with no loss.
486   context_ptr->eflags = machine_state->__rflags;
487   AddReg(cs);
488   AddReg(fs);
489   AddReg(gs);
490 #endif
491 #undef AddReg(a)
492 
493   return true;
494 }
495 #endif
496 
WriteThreadStream(mach_port_t thread_id,MDRawThread * thread)497 bool MinidumpGenerator::WriteThreadStream(mach_port_t thread_id,
498                                           MDRawThread *thread) {
499   breakpad_thread_state_data_t state;
500   mach_msg_type_number_t state_count = sizeof(state);
501 
502   if (thread_get_state(thread_id, BREAKPAD_MACHINE_THREAD_STATE,
503                        state, &state_count) ==
504       KERN_SUCCESS) {
505     if (!WriteStack(state, &thread->stack))
506       return false;
507 
508     if (!WriteContext(state, &thread->thread_context))
509       return false;
510 
511     thread->thread_id = thread_id;
512   } else {
513     return false;
514   }
515 
516   return true;
517 }
518 
WriteThreadListStream(MDRawDirectory * thread_list_stream)519 bool MinidumpGenerator::WriteThreadListStream(
520     MDRawDirectory *thread_list_stream) {
521   TypedMDRVA<MDRawThreadList> list(&writer_);
522   thread_act_port_array_t threads_for_task;
523   mach_msg_type_number_t thread_count;
524   int non_generator_thread_count;
525 
526   if (task_threads(crashing_task_, &threads_for_task, &thread_count))
527     return false;
528 
529   // Don't include the generator thread
530   non_generator_thread_count = thread_count - 1;
531   if (!list.AllocateObjectAndArray(non_generator_thread_count,
532                                    sizeof(MDRawThread)))
533     return false;
534 
535   thread_list_stream->stream_type = MD_THREAD_LIST_STREAM;
536   thread_list_stream->location = list.location();
537 
538   list.get()->number_of_threads = non_generator_thread_count;
539 
540   MDRawThread thread;
541   int thread_idx = 0;
542 
543   for (unsigned int i = 0; i < thread_count; ++i) {
544     memset(&thread, 0, sizeof(MDRawThread));
545 
546     if (threads_for_task[i] != handler_thread_) {
547       if (!WriteThreadStream(threads_for_task[i], &thread))
548         return false;
549 
550       list.CopyIndexAfterObject(thread_idx++, &thread, sizeof(MDRawThread));
551     }
552   }
553 
554   return true;
555 }
556 
557 bool
WriteExceptionStream(MDRawDirectory * exception_stream)558 MinidumpGenerator::WriteExceptionStream(MDRawDirectory *exception_stream) {
559   TypedMDRVA<MDRawExceptionStream> exception(&writer_);
560 
561   if (!exception.Allocate())
562     return false;
563 
564   exception_stream->stream_type = MD_EXCEPTION_STREAM;
565   exception_stream->location = exception.location();
566   MDRawExceptionStream *exception_ptr = exception.get();
567   exception_ptr->thread_id = exception_thread_;
568 
569   // This naming is confusing, but it is the proper translation from
570   // mach naming to minidump naming.
571   exception_ptr->exception_record.exception_code = exception_type_;
572   exception_ptr->exception_record.exception_flags = exception_code_;
573 
574   breakpad_thread_state_data_t state;
575   mach_msg_type_number_t stateCount = sizeof(state);
576 
577   if (thread_get_state(exception_thread_,
578                        BREAKPAD_MACHINE_THREAD_STATE,
579                        state,
580                        &stateCount) != KERN_SUCCESS)
581     return false;
582 
583   if (!WriteContext(state, &exception_ptr->thread_context))
584     return false;
585 
586   if (exception_type_ == EXC_BAD_ACCESS)
587     exception_ptr->exception_record.exception_address = exception_subcode_;
588   else
589     exception_ptr->exception_record.exception_address = CurrentPCForStack(state);
590 
591   return true;
592 }
593 
WriteSystemInfoStream(MDRawDirectory * system_info_stream)594 bool MinidumpGenerator::WriteSystemInfoStream(
595     MDRawDirectory *system_info_stream) {
596   TypedMDRVA<MDRawSystemInfo> info(&writer_);
597 
598   if (!info.Allocate())
599     return false;
600 
601   system_info_stream->stream_type = MD_SYSTEM_INFO_STREAM;
602   system_info_stream->location = info.location();
603 
604   // CPU Information
605   uint32_t cpu_type;
606   size_t len = sizeof(cpu_type);
607   sysctlbyname("hw.cputype", &cpu_type, &len, NULL, 0);
608   uint32_t number_of_processors;
609   len = sizeof(number_of_processors);
610   sysctlbyname("hw.ncpu", &number_of_processors, &len, NULL, 0);
611   MDRawSystemInfo *info_ptr = info.get();
612 
613   switch (cpu_type) {
614     case CPU_TYPE_POWERPC:
615       info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_PPC;
616       break;
617     case CPU_TYPE_I386:
618       info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_X86;
619 #ifdef __i386__
620       // ebx is used for PIC code, so we need
621       // to preserve it.
622 #define cpuid(op,eax,ebx,ecx,edx)      \
623   asm ("pushl %%ebx   \n\t"            \
624        "cpuid         \n\t"            \
625        "movl %%ebx,%1 \n\t"            \
626        "popl %%ebx"                    \
627        : "=a" (eax),                   \
628          "=g" (ebx),                   \
629          "=c" (ecx),                   \
630          "=d" (edx)                    \
631        : "0" (op))
632       int unused, unused2;
633       // get vendor id
634       cpuid(0, unused, info_ptr->cpu.x86_cpu_info.vendor_id[0],
635             info_ptr->cpu.x86_cpu_info.vendor_id[2],
636             info_ptr->cpu.x86_cpu_info.vendor_id[1]);
637       // get version and feature info
638       cpuid(1, info_ptr->cpu.x86_cpu_info.version_information, unused, unused2,
639             info_ptr->cpu.x86_cpu_info.feature_information);
640       // family
641       info_ptr->processor_level =
642         (info_ptr->cpu.x86_cpu_info.version_information & 0xF00) >> 8;
643       // 0xMMSS (Model, Stepping)
644       info_ptr->processor_revision =
645         (info_ptr->cpu.x86_cpu_info.version_information & 0xF) |
646         ((info_ptr->cpu.x86_cpu_info.version_information & 0xF0) << 4);
647 #endif // __i386__
648       break;
649     default:
650       info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_UNKNOWN;
651       break;
652   }
653 
654   info_ptr->number_of_processors = number_of_processors;
655   info_ptr->platform_id = MD_OS_MAC_OS_X;
656 
657   MDLocationDescriptor build_string_loc;
658 
659   if (!writer_.WriteString(build_string_, 0,
660                            &build_string_loc))
661     return false;
662 
663   info_ptr->csd_version_rva = build_string_loc.rva;
664   info_ptr->major_version = os_major_version_;
665   info_ptr->minor_version = os_minor_version_;
666   info_ptr->build_number = os_build_number_;
667 
668   return true;
669 }
670 
WriteModuleStream(unsigned int index,MDRawModule * module)671 bool MinidumpGenerator::WriteModuleStream(unsigned int index,
672                                           MDRawModule *module) {
673   if (dynamic_images_) {
674     // we're in a different process than the crashed process
675     DynamicImage *image = dynamic_images_->GetImage(index);
676 
677     if (!image)
678       return false;
679 
680     const breakpad_mach_header *header = image->GetMachHeader();
681 
682     if (!header)
683       return false;
684 
685     int cpu_type = header->cputype;
686 
687     memset(module, 0, sizeof(MDRawModule));
688 
689     MDLocationDescriptor string_location;
690 
691     const char* name = image->GetFilePath();
692     if (!writer_.WriteString(name, 0, &string_location))
693       return false;
694 
695     module->base_of_image = image->GetVMAddr() + image->GetVMAddrSlide();
696     module->size_of_image = image->GetVMSize();
697     module->module_name_rva = string_location.rva;
698 
699     // We'll skip the executable module, because they don't have
700     // LC_ID_DYLIB load commands, and the crash processing server gets
701     // version information from the Plist file, anyway.
702     if (index != (uint32_t)FindExecutableModule()) {
703       module->version_info.signature = MD_VSFIXEDFILEINFO_SIGNATURE;
704       module->version_info.struct_version |= MD_VSFIXEDFILEINFO_VERSION;
705       // Convert MAC dylib version format, which is a 32 bit number, to the
706       // format used by minidump.  The mac format is <16 bits>.<8 bits>.<8 bits>
707       // so it fits nicely into the windows version with some massaging
708       // The mapping is:
709       //    1) upper 16 bits of MAC version go to lower 16 bits of product HI
710       //    2) Next most significant 8 bits go to upper 16 bits of product LO
711       //    3) Least significant 8 bits go to lower 16 bits of product LO
712       uint32_t modVersion = image->GetVersion();
713       module->version_info.file_version_hi = 0;
714       module->version_info.file_version_hi = modVersion >> 16;
715       module->version_info.file_version_lo |= (modVersion & 0xff00)  << 8;
716       module->version_info.file_version_lo |= (modVersion & 0xff);
717     }
718 
719     if (!WriteCVRecord(module, cpu_type, name)) {
720       return false;
721     }
722   } else {
723     // we're getting module info in the crashed process
724 
725     const breakpad_mach_header *header;
726     header = (breakpad_mach_header*)_dyld_get_image_header(index);
727     if (!header)
728       return false;
729 
730 #ifdef __LP64__
731     assert(header->magic == MH_MAGIC_64);
732 
733     if(header->magic != MH_MAGIC_64)
734       return false;
735 #else
736     assert(header->magic == MH_MAGIC);
737 
738     if(header->magic != MH_MAGIC)
739       return false;
740 #endif
741 
742     int cpu_type = header->cputype;
743     unsigned long slide = _dyld_get_image_vmaddr_slide(index);
744     const char* name = _dyld_get_image_name(index);
745     const struct load_command *cmd =
746       reinterpret_cast<const struct load_command *>(header + 1);
747 
748     memset(module, 0, sizeof(MDRawModule));
749 
750     for (unsigned int i = 0; cmd && (i < header->ncmds); i++) {
751       if (cmd->cmd == LC_SEGMENT) {
752 
753         const breakpad_mach_segment_command *seg =
754           reinterpret_cast<const breakpad_mach_segment_command *>(cmd);
755 
756         if (!strcmp(seg->segname, "__TEXT")) {
757           MDLocationDescriptor string_location;
758 
759           if (!writer_.WriteString(name, 0, &string_location))
760             return false;
761 
762           module->base_of_image = seg->vmaddr + slide;
763           module->size_of_image = seg->vmsize;
764           module->module_name_rva = string_location.rva;
765 
766           if (!WriteCVRecord(module, cpu_type, name))
767             return false;
768 
769           return true;
770         }
771       }
772 
773       cmd = reinterpret_cast<struct load_command*>((char *)cmd + cmd->cmdsize);
774     }
775   }
776 
777   return true;
778 }
779 
FindExecutableModule()780 int MinidumpGenerator::FindExecutableModule() {
781   if (dynamic_images_) {
782     int index = dynamic_images_->GetExecutableImageIndex();
783 
784     if (index >= 0) {
785       return index;
786     }
787   } else {
788     int image_count = _dyld_image_count();
789     const struct mach_header *header;
790 
791     for (int index = 0; index < image_count; ++index) {
792       header = _dyld_get_image_header(index);
793 
794       if (header->filetype == MH_EXECUTE)
795         return index;
796     }
797   }
798 
799   // failed - just use the first image
800   return 0;
801 }
802 
WriteCVRecord(MDRawModule * module,int cpu_type,const char * module_path)803 bool MinidumpGenerator::WriteCVRecord(MDRawModule *module, int cpu_type,
804                                       const char *module_path) {
805   TypedMDRVA<MDCVInfoPDB70> cv(&writer_);
806 
807   // Only return the last path component of the full module path
808   const char *module_name = strrchr(module_path, '/');
809 
810   // Increment past the slash
811   if (module_name)
812     ++module_name;
813   else
814     module_name = "<Unknown>";
815 
816   size_t module_name_length = strlen(module_name);
817 
818   if (!cv.AllocateObjectAndArray(module_name_length + 1, sizeof(u_int8_t)))
819     return false;
820 
821   if (!cv.CopyIndexAfterObject(0, module_name, module_name_length))
822     return false;
823 
824   module->cv_record = cv.location();
825   MDCVInfoPDB70 *cv_ptr = cv.get();
826   cv_ptr->cv_signature = MD_CVINFOPDB70_SIGNATURE;
827   cv_ptr->age = 0;
828 
829   // Get the module identifier
830   FileID file_id(module_path);
831   unsigned char identifier[16];
832 
833   if (file_id.MachoIdentifier(cpu_type, identifier)) {
834     cv_ptr->signature.data1 = (uint32_t)identifier[0] << 24 |
835       (uint32_t)identifier[1] << 16 | (uint32_t)identifier[2] << 8 |
836       (uint32_t)identifier[3];
837     cv_ptr->signature.data2 = (uint32_t)identifier[4] << 8 | identifier[5];
838     cv_ptr->signature.data3 = (uint32_t)identifier[6] << 8 | identifier[7];
839     cv_ptr->signature.data4[0] = identifier[8];
840     cv_ptr->signature.data4[1] = identifier[9];
841     cv_ptr->signature.data4[2] = identifier[10];
842     cv_ptr->signature.data4[3] = identifier[11];
843     cv_ptr->signature.data4[4] = identifier[12];
844     cv_ptr->signature.data4[5] = identifier[13];
845     cv_ptr->signature.data4[6] = identifier[14];
846     cv_ptr->signature.data4[7] = identifier[15];
847   }
848 
849   return true;
850 }
851 
WriteModuleListStream(MDRawDirectory * module_list_stream)852 bool MinidumpGenerator::WriteModuleListStream(
853     MDRawDirectory *module_list_stream) {
854   TypedMDRVA<MDRawModuleList> list(&writer_);
855 
856   int image_count = dynamic_images_ ?
857     dynamic_images_->GetImageCount() : _dyld_image_count();
858 
859   if (!list.AllocateObjectAndArray(image_count, MD_MODULE_SIZE))
860     return false;
861 
862   module_list_stream->stream_type = MD_MODULE_LIST_STREAM;
863   module_list_stream->location = list.location();
864   list.get()->number_of_modules = image_count;
865 
866   // Write out the executable module as the first one
867   MDRawModule module;
868   int executableIndex = FindExecutableModule();
869 
870   if (!WriteModuleStream(executableIndex, &module)) {
871     return false;
872   }
873 
874   list.CopyIndexAfterObject(0, &module, MD_MODULE_SIZE);
875   int destinationIndex = 1;  // Write all other modules after this one
876 
877   for (int i = 0; i < image_count; ++i) {
878     if (i != executableIndex) {
879       if (!WriteModuleStream(i, &module)) {
880         return false;
881       }
882 
883       list.CopyIndexAfterObject(destinationIndex++, &module, MD_MODULE_SIZE);
884     }
885   }
886 
887   return true;
888 }
889 
WriteMiscInfoStream(MDRawDirectory * misc_info_stream)890 bool MinidumpGenerator::WriteMiscInfoStream(MDRawDirectory *misc_info_stream) {
891   TypedMDRVA<MDRawMiscInfo> info(&writer_);
892 
893   if (!info.Allocate())
894     return false;
895 
896   misc_info_stream->stream_type = MD_MISC_INFO_STREAM;
897   misc_info_stream->location = info.location();
898 
899   MDRawMiscInfo *info_ptr = info.get();
900   info_ptr->size_of_info = sizeof(MDRawMiscInfo);
901   info_ptr->flags1 = MD_MISCINFO_FLAGS1_PROCESS_ID |
902     MD_MISCINFO_FLAGS1_PROCESS_TIMES |
903     MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO;
904 
905   // Process ID
906   info_ptr->process_id = getpid();
907 
908   // Times
909   struct rusage usage;
910   if (getrusage(RUSAGE_SELF, &usage) != -1) {
911     // Omit the fractional time since the MDRawMiscInfo only wants seconds
912     info_ptr->process_user_time = usage.ru_utime.tv_sec;
913     info_ptr->process_kernel_time = usage.ru_stime.tv_sec;
914   }
915   int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, info_ptr->process_id };
916   size_t size;
917   if (!sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &size, NULL, 0)) {
918     mach_vm_address_t addr;
919     if (mach_vm_allocate(mach_task_self(),
920                          &addr,
921                          size,
922                          true) == KERN_SUCCESS) {
923       struct kinfo_proc *proc = (struct kinfo_proc *)addr;
924       if (!sysctl(mib, sizeof(mib) / sizeof(mib[0]), proc, &size, NULL, 0))
925         info_ptr->process_create_time = proc->kp_proc.p_starttime.tv_sec;
926       mach_vm_deallocate(mach_task_self(), addr, size);
927     }
928   }
929 
930   // Speed
931   uint64_t speed;
932   size = sizeof(speed);
933   sysctlbyname("hw.cpufrequency_max", &speed, &size, NULL, 0);
934   info_ptr->processor_max_mhz = speed / (1000 * 1000);
935   info_ptr->processor_mhz_limit = speed / (1000 * 1000);
936   size = sizeof(speed);
937   sysctlbyname("hw.cpufrequency", &speed, &size, NULL, 0);
938   info_ptr->processor_current_mhz = speed / (1000 * 1000);
939 
940   return true;
941 }
942 
WriteBreakpadInfoStream(MDRawDirectory * breakpad_info_stream)943 bool MinidumpGenerator::WriteBreakpadInfoStream(
944     MDRawDirectory *breakpad_info_stream) {
945   TypedMDRVA<MDRawBreakpadInfo> info(&writer_);
946 
947   if (!info.Allocate())
948     return false;
949 
950   breakpad_info_stream->stream_type = MD_BREAKPAD_INFO_STREAM;
951   breakpad_info_stream->location = info.location();
952   MDRawBreakpadInfo *info_ptr = info.get();
953 
954   if (exception_thread_ && exception_type_) {
955     info_ptr->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID |
956                          MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID;
957     info_ptr->dump_thread_id = handler_thread_;
958     info_ptr->requesting_thread_id = exception_thread_;
959   } else {
960     info_ptr->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID;
961     info_ptr->dump_thread_id = handler_thread_;
962     info_ptr->requesting_thread_id = 0;
963   }
964 
965   return true;
966 }
967 
968 }  // namespace google_breakpad
969