Index: util/mac/process_reader.cc |
diff --git a/util/mac/process_reader.cc b/util/mac/process_reader.cc |
index 7013f7ec70ff88c5bfd44e9aa9d8e737490ab351..fbe5ff07e19656dddaa0dc3375188641a1b896ff 100644 |
--- a/util/mac/process_reader.cc |
+++ b/util/mac/process_reader.cc |
@@ -24,6 +24,8 @@ |
#include "base/mac/mach_logging.h" |
#include "base/mac/scoped_mach_port.h" |
#include "base/mac/scoped_mach_vm.h" |
+#include "util/mac/mach_o_image_reader.h" |
+#include "util/mac/process_types.h" |
#include "util/misc/scoped_forbid_return.h" |
namespace { |
@@ -352,11 +354,119 @@ void ProcessReader::InitializeModules() { |
initialized_modules_ = true; |
- // TODO(mark): Complete this implementation. The implementation depends on |
- // process_types, which cannot land yet because it depends on this file, |
- // process_reader. This temporary “cut” was made to avoid a review that’s too |
- // large. Yes, this circular dependency is unfortunate. Suggestions are |
- // welcome. |
+ // This API only works on Mac OS X 10.6 and higher. On Mac OS X 10.5, find the |
+ // “_dyld_all_image_infos” symbol in the loaded LC_LOAD_DYLINKER (dyld). |
+ task_dyld_info_data_t dyld_info; |
+ mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; |
+ kern_return_t kr = task_info( |
+ task_, TASK_DYLD_INFO, reinterpret_cast<task_info_t>(&dyld_info), &count); |
+ if (kr != KERN_SUCCESS) { |
+ MACH_LOG(WARNING, kr) << "task_info"; |
+ return; |
+ } |
+ |
+ // TODO(mark): Deal with statically linked executables which don’t use dyld. |
+ // This may look for the module that matches the executable path in the same |
+ // data set that vmmap uses. |
+ |
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 |
+ // The task_dyld_info_data_t struct grew in 10.7, adding the format field. |
+ // Don’t check this field if it’s not present, which can happen when either |
+ // the SDK used at compile time or the kernel at run time are too old and |
+ // don’t know about it. |
+ if (count >= TASK_DYLD_INFO_COUNT) { |
+ const integer_t kExpectedFormat = |
+ !Is64Bit() ? TASK_DYLD_ALL_IMAGE_INFO_32 : TASK_DYLD_ALL_IMAGE_INFO_64; |
+ if (dyld_info.all_image_info_format != kExpectedFormat) { |
+ LOG(WARNING) << "unexpected task_dyld_info_data_t::all_image_info_format " |
+ << dyld_info.all_image_info_format; |
+ DCHECK_EQ(dyld_info.all_image_info_format, kExpectedFormat); |
+ return; |
+ } |
+ } |
+#endif |
+ |
+ process_types::dyld_all_image_infos all_image_infos; |
+ if (!all_image_infos.Read(this, dyld_info.all_image_info_addr)) { |
+ LOG(WARNING) << "could not read dyld_all_image_infos"; |
+ return; |
+ } |
+ |
+ // Note that all_image_infos.infoArrayCount may be 0 if a crash occurred while |
+ // dyld was loading the executable. This can happen if a required dynamic |
+ // library was not found. |
+ DCHECK_GE(all_image_infos.version, 1u); |
+ DCHECK_NE(all_image_infos.infoArray, static_cast<mach_vm_address_t>(NULL)); |
+ |
+ std::vector<process_types::dyld_image_info> image_info_vector( |
+ all_image_infos.infoArrayCount); |
+ if (!process_types::dyld_image_info::ReadArrayInto(this, |
+ all_image_infos.infoArray, |
+ image_info_vector.size(), |
+ &image_info_vector[0])) { |
+ LOG(WARNING) << "could not read dyld_image_info array"; |
+ return; |
+ } |
+ |
+ bool found_dyld = false; |
+ for (const process_types::dyld_image_info& image_info : image_info_vector) { |
+ ProcessReaderModule module; |
+ module.address = image_info.imageLoadAddress; |
+ module.timestamp = image_info.imageFileModDate; |
+ if (!task_memory_->ReadCString(image_info.imageFilePath, &module.name)) { |
+ LOG(WARNING) << "could not read dyld_image_info::imageFilePath"; |
+ // Proceed anyway with an empty module name. |
+ } |
+ |
+ modules_.push_back(module); |
+ |
+ if (all_image_infos.version >= 2 && all_image_infos.dyldImageLoadAddress && |
+ image_info.imageLoadAddress == all_image_infos.dyldImageLoadAddress) { |
+ found_dyld = true; |
+ } |
+ } |
+ |
+ // all_image_infos.infoArray doesn’t include an entry for dyld, but dyld is |
+ // loaded into the process’ address space as a module. Its load address is |
+ // easily known given a sufficiently recent all_image_infos.version, but the |
+ // timestamp and pathname are not given as they are for other modules. |
+ // |
+ // The timestamp is a lost cause, because the kernel doesn’t record the |
+ // timestamp of the dynamic linker at the time it’s loaded in the same way |
+ // that dyld records the timestamps of other modules when they’re loaded. (The |
+ // timestamp for the main executable is also not reported and appears as 0 |
+ // even when accessed via dyld APIs, because it’s loaded by the kernel, not by |
+ // dyld.) |
+ // |
+ // The name can be determined, but it’s not as simple as hardcoding the |
+ // default "/usr/lib/dyld" because an executable could have specified anything |
+ // in its LC_LOAD_DYLINKER command. |
+ if (!found_dyld && all_image_infos.version >= 2 && |
+ all_image_infos.dyldImageLoadAddress) { |
+ ProcessReaderModule module; |
+ module.address = all_image_infos.dyldImageLoadAddress; |
+ module.timestamp = 0; |
+ |
+ // Examine the executable’s LC_LOAD_DYLINKER load command to find the path |
+ // used to load dyld. |
+ MachOImageReader executable; |
+ if (all_image_infos.infoArrayCount >= 1 && |
+ executable.Initialize(this, modules_[0].address, modules_[0].name) && |
+ executable.FileType() == MH_EXECUTE && |
+ !executable.DylinkerName().empty()) { |
+ module.name = executable.DylinkerName(); |
+ } else { |
+ // Look inside dyld directly to find its preferred path. |
+ MachOImageReader dyld; |
+ if (dyld.Initialize(this, module.address, "(dyld)") && |
+ dyld.FileType() == MH_DYLINKER && !dyld.DylinkerName().empty()) { |
+ module.name = dyld.DylinkerName(); |
+ } |
+ } |
+ |
+ // dyld is loaded in the process even if its path can’t be determined. |
+ modules_.push_back(module); |
+ } |
} |
mach_vm_address_t ProcessReader::CalculateStackRegion( |