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