Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(332)

Side by Side Diff: util/mac/process_reader.cc

Issue 666483002: Create snapshot/mac and move some files from snapshot and util to there (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad/+/master
Patch Set: Move process_reader, process_types, and mach_o_image*_reader from util/mac to snapshot/mac Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « util/mac/process_reader.h ('k') | util/mac/process_reader_test.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2014 The Crashpad Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "util/mac/process_reader.h"
16
17 #include <AvailabilityMacros.h>
18 #include <mach/mach_vm.h>
19 #include <mach-o/loader.h>
20
21 #include <algorithm>
22
23 #include "base/logging.h"
24 #include "base/mac/mach_logging.h"
25 #include "base/mac/scoped_mach_port.h"
26 #include "base/mac/scoped_mach_vm.h"
27 #include "base/strings/stringprintf.h"
28 #include "util/mac/mach_o_image_reader.h"
29 #include "util/mac/process_types.h"
30 #include "util/misc/scoped_forbid_return.h"
31
32 namespace {
33
34 void MachTimeValueToTimeval(const time_value& mach, timeval* tv) {
35 tv->tv_sec = mach.seconds;
36 tv->tv_usec = mach.microseconds;
37 }
38
39 kern_return_t MachVMRegionRecurseDeepest(task_t task,
40 mach_vm_address_t* address,
41 mach_vm_size_t* size,
42 natural_t* depth,
43 vm_prot_t* protection,
44 unsigned int* user_tag) {
45 vm_region_submap_short_info_64 submap_info;
46 mach_msg_type_number_t count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;
47 while (true) {
48 kern_return_t kr = mach_vm_region_recurse(
49 task,
50 address,
51 size,
52 depth,
53 reinterpret_cast<vm_region_recurse_info_t>(&submap_info),
54 &count);
55 if (kr != KERN_SUCCESS) {
56 return kr;
57 }
58
59 if (!submap_info.is_submap) {
60 *protection = submap_info.protection;
61 *user_tag = submap_info.user_tag;
62 return KERN_SUCCESS;
63 }
64
65 ++*depth;
66 }
67 }
68
69 } // namespace
70
71 namespace crashpad {
72
73 ProcessReader::Thread::Thread()
74 : thread_context(),
75 float_context(),
76 debug_context(),
77 id(0),
78 stack_region_address(0),
79 stack_region_size(0),
80 thread_specific_data_address(0),
81 port(THREAD_NULL),
82 suspend_count(0),
83 priority(0) {
84 }
85
86 ProcessReader::Module::Module() : name(), reader(nullptr), timestamp(0) {
87 }
88
89 ProcessReader::Module::~Module() {
90 }
91
92 ProcessReader::ProcessReader()
93 : kern_proc_info_(),
94 threads_(),
95 modules_(),
96 module_readers_(),
97 task_memory_(),
98 task_(TASK_NULL),
99 initialized_(),
100 is_64_bit_(false),
101 initialized_threads_(false),
102 initialized_modules_(false) {
103 }
104
105 ProcessReader::~ProcessReader() {
106 for (const Thread& thread : threads_) {
107 kern_return_t kr = mach_port_deallocate(mach_task_self(), thread.port);
108 MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "mach_port_deallocate";
109 }
110 }
111
112 bool ProcessReader::Initialize(task_t task) {
113 INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
114
115 pid_t pid;
116 kern_return_t kr = pid_for_task(task, &pid);
117 if (kr != KERN_SUCCESS) {
118 MACH_LOG(ERROR, kr) << "pid_for_task";
119 return false;
120 }
121
122 int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
123 size_t len = sizeof(kern_proc_info_);
124 if (sysctl(mib, arraysize(mib), &kern_proc_info_, &len, nullptr, 0) != 0) {
125 PLOG(ERROR) << "sysctl for pid " << pid;
126 return false;
127 }
128
129 DCHECK_EQ(kern_proc_info_.kp_proc.p_pid, pid);
130
131 is_64_bit_ = kern_proc_info_.kp_proc.p_flag & P_LP64;
132
133 task_memory_.reset(new TaskMemory(task));
134 task_ = task;
135
136 INITIALIZATION_STATE_SET_VALID(initialized_);
137 return true;
138 }
139
140 void ProcessReader::StartTime(timeval* start_time) const {
141 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
142 *start_time = kern_proc_info_.kp_proc.p_starttime;
143 }
144
145 bool ProcessReader::CPUTimes(timeval* user_time, timeval* system_time) const {
146 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
147
148 // Calculate user and system time the same way the kernel does for
149 // getrusage(). See 10.9.2 xnu-2422.90.20/bsd/kern/kern_resource.c calcru().
150 timerclear(user_time);
151 timerclear(system_time);
152
153 // As of the 10.8 SDK, the preferred routine is MACH_TASK_BASIC_INFO.
154 // TASK_BASIC_INFO_64 is equivalent and works on earlier systems.
155 task_basic_info_64 task_basic_info;
156 mach_msg_type_number_t task_basic_info_count = TASK_BASIC_INFO_64_COUNT;
157 kern_return_t kr = task_info(task_,
158 TASK_BASIC_INFO_64,
159 reinterpret_cast<task_info_t>(&task_basic_info),
160 &task_basic_info_count);
161 if (kr != KERN_SUCCESS) {
162 MACH_LOG(WARNING, kr) << "task_info TASK_BASIC_INFO_64";
163 return false;
164 }
165
166 task_thread_times_info_data_t task_thread_times;
167 mach_msg_type_number_t task_thread_times_count = TASK_THREAD_TIMES_INFO_COUNT;
168 kr = task_info(task_,
169 TASK_THREAD_TIMES_INFO,
170 reinterpret_cast<task_info_t>(&task_thread_times),
171 &task_thread_times_count);
172 if (kr != KERN_SUCCESS) {
173 MACH_LOG(WARNING, kr) << "task_info TASK_THREAD_TIMES";
174 return false;
175 }
176
177 MachTimeValueToTimeval(task_basic_info.user_time, user_time);
178 MachTimeValueToTimeval(task_basic_info.system_time, system_time);
179
180 timeval thread_user_time;
181 MachTimeValueToTimeval(task_thread_times.user_time, &thread_user_time);
182 timeval thread_system_time;
183 MachTimeValueToTimeval(task_thread_times.system_time, &thread_system_time);
184
185 timeradd(user_time, &thread_user_time, user_time);
186 timeradd(system_time, &thread_system_time, system_time);
187
188 return true;
189 }
190
191 const std::vector<ProcessReader::Thread>& ProcessReader::Threads() {
192 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
193
194 if (!initialized_threads_) {
195 InitializeThreads();
196 }
197
198 return threads_;
199 }
200
201 const std::vector<ProcessReader::Module>& ProcessReader::Modules() {
202 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
203
204 if (!initialized_modules_) {
205 InitializeModules();
206 }
207
208 return modules_;
209 }
210
211 void ProcessReader::InitializeThreads() {
212 DCHECK(!initialized_threads_);
213 DCHECK(threads_.empty());
214
215 initialized_threads_ = true;
216
217 thread_act_array_t threads;
218 mach_msg_type_number_t thread_count = 0;
219 kern_return_t kr = task_threads(task_, &threads, &thread_count);
220 if (kr != KERN_SUCCESS) {
221 MACH_LOG(WARNING, kr) << "task_threads";
222 return;
223 }
224
225 // The send rights in the |threads| array won’t have their send rights managed
226 // by anything until they’re added to |threads_| by the loop below. Any early
227 // return (or exception) that happens between here and the completion of the
228 // loop below will leak thread port send rights.
229 ScopedForbidReturn threads_need_owners;
230
231 base::mac::ScopedMachVM threads_vm(
232 reinterpret_cast<vm_address_t>(threads),
233 mach_vm_round_page(thread_count * sizeof(*threads)));
234
235 for (size_t index = 0; index < thread_count; ++index) {
236 Thread thread;
237 thread.port = threads[index];
238
239 #if defined(ARCH_CPU_X86_FAMILY)
240 const thread_state_flavor_t kThreadStateFlavor =
241 Is64Bit() ? x86_THREAD_STATE64 : x86_THREAD_STATE32;
242 mach_msg_type_number_t thread_state_count =
243 Is64Bit() ? x86_THREAD_STATE64_COUNT : x86_THREAD_STATE32_COUNT;
244
245 // TODO(mark): Use the AVX variants instead of the FLOAT variants?
246 const thread_state_flavor_t kFloatStateFlavor =
247 Is64Bit() ? x86_FLOAT_STATE64 : x86_FLOAT_STATE32;
248 mach_msg_type_number_t float_state_count =
249 Is64Bit() ? x86_FLOAT_STATE64_COUNT : x86_FLOAT_STATE32_COUNT;
250
251 const thread_state_flavor_t kDebugStateFlavor =
252 Is64Bit() ? x86_DEBUG_STATE64 : x86_DEBUG_STATE32;
253 mach_msg_type_number_t debug_state_count =
254 Is64Bit() ? x86_DEBUG_STATE64_COUNT : x86_DEBUG_STATE32_COUNT;
255 #endif
256
257 kr = thread_get_state(
258 thread.port,
259 kThreadStateFlavor,
260 reinterpret_cast<thread_state_t>(&thread.thread_context),
261 &thread_state_count);
262 if (kr != KERN_SUCCESS) {
263 MACH_LOG(ERROR, kr) << "thread_get_state(" << kThreadStateFlavor << ")";
264 continue;
265 }
266
267 kr = thread_get_state(
268 thread.port,
269 kFloatStateFlavor,
270 reinterpret_cast<thread_state_t>(&thread.float_context),
271 &float_state_count);
272 if (kr != KERN_SUCCESS) {
273 MACH_LOG(ERROR, kr) << "thread_get_state(" << kFloatStateFlavor << ")";
274 continue;
275 }
276
277 kr = thread_get_state(
278 thread.port,
279 kDebugStateFlavor,
280 reinterpret_cast<thread_state_t>(&thread.debug_context),
281 &debug_state_count);
282 if (kr != KERN_SUCCESS) {
283 MACH_LOG(ERROR, kr) << "thread_get_state(" << kDebugStateFlavor << ")";
284 continue;
285 }
286
287 thread_basic_info basic_info;
288 mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
289 kr = thread_info(thread.port,
290 THREAD_BASIC_INFO,
291 reinterpret_cast<thread_info_t>(&basic_info),
292 &count);
293 if (kr != KERN_SUCCESS) {
294 MACH_LOG(WARNING, kr) << "thread_info(THREAD_BASIC_INFO)";
295 } else {
296 thread.suspend_count = basic_info.suspend_count;
297 }
298
299 thread_identifier_info identifier_info;
300 count = THREAD_IDENTIFIER_INFO_COUNT;
301 kr = thread_info(thread.port,
302 THREAD_IDENTIFIER_INFO,
303 reinterpret_cast<thread_info_t>(&identifier_info),
304 &count);
305 if (kr != KERN_SUCCESS) {
306 MACH_LOG(WARNING, kr) << "thread_info(THREAD_IDENTIFIER_INFO)";
307 } else {
308 thread.id = identifier_info.thread_id;
309
310 // thread_identifier_info::thread_handle contains the base of the
311 // thread-specific data area, which on x86 and x86_64 is the thread’s base
312 // address of the %gs segment. 10.9.2 xnu-2422.90.20/osfmk/kern/thread.c
313 // thread_info_internal() gets the value from
314 // machine_thread::cthread_self, which is the same value used to set the
315 // %gs base in xnu-2422.90.20/osfmk/i386/pcb_native.c
316 // act_machine_switch_pcb().
317 //
318 // This address is the internal pthread’s _pthread::tsd[], an array of
319 // void* values that can be indexed by pthread_key_t values.
320 thread.thread_specific_data_address = identifier_info.thread_handle;
321 }
322
323 thread_precedence_policy precedence;
324 count = THREAD_PRECEDENCE_POLICY_COUNT;
325 boolean_t get_default = FALSE;
326 kr = thread_policy_get(thread.port,
327 THREAD_PRECEDENCE_POLICY,
328 reinterpret_cast<thread_policy_t>(&precedence),
329 &count,
330 &get_default);
331 if (kr != KERN_SUCCESS) {
332 MACH_LOG(INFO, kr) << "thread_policy_get";
333 } else {
334 thread.priority = precedence.importance;
335 }
336
337 #if defined(ARCH_CPU_X86_FAMILY)
338 mach_vm_address_t stack_pointer = Is64Bit()
339 ? thread.thread_context.t64.__rsp
340 : thread.thread_context.t32.__esp;
341 #endif
342
343 thread.stack_region_address =
344 CalculateStackRegion(stack_pointer, &thread.stack_region_size);
345
346 threads_.push_back(thread);
347 }
348
349 threads_need_owners.Disarm();
350 }
351
352 void ProcessReader::InitializeModules() {
353 DCHECK(!initialized_modules_);
354 DCHECK(modules_.empty());
355
356 initialized_modules_ = true;
357
358 task_dyld_info_data_t dyld_info;
359 mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
360 kern_return_t kr = task_info(
361 task_, TASK_DYLD_INFO, reinterpret_cast<task_info_t>(&dyld_info), &count);
362 if (kr != KERN_SUCCESS) {
363 MACH_LOG(WARNING, kr) << "task_info";
364 return;
365 }
366
367 // TODO(mark): Deal with statically linked executables which don’t use dyld.
368 // This may look for the module that matches the executable path in the same
369 // data set that vmmap uses.
370
371 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
372 // The task_dyld_info_data_t struct grew in 10.7, adding the format field.
373 // Don’t check this field if it’s not present, which can happen when either
374 // the SDK used at compile time or the kernel at run time are too old and
375 // don’t know about it.
376 if (count >= TASK_DYLD_INFO_COUNT) {
377 const integer_t kExpectedFormat =
378 !Is64Bit() ? TASK_DYLD_ALL_IMAGE_INFO_32 : TASK_DYLD_ALL_IMAGE_INFO_64;
379 if (dyld_info.all_image_info_format != kExpectedFormat) {
380 LOG(WARNING) << "unexpected task_dyld_info_data_t::all_image_info_format "
381 << dyld_info.all_image_info_format;
382 DCHECK_EQ(dyld_info.all_image_info_format, kExpectedFormat);
383 return;
384 }
385 }
386 #endif
387
388 process_types::dyld_all_image_infos all_image_infos;
389 if (!all_image_infos.Read(this, dyld_info.all_image_info_addr)) {
390 LOG(WARNING) << "could not read dyld_all_image_infos";
391 return;
392 }
393
394 DCHECK_GE(all_image_infos.version, 1u);
395
396 // Note that all_image_infos.infoArrayCount may be 0 if a crash occurred while
397 // dyld was loading the executable. This can happen if a required dynamic
398 // library was not found. Similarly, all_image_infos.infoArray may be nullptr
399 // if a crash occurred while dyld was updating it.
400 //
401 // TODO(mark): It may be possible to recover from these situations by looking
402 // through memory mappings for Mach-O images.
403 if (all_image_infos.infoArrayCount == 0) {
404 LOG(WARNING) << "all_image_infos.infoArrayCount is zero";
405 return;
406 }
407 if (!all_image_infos.infoArray) {
408 LOG(WARNING) << "all_image_infos.infoArray is nullptr";
409 return;
410 }
411
412 std::vector<process_types::dyld_image_info> image_info_vector(
413 all_image_infos.infoArrayCount);
414 if (!process_types::dyld_image_info::ReadArrayInto(this,
415 all_image_infos.infoArray,
416 image_info_vector.size(),
417 &image_info_vector[0])) {
418 LOG(WARNING) << "could not read dyld_image_info array";
419 return;
420 }
421
422 size_t main_executable_count = 0;
423 bool found_dyld = false;
424 modules_.reserve(image_info_vector.size());
425 for (const process_types::dyld_image_info& image_info : image_info_vector) {
426 Module module;
427 module.timestamp = image_info.imageFileModDate;
428
429 if (!task_memory_->ReadCString(image_info.imageFilePath, &module.name)) {
430 LOG(WARNING) << "could not read dyld_image_info::imageFilePath";
431 // Proceed anyway with an empty module name.
432 }
433
434 scoped_ptr<MachOImageReader> reader(new MachOImageReader());
435 if (!reader->Initialize(this, image_info.imageLoadAddress, module.name)) {
436 reader.reset();
437 }
438
439 module.reader = reader.get();
440
441 uint32_t file_type = reader ? reader->FileType() : 0;
442
443 module_readers_.push_back(reader.release());
444 modules_.push_back(module);
445
446 if (all_image_infos.version >= 2 && all_image_infos.dyldImageLoadAddress &&
447 image_info.imageLoadAddress == all_image_infos.dyldImageLoadAddress) {
448 found_dyld = true;
449
450 LOG_IF(WARNING, file_type != MH_DYLINKER)
451 << base::StringPrintf("dylinker (%s) has unexpected Mach-O type %d",
452 module.name.c_str(),
453 file_type);
454 }
455
456 if (file_type == MH_EXECUTE) {
457 // On Mac OS X 10.6, the main executable does not normally show up at
458 // index 0. This is because of how 10.6.8 dyld-132.13/src/dyld.cpp
459 // notifyGDB(), the function resposible for causing
460 // dyld_all_image_infos::infoArray to be updated, is called. It is
461 // registered to be called when all dependents of an image have been
462 // mapped (dyld_image_state_dependents_mapped), meaning that the main
463 // executable won’t be added to the list until all of the libraries it
464 // depends on are, even though dyld begins looking at the main executable
465 // first. This changed in later versions of dyld, including those present
466 // in 10.7. 10.9.4 dyld-239.4/src/dyld.cpp updateAllImages() (renamed from
467 // notifyGDB()) is registered to be called when an image itself has been
468 // mapped (dyld_image_state_mapped), regardless of the libraries that it
469 // depends on.
470 //
471 // The interface requires that the main executable be first in the list,
472 // so swap it into the right position.
473 size_t index = modules_.size() - 1;
474 if (main_executable_count == 0) {
475 std::swap(modules_[0], modules_[index]);
476 } else {
477 LOG(WARNING) << base::StringPrintf(
478 "multiple MH_EXECUTE modules (%s, %s)",
479 modules_[0].name.c_str(),
480 modules_[index].name.c_str());
481 }
482 ++main_executable_count;
483 }
484 }
485
486 LOG_IF(WARNING, main_executable_count == 0) << "no MH_EXECUTE modules";
487
488 // all_image_infos.infoArray doesn’t include an entry for dyld, but dyld is
489 // loaded into the process’ address space as a module. Its load address is
490 // easily known given a sufficiently recent all_image_infos.version, but the
491 // timestamp and pathname are not given as they are for other modules.
492 //
493 // The timestamp is a lost cause, because the kernel doesn’t record the
494 // timestamp of the dynamic linker at the time it’s loaded in the same way
495 // that dyld records the timestamps of other modules when they’re loaded. (The
496 // timestamp for the main executable is also not reported and appears as 0
497 // even when accessed via dyld APIs, because it’s loaded by the kernel, not by
498 // dyld.)
499 //
500 // The name can be determined, but it’s not as simple as hardcoding the
501 // default "/usr/lib/dyld" because an executable could have specified anything
502 // in its LC_LOAD_DYLINKER command.
503 if (!found_dyld && all_image_infos.version >= 2 &&
504 all_image_infos.dyldImageLoadAddress) {
505 Module module;
506 module.timestamp = 0;
507
508 // Examine the executable’s LC_LOAD_DYLINKER load command to find the path
509 // used to load dyld.
510 if (all_image_infos.infoArrayCount >= 1 && main_executable_count >= 1) {
511 module.name = modules_[0].reader->DylinkerName();
512 }
513 std::string module_name = !module.name.empty() ? module.name : "(dyld)";
514
515 scoped_ptr<MachOImageReader> reader(new MachOImageReader());
516 if (!reader->Initialize(
517 this, all_image_infos.dyldImageLoadAddress, module_name)) {
518 reader.reset();
519 }
520
521 module.reader = reader.get();
522
523 uint32_t file_type = reader ? reader->FileType() : 0;
524
525 LOG_IF(WARNING, file_type != MH_DYLINKER)
526 << base::StringPrintf("dylinker (%s) has unexpected Mach-O type %d",
527 module.name.c_str(),
528 file_type);
529
530 if (module.name.empty() && file_type == MH_DYLINKER) {
531 // Look inside dyld directly to find its preferred path.
532 module.name = reader->DylinkerName();
533 }
534
535 if (module.name.empty()) {
536 module.name = "(dyld)";
537 }
538
539 // dyld is loaded in the process even if its path can’t be determined.
540 module_readers_.push_back(reader.release());
541 modules_.push_back(module);
542 }
543 }
544
545 mach_vm_address_t ProcessReader::CalculateStackRegion(
546 mach_vm_address_t stack_pointer,
547 mach_vm_size_t* stack_region_size) {
548 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
549
550 // For pthreads, it may be possible to compute the stack region based on the
551 // internal _pthread::stackaddr and _pthread::stacksize. The _pthread struct
552 // for a thread can be located at TSD slot 0, or the known offsets of
553 // stackaddr and stacksize from the TSD area could be used.
554 mach_vm_address_t region_base = stack_pointer;
555 mach_vm_size_t region_size;
556 natural_t depth = 0;
557 vm_prot_t protection;
558 unsigned int user_tag;
559 kern_return_t kr = MachVMRegionRecurseDeepest(
560 task_, &region_base, &region_size, &depth, &protection, &user_tag);
561 if (kr != KERN_SUCCESS) {
562 MACH_LOG(INFO, kr) << "mach_vm_region_recurse";
563 *stack_region_size = 0;
564 return 0;
565 }
566
567 if (region_base > stack_pointer) {
568 // There’s nothing mapped at the stack pointer’s address. Something may have
569 // trashed the stack pointer. Note that this shouldn’t happen for a normal
570 // stack guard region violation because the guard region is mapped but has
571 // VM_PROT_NONE protection.
572 *stack_region_size = 0;
573 return 0;
574 }
575
576 mach_vm_address_t start_address = stack_pointer;
577
578 if ((protection & VM_PROT_READ) == 0) {
579 // If the region isn’t readable, the stack pointer probably points to the
580 // guard region. Don’t include it as part of the stack, and don’t include
581 // anything at any lower memory address. The code below may still possibly
582 // find the real stack region at a memory address higher than this region.
583 start_address = region_base + region_size;
584 } else {
585 // If the ABI requires a red zone, adjust the region to include it if
586 // possible.
587 LocateRedZone(&start_address, &region_base, &region_size, user_tag);
588
589 // Regardless of whether the ABI requires a red zone, capture up to
590 // kExtraCaptureSize additional bytes of stack, but only if present in the
591 // region that was already found.
592 const mach_vm_size_t kExtraCaptureSize = 128;
593 start_address = std::max(start_address >= kExtraCaptureSize
594 ? start_address - kExtraCaptureSize
595 : start_address,
596 region_base);
597
598 // Align start_address to a 16-byte boundary, which can help readers by
599 // ensuring that data is aligned properly. This could page-align instead,
600 // but that might be wasteful.
601 const mach_vm_size_t kDesiredAlignment = 16;
602 start_address &= ~(kDesiredAlignment - 1);
603 DCHECK_GE(start_address, region_base);
604 }
605
606 region_size -= (start_address - region_base);
607 region_base = start_address;
608
609 mach_vm_size_t total_region_size = region_size;
610
611 // The stack region may have gotten split up into multiple abutting regions.
612 // Try to coalesce them. This frequently happens for the main thread’s stack
613 // when setrlimit(RLIMIT_STACK, …) is called. It may also happen if a region
614 // is split up due to an mprotect() or vm_protect() call.
615 //
616 // Stack regions created by the kernel and the pthreads library will be marked
617 // with the VM_MEMORY_STACK user tag. Scanning for multiple adjacent regions
618 // with the same tag should find an entire stack region. Checking that the
619 // protection on individual regions is not VM_PROT_NONE should guarantee that
620 // this algorithm doesn’t collect map entries belonging to another thread’s
621 // stack: well-behaved stacks (such as those created by the kernel and the
622 // pthreads library) have VM_PROT_NONE guard regions at their low-address
623 // ends.
624 //
625 // Other stack regions may not be so well-behaved and thus if user_tag is not
626 // VM_MEMORY_STACK, the single region that was found is used as-is without
627 // trying to merge it with other adjacent regions.
628 if (user_tag == VM_MEMORY_STACK) {
629 mach_vm_address_t try_address = region_base;
630 mach_vm_address_t original_try_address;
631
632 while (try_address += region_size,
633 original_try_address = try_address,
634 (kr = MachVMRegionRecurseDeepest(task_,
635 &try_address,
636 &region_size,
637 &depth,
638 &protection,
639 &user_tag) == KERN_SUCCESS) &&
640 try_address == original_try_address &&
641 (protection & VM_PROT_READ) != 0 &&
642 user_tag == VM_MEMORY_STACK) {
643 total_region_size += region_size;
644 }
645
646 if (kr != KERN_SUCCESS && kr != KERN_INVALID_ADDRESS) {
647 // Tolerate KERN_INVALID_ADDRESS because it will be returned when there
648 // are no more regions in the map at or above the specified |try_address|.
649 MACH_LOG(INFO, kr) << "mach_vm_region_recurse";
650 }
651 }
652
653 *stack_region_size = total_region_size;
654 return region_base;
655 }
656
657 void ProcessReader::LocateRedZone(mach_vm_address_t* const start_address,
658 mach_vm_address_t* const region_base,
659 mach_vm_address_t* const region_size,
660 const unsigned int user_tag) {
661 #if defined(ARCH_CPU_X86_FAMILY)
662 if (Is64Bit()) {
663 // x86_64 has a red zone. See AMD64 ABI 0.99.6,
664 // http://www.x86-64.org/documentation/abi.pdf, section 3.2.2, “The Stack
665 // Frame”.
666 const mach_vm_size_t kRedZoneSize = 128;
667 mach_vm_address_t red_zone_base =
668 *start_address >= kRedZoneSize ? *start_address - kRedZoneSize : 0;
669 bool red_zone_ok = false;
670 if (red_zone_base >= *region_base) {
671 // The red zone is within the region already discovered.
672 red_zone_ok = true;
673 } else if (red_zone_base < *region_base && user_tag == VM_MEMORY_STACK) {
674 // Probe to see if there’s a region immediately below the one already
675 // discovered.
676 mach_vm_address_t red_zone_region_base = red_zone_base;
677 mach_vm_size_t red_zone_region_size;
678 natural_t red_zone_depth = 0;
679 vm_prot_t red_zone_protection;
680 unsigned int red_zone_user_tag;
681 kern_return_t kr = MachVMRegionRecurseDeepest(task_,
682 &red_zone_region_base,
683 &red_zone_region_size,
684 &red_zone_depth,
685 &red_zone_protection,
686 &red_zone_user_tag);
687 if (kr != KERN_SUCCESS) {
688 MACH_LOG(INFO, kr) << "mach_vm_region_recurse";
689 *start_address = *region_base;
690 } else if (red_zone_region_base + red_zone_region_size == *region_base &&
691 (red_zone_protection & VM_PROT_READ) != 0 &&
692 red_zone_user_tag == user_tag) {
693 // The region containing the red zone is immediately below the region
694 // already found, it’s readable (not the guard region), and it has the
695 // same user tag as the region already found, so merge them.
696 red_zone_ok = true;
697 *region_base -= red_zone_region_size;
698 *region_size += red_zone_region_size;
699 }
700 }
701
702 if (red_zone_ok) {
703 // Begin capturing from the base of the red zone (but not the entire
704 // region that encompasses the red zone).
705 *start_address = red_zone_base;
706 } else {
707 // The red zone would go lower into another region in memory, but no
708 // region was found. Memory can only be captured to an address as low as
709 // the base address of the region already found.
710 *start_address = *region_base;
711 }
712 }
713 #endif
714 }
715
716 } // namespace crashpad
OLDNEW
« no previous file with comments | « util/mac/process_reader.h ('k') | util/mac/process_reader_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698