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