Index: util/mac/process_reader.cc |
diff --git a/util/mac/process_reader.cc b/util/mac/process_reader.cc |
index 5d990c4e6b348c3568bed4ff1b9fc2cafe944db3..f82a4f5c5b3e5f903dbe074f965a90820b3776b1 100644 |
--- a/util/mac/process_reader.cc |
+++ b/util/mac/process_reader.cc |
@@ -24,6 +24,7 @@ |
#include "base/mac/mach_logging.h" |
#include "base/mac/scoped_mach_port.h" |
#include "base/mac/scoped_mach_vm.h" |
+#include "base/strings/stringprintf.h" |
#include "util/mac/mach_o_image_reader.h" |
#include "util/mac/process_types.h" |
#include "util/misc/scoped_forbid_return.h" |
@@ -82,7 +83,7 @@ ProcessReader::Thread::Thread() |
priority(0) { |
} |
-ProcessReader::Module::Module() : name(), address(0), timestamp(0) { |
+ProcessReader::Module::Module() : name(), reader(NULL), timestamp(0) { |
} |
ProcessReader::Module::~Module() { |
@@ -92,6 +93,7 @@ ProcessReader::ProcessReader() |
: kern_proc_info_(), |
threads_(), |
modules_(), |
+ module_readers_(), |
task_memory_(), |
task_(MACH_PORT_NULL), |
initialized_(), |
@@ -389,11 +391,23 @@ void ProcessReader::InitializeModules() { |
return; |
} |
+ DCHECK_GE(all_image_infos.version, 1u); |
+ |
// 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)); |
+ // library was not found. Similarly, all_image_infos.infoArray may be NULL if |
+ // a crash occurred while dyld was updating it. |
+ // |
+ // TODO(mark): It may be possible to recover from these situations by looking |
+ // through memory mappings for Mach-O images. |
+ if (all_image_infos.infoArrayCount == 0) { |
+ LOG(WARNING) << "all_image_infos.infoArrayCount is zero"; |
+ return; |
+ } |
+ if (!all_image_infos.infoArray) { |
+ LOG(WARNING) << "all_image_infos.infoArray is NULL"; |
+ return; |
+ } |
std::vector<process_types::dyld_image_info> image_info_vector( |
all_image_infos.infoArrayCount); |
@@ -405,24 +419,72 @@ void ProcessReader::InitializeModules() { |
return; |
} |
+ size_t main_executable_count = 0; |
bool found_dyld = false; |
+ modules_.reserve(image_info_vector.size()); |
for (const process_types::dyld_image_info& image_info : image_info_vector) { |
Module 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. |
} |
+ scoped_ptr<MachOImageReader> reader(new MachOImageReader()); |
+ if (!reader->Initialize(this, image_info.imageLoadAddress, module.name)) { |
+ reader.reset(); |
+ } |
+ |
+ module.reader = reader.get(); |
+ |
+ uint32_t file_type = reader ? reader->FileType() : 0; |
+ |
+ module_readers_.push_back(reader.release()); |
modules_.push_back(module); |
if (all_image_infos.version >= 2 && all_image_infos.dyldImageLoadAddress && |
image_info.imageLoadAddress == all_image_infos.dyldImageLoadAddress) { |
found_dyld = true; |
+ |
+ LOG_IF(WARNING, file_type != MH_DYLINKER) |
+ << base::StringPrintf("dylinker (%s) has unexpected Mach-O type %d", |
+ module.name.c_str(), |
+ file_type); |
+ } |
+ |
+ if (file_type == MH_EXECUTE) { |
+ // On Mac OS X 10.6, the main executable does not normally show up at |
+ // index 0. This is because of how 10.6.8 dyld-132.13/src/dyld.cpp |
+ // notifyGDB(), the function resposible for causing |
+ // dyld_all_image_infos::infoArray to be updated, is called. It is |
+ // registered to be called when all dependents of an image have been |
+ // mapped (dyld_image_state_dependents_mapped), meaning that the main |
+ // executable won’t be added to the list until all of the libraries it |
+ // depends on are, even though dyld begins looking at the main executable |
+ // first. This changed in later versions of dyld, including those present |
+ // in 10.7. 10.9.4 dyld-239.4/src/dyld.cpp updateAllImages() (renamed from |
+ // notifyGDB()) is registered to be called when an image itself has been |
+ // mapped (dyld_image_state_mapped), regardless of the libraries that it |
+ // depends on. |
+ // |
+ // The interface requires that the main executable be first in the list, |
+ // so swap it into the right position. |
+ size_t index = modules_.size() - 1; |
+ if (main_executable_count == 0) { |
+ std::swap(modules_[0], modules_[index]); |
+ } else { |
+ LOG(WARNING) << base::StringPrintf( |
+ "multiple MH_EXECUTE modules (%s, %s)", |
+ modules_[0].name.c_str(), |
+ modules_[index].name.c_str()); |
+ } |
+ ++main_executable_count; |
} |
} |
+ LOG_IF(WARNING, main_executable_count == 0) << "no MH_EXECUTE modules"; |
+ |
// 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 |
@@ -441,27 +503,41 @@ void ProcessReader::InitializeModules() { |
if (!found_dyld && all_image_infos.version >= 2 && |
all_image_infos.dyldImageLoadAddress) { |
Module 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 { |
+ if (all_image_infos.infoArrayCount >= 1 && main_executable_count >= 1) { |
+ module.name = modules_[0].reader->DylinkerName(); |
+ } |
+ std::string module_name = !module.name.empty() ? module.name : "(dyld)"; |
+ |
+ scoped_ptr<MachOImageReader> reader(new MachOImageReader()); |
+ if (!reader->Initialize( |
+ this, all_image_infos.dyldImageLoadAddress, module_name)) { |
+ reader.reset(); |
+ } |
+ |
+ module.reader = reader.get(); |
+ |
+ uint32_t file_type = reader ? reader->FileType() : 0; |
+ |
+ LOG_IF(WARNING, file_type != MH_DYLINKER) |
+ << base::StringPrintf("dylinker (%s) has unexpected Mach-O type %d", |
+ module.name.c_str(), |
+ file_type); |
+ |
+ if (module.name.empty() && file_type == MH_DYLINKER) { |
// 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(); |
- } |
+ module.name = reader->DylinkerName(); |
+ } |
+ |
+ if (module.name.empty()) { |
+ module.name = "(dyld)"; |
} |
// dyld is loaded in the process even if its path can’t be determined. |
+ module_readers_.push_back(reader.release()); |
modules_.push_back(module); |
} |
} |