Add crashpad_info, MachOImageAnnotationsReader, and its test.

TEST=snapshot_test MachOImageAnnotationsReader.*
R=rsesek@chromium.org

Review URL: https://codereview.chromium.org/651283003
This commit is contained in:
Mark Mentovai 2014-10-17 13:47:02 -04:00
parent bcae4d94d5
commit b43f510a52
11 changed files with 816 additions and 4 deletions

View File

@ -26,6 +26,8 @@
'sources': [
'capture_context_mac.h',
'capture_context_mac.S',
'crashpad_info.cc',
'crashpad_info.h',
'simple_string_dictionary.cc',
'simple_string_dictionary.h',
'simulate_crash.h',

76
client/crashpad_info.cc Normal file
View File

@ -0,0 +1,76 @@
// Copyright 2014 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "client/crashpad_info.h"
#include <mach-o/loader.h>
#if CXX_LIBRARY_VERSION >= 2011
#include <type_traits>
#endif
namespace {
static const uint32_t kCrashpadInfoVersion = 1;
} // namespace
namespace crashpad {
#if CXX_LIBRARY_VERSION >= 2011
// In C++11, check that CrashpadInfo has standard layout, which is what is
// actually important.
static_assert(std::is_standard_layout<CrashpadInfo>::value,
"CrashpadInfo must be standard layout");
#else
// In C++98 (ISO 14882), section 9.5.1 says that a union cannot have a member
// with a non-trivial ctor, copy ctor, dtor, or assignment operator. Use this
// property to ensure that CrashpadInfo remains POD. This doesnt work for C++11
// because the requirements for unions have been relaxed.
union Compile_Assert {
CrashpadInfo Compile_Assert__CrashpadInfo_must_be_pod;
};
#endif
// Put the structure in __DATA,__crashpad_info where it can be easily found
// without having to consult the symbol table. The “used” attribute prevents it
// from being dead-stripped. This isnt placed in an unnamed namespace:
// hopefully, this will catch attempts to place multiple copies of this
// structure into the same module. If thats attempted, and the name of the
// symbol is the same in each translation unit, it will result in a linker
// error, which is better than having multiple structures show up.
//
// This may result in a static module initializer in debug-mode builds, but
// because its POD, no code should need to run to initialize this under
// release-mode optimization.
__attribute__((section(SEG_DATA ",__crashpad_info"),
used,
visibility("hidden"))) CrashpadInfo g_crashpad_info;
// static
CrashpadInfo* CrashpadInfo::GetCrashpadInfo() {
return &g_crashpad_info;
}
CrashpadInfo::CrashpadInfo()
: signature_(kSignature),
size_(sizeof(*this)),
version_(kCrashpadInfoVersion),
padding_0_(0),
simple_annotations_(nullptr) {
}
const uint32_t CrashpadInfo::kSignature;
} // namespace crashpad

66
client/crashpad_info.h Normal file
View File

@ -0,0 +1,66 @@
// Copyright 2014 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CRASHPAD_CLIENT_CRASHPAD_INFO_H_
#define CRASHPAD_CLIENT_CRASHPAD_INFO_H_
#include "base/basictypes.h"
#include <stdint.h>
#include "client/simple_string_dictionary.h"
namespace crashpad {
//! \brief A structure that can be used by a Crashpad-enabled program to
//! provide information to the Crashpad crash handler.
//!
//! It is possible for one CrashpadInfo structure to appear in each loaded code
//! module in a process, but from the perspective of the user of the client
//! interface, there is only one global CrashpadInfo structure, located in the
//! module that contains the client interface code.
struct CrashpadInfo {
public:
//! \brief Returns the global CrashpadInfo structure.
static CrashpadInfo* GetCrashpadInfo();
CrashpadInfo();
void set_simple_annotations(SimpleStringDictionary* simple_annotations) {
simple_annotations_ = simple_annotations;
}
static const uint32_t kSignature = 'CPad';
private:
// The compiler wont necessarily see anyone using these fields, but it
// shouldnt warn about that. These fields arent intended for use by the
// process theyre found in, theyre supposed to be read by the crash
// reporting process.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-private-field"
// Fields present in version 1:
uint32_t signature_; // kSignature
uint32_t size_; // The size of the entire CrashpadInfo structure.
uint32_t version_; // kVersion
uint32_t padding_0_;
SimpleStringDictionary* simple_annotations_; // weak
#pragma clang diagnostic pop
DISALLOW_COPY_AND_ASSIGN(CrashpadInfo);
};
} // namespace crashpad
#endif // CRASHPAD_CLIENT_CRASHPAD_INFO_H_

View File

@ -0,0 +1,198 @@
// Copyright 2014 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "snapshot/mac/mach_o_image_annotations_reader.h"
#include <mach-o/loader.h>
#include <mach/mach.h>
#include "base/logging.h"
#include "client/crashpad_info.h"
#include "client/simple_string_dictionary.h"
#include "snapshot/mac/mach_o_image_reader.h"
#include "snapshot/mac/process_reader.h"
#include "snapshot/mac/process_types.h"
#include "util/mach/task_memory.h"
#include "util/stdlib/strnlen.h"
namespace crashpad {
MachOImageAnnotationsReader::MachOImageAnnotationsReader(
ProcessReader* process_reader,
const MachOImageReader* image_reader,
const std::string& name)
: name_(name),
process_reader_(process_reader),
image_reader_(image_reader) {
}
std::vector<std::string> MachOImageAnnotationsReader::Vector() const {
std::vector<std::string> vector_annotations;
ReadCrashReporterClientAnnotations(&vector_annotations);
ReadDyldErrorStringAnnotation(&vector_annotations);
return vector_annotations;
}
std::map<std::string, std::string> MachOImageAnnotationsReader::SimpleMap()
const {
std::map<std::string, std::string> simple_map_annotations;
ReadCrashpadSimpleAnnotations(&simple_map_annotations);
return simple_map_annotations;
}
void MachOImageAnnotationsReader::ReadCrashReporterClientAnnotations(
std::vector<std::string>* vector_annotations) const {
mach_vm_address_t crash_info_address;
const process_types::section* crash_info_section =
image_reader_->GetSectionByName(
SEG_DATA, "__crash_info", &crash_info_address);
if (!crash_info_section) {
return;
}
process_types::crashreporter_annotations_t crash_info;
if (crash_info_section->size < crash_info.ExpectedSize(process_reader_)) {
LOG(WARNING) << "small crash info section size " << crash_info_section->size
<< " in " << name_;
return;
}
if (!crash_info.Read(process_reader_, crash_info_address)) {
LOG(WARNING) << "could not read crash info from " << name_;
return;
}
if (crash_info.version != 4) {
LOG(WARNING) << "unexpected crash info version " << crash_info.version
<< " in " << name_;
return;
}
// This number was totally made up out of nowhere, but it seems prudent to
// enforce some limit.
const size_t kMaxMessageSize = 1024;
if (crash_info.message) {
std::string message;
if (process_reader_->Memory()->
ReadCStringSizeLimited(
crash_info.message, kMaxMessageSize, &message)) {
vector_annotations->push_back(message);
} else {
LOG(WARNING) << "could not read crash message in " << name_;
}
}
if (crash_info.message2) {
std::string message;
if (process_reader_->Memory()->
ReadCStringSizeLimited(
crash_info.message2, kMaxMessageSize, &message)) {
vector_annotations->push_back(message);
} else {
LOG(WARNING) << "could not read crash message 2 in " << name_;
}
}
}
void MachOImageAnnotationsReader::ReadDyldErrorStringAnnotation(
std::vector<std::string>* vector_annotations) const {
// dyld stores its error string at the external symbol for |const char
// error_string[1024]|. See 10.9.5 dyld-239.4/src/dyld.cpp error_string.
if (image_reader_->FileType() != MH_DYLINKER) {
return;
}
mach_vm_address_t error_string_address;
if (!image_reader_->LookUpExternalDefinedSymbol("_error_string",
&error_string_address)) {
return;
}
std::string message;
// 1024 here is distinct from kMaxMessageSize above, because it refers to a
// precisely-sized buffer inside dyld.
if (process_reader_->Memory()->
ReadCStringSizeLimited(error_string_address, 1024, &message)) {
if (!message.empty()) {
vector_annotations->push_back(message);
}
} else {
LOG(WARNING) << "could not read dylinker error string from " << name_;
}
}
void MachOImageAnnotationsReader::ReadCrashpadSimpleAnnotations(
std::map<std::string, std::string>* simple_map_annotations) const {
mach_vm_address_t crashpad_info_address;
const process_types::section* crashpad_info_section =
image_reader_->GetSectionByName(
SEG_DATA, "__crashpad_info", &crashpad_info_address);
if (!crashpad_info_section) {
return;
}
process_types::CrashpadInfo crashpad_info;
if (crashpad_info_section->size <
crashpad_info.ExpectedSize(process_reader_)) {
LOG(WARNING) << "small crashpad info section size "
<< crashpad_info_section->size << " in " << name_;
return;
}
if (!crashpad_info.Read(process_reader_, crashpad_info_address)) {
LOG(WARNING) << "could not read crashpad info from " << name_;
return;
}
if (crashpad_info.signature != CrashpadInfo::kSignature ||
crashpad_info.size != crashpad_info_section->size ||
crashpad_info.version < 1) {
LOG(WARNING) << "unexpected crashpad info data in " << name_;
return;
}
if (!crashpad_info.simple_annotations) {
return;
}
std::vector<SimpleStringDictionary::Entry>
simple_annotations(SimpleStringDictionary::num_entries);
if (!process_reader_->Memory()
->Read(crashpad_info.simple_annotations,
simple_annotations.size() * sizeof(simple_annotations[0]),
&simple_annotations[0])) {
LOG(WARNING) << "could not read simple annotations from " << name_;
return;
}
for (const auto& entry : simple_annotations) {
size_t key_length = strnlen(entry.key, sizeof(entry.key));
if (key_length) {
std::string key(entry.key, key_length);
std::string value(entry.value, strnlen(entry.value, sizeof(entry.value)));
if (!simple_map_annotations->count(key)) {
simple_map_annotations->insert(
std::pair<std::string, std::string>(key, value));
} else {
LOG(INFO) << "duplicate simple annotation " << key << " in " << name_;
}
}
}
}
} // namespace crashpad

View File

@ -0,0 +1,92 @@
// Copyright 2014 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_ANNOTATIONS_READER_H_
#define CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_ANNOTATIONS_READER_H_
#include <map>
#include <string>
#include <vector>
#include "base/basictypes.h"
namespace crashpad {
class MachOImageReader;
class ProcessReader;
//! \brief A reader for annotations stored in a Mach-O image mapped into another
//! process.
//!
//! These annotations are stored for the benefit of crash reporters, and provide
//! information though to be potentially useful for crash analysis. This class
//! can decode annotations stored in these formats:
//! - CrashpadInfo. This format is used by Crashpad clients. The “simple
//! annotations” are recovered from any module with a compatible data
//! section, and are included in the annotations returned by SimpleMap().
//! - `CrashReporterClient.h`s `crashreporter_annotations_t`. This format is
//! used by Apple code. The `message` and `message2` fields can be recovered
//! from any module with a compatible data section, and are included in the
//! annotations returned by Vector().
//! - `dyld`s `error_string`. This format is used exclusively by dyld,
//! typically for fatal errors. This string can be recovered from any
//! `MH_DYLINKER`-type module with this symbol, and is included in the
//! annotations returned by Vector().
class MachOImageAnnotationsReader {
public:
//! \brief Constructs an object.
//!
//! \param[in] process_reader The reader for the remote process.
//! \param[in] image_reader The MachOImageReader for the Mach-O image file
//! contained within the remote process.
//! \param[in] name The modules name, a string to be used in logged messages.
//! This string is for diagnostic purposes only, and may be empty.
MachOImageAnnotationsReader(ProcessReader* process_reader,
const MachOImageReader* image_reader,
const std::string& name);
~MachOImageAnnotationsReader() {}
//! \brief Returns the modules annotations that are organized as a vector of
//! strings.
std::vector<std::string> Vector() const;
//! \brief Returns the modules annotations that are organized as key-value
//! pairs, where all keys and values are strings.
std::map<std::string, std::string> SimpleMap() const;
private:
// Reades crashreporter_annotations_t::message and
// crashreporter_annotations_t::message2 on behalf of Vector().
void ReadCrashReporterClientAnnotations(
std::vector<std::string>* vector_annotations) const;
// Reads dyld_error_string on behalf of Vector().
void ReadDyldErrorStringAnnotation(
std::vector<std::string>* vector_annotations) const;
// Reads CrashpadInfo::simple_annotations_ on behalf of SimpleMap().
void ReadCrashpadSimpleAnnotations(
std::map<std::string, std::string>* simple_map_annotations) const;
std::string name_;
ProcessReader* process_reader_; // weak
const MachOImageReader* image_reader_; // weak
DISALLOW_COPY_AND_ASSIGN(MachOImageAnnotationsReader);
};
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_ANNOTATIONS_READER_H_

View File

@ -0,0 +1,334 @@
// Copyright 2014 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "snapshot/mac/mach_o_image_annotations_reader.h"
#include <dlfcn.h>
#include <mach/mach.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <map>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "client/crashpad_info.h"
#include "client/simple_string_dictionary.h"
#include "gtest/gtest.h"
#include "snapshot/mac/process_reader.h"
#include "util/file/fd_io.h"
#include "util/mac/mac_util.h"
#include "util/mach/exc_server_variants.h"
#include "util/mach/exception_ports.h"
#include "util/mach/mach_message_server.h"
#include "util/test/errors.h"
#include "util/test/mac/mach_errors.h"
#include "util/test/mac/mach_multiprocess.h"
namespace crashpad {
namespace test {
namespace {
class TestMachOImageAnnotationsReader final : public MachMultiprocess,
public UniversalMachExcServer {
public:
enum TestType {
// Dont crash, just test the CrashpadInfo interface.
kDontCrash = 0,
// The child process should crash by calling abort(). The parent verifies
// that the system libraries set the expected annotations.
kCrashAbort,
// The child process should crash by setting DYLD_INSERT_LIBRARIES to
// contain a nonexistent library. The parent verifies that dyld sets the
// expected annotations.
kCrashDyld,
};
explicit TestMachOImageAnnotationsReader(TestType test_type)
: MachMultiprocess(),
UniversalMachExcServer(),
test_type_(test_type) {
}
~TestMachOImageAnnotationsReader() {}
// UniversalMachExcServer:
kern_return_t CatchMachException(exception_behavior_t behavior,
exception_handler_t exception_port,
thread_t thread,
task_t task,
exception_type_t exception,
const mach_exception_data_type_t* code,
mach_msg_type_number_t code_count,
thread_state_flavor_t* flavor,
const natural_t* old_state,
mach_msg_type_number_t old_state_count,
thread_state_t new_state,
mach_msg_type_number_t* new_state_count,
bool* destroy_complex_request) override {
*destroy_complex_request = true;
EXPECT_EQ(ChildTask(), task);
ProcessReader process_reader;
bool rv = process_reader.Initialize(task);
if (!rv) {
ADD_FAILURE();
} else {
const std::vector<ProcessReader::Module>& modules =
process_reader.Modules();
std::vector<std::string> all_annotations_vector;
for (const ProcessReader::Module& module : modules) {
MachOImageAnnotationsReader module_annotations_reader(
&process_reader, module.reader, module.name);
std::vector<std::string> module_annotations_vector =
module_annotations_reader.Vector();
all_annotations_vector.insert(all_annotations_vector.end(),
module_annotations_vector.begin(),
module_annotations_vector.end());
}
// Mac OS X 10.6 doesnt have support for CrashReporter annotations
// (CrashReporterClient.h), so dont look for any special annotations in
// that version.
int mac_os_x_minor_version = MacOSXMinorVersion();
if (mac_os_x_minor_version > 7) {
EXPECT_GE(all_annotations_vector.size(), 1u);
const char* expected_annotation = nullptr;
switch (test_type_) {
case kCrashAbort:
// The child process calls abort(), so the expected annotation
// reflects this, with a string set by 10.7.5
// Libc-763.13/stdlib/abort-fbsd.c abort(). This string is still
// present in 10.9.5 Libc-997.90.3/stdlib/FreeBSD/abort.c abort(),
// but because abort() tests to see if a message is already set and
// something else in Libc will have set a message, this string is
// not the expectation on 10.9 or higher. Instead, after fork(), the
// child process has a message indicating that a fork() without
// exec() occurred. See 10.9.5 Libc-997.90.3/sys/_libc_fork_child.c
// _libc_fork_child().
expected_annotation =
mac_os_x_minor_version <= 8
? "abort() called"
: "crashed on child side of fork pre-exec";
break;
case kCrashDyld:
// This is independent of dylds error_string, which is tested
// below.
expected_annotation = "dyld: launch, loading dependent libraries";
break;
default:
ADD_FAILURE();
break;
}
size_t expected_annotation_length = strlen(expected_annotation);
bool found = false;
for (const std::string& annotation : all_annotations_vector) {
// Look for the expectation as a leading susbtring, because the actual
// string that dyld uses will have the contents of the
// DYLD_INSERT_LIBRARIES environment variable appended to it on Mac
// OS X 10.10.
if (annotation.substr(0, expected_annotation_length) ==
expected_annotation) {
found = true;
break;
}
}
EXPECT_TRUE(found);
}
// dyld exposes its error_string at least as far back as Mac OS X 10.4.
if (test_type_ == kCrashDyld) {
const char kExpectedAnnotation[] = "could not load inserted library";
size_t expected_annotation_length = strlen(kExpectedAnnotation);
bool found = false;
for (const std::string& annotation : all_annotations_vector) {
// Look for the expectation as a leading substring, because the actual
// string will contain the librarys pathname and, on Mac OS X 10.9
// and later, a reason.
if (annotation.substr(0, expected_annotation_length) ==
kExpectedAnnotation) {
found = true;
break;
}
}
EXPECT_TRUE(found);
}
}
return ExcServerSuccessfulReturnValue(behavior, false);
}
private:
// MachMultiprocess:
void MachMultiprocessParent() override {
ProcessReader process_reader;
ASSERT_TRUE(process_reader.Initialize(ChildTask()));
// Wait for the child process to indicate that its done setting up its
// annotations via the CrashpadInfo interface.
char c;
CheckedReadFD(ReadPipeFD(), &c, sizeof(c));
// Verify the “simple map” annotations set via the CrashpadInfo interface.
const std::vector<ProcessReader::Module>& modules =
process_reader.Modules();
std::map<std::string, std::string> all_annotations_simple_map;
for (const ProcessReader::Module& module : modules) {
MachOImageAnnotationsReader module_annotations_reader(
&process_reader, module.reader, module.name);
std::map<std::string, std::string> module_annotations_simple_map =
module_annotations_reader.SimpleMap();
all_annotations_simple_map.insert(module_annotations_simple_map.begin(),
module_annotations_simple_map.end());
}
EXPECT_GE(all_annotations_simple_map.size(), 5u);
EXPECT_EQ("crash", all_annotations_simple_map["#TEST# pad"]);
EXPECT_EQ("value", all_annotations_simple_map["#TEST# key"]);
EXPECT_EQ("y", all_annotations_simple_map["#TEST# x"]);
EXPECT_EQ("shorter", all_annotations_simple_map["#TEST# longer"]);
EXPECT_EQ("", all_annotations_simple_map["#TEST# empty_value"]);
// Tell the child process that its permitted to crash.
CheckedWriteFD(WritePipeFD(), &c, sizeof(c));
if (test_type_ != kDontCrash) {
// Handle the childs crash. Further validation will be done in
// CatchMachException().
mach_msg_return_t mr =
MachMessageServer::Run(this,
LocalPort(),
MACH_MSG_OPTION_NONE,
MachMessageServer::kOneShot,
MachMessageServer::kBlocking,
MACH_MSG_TIMEOUT_NONE);
EXPECT_EQ(MACH_MSG_SUCCESS, mr)
<< MachErrorMessage(mr, "MachMessageServer::Run");
switch (test_type_) {
case kCrashAbort:
SetExpectedChildTermination(kTerminationSignal, SIGABRT);
break;
case kCrashDyld:
// dyld fatal errors result in the execution of an int3 instruction on
// x86 and a trap instruction on ARM, both of which raise SIGTRAP.
// 10.9.5 dyld-239.4/src/dyldStartup.s _dyld_fatal_error.
SetExpectedChildTermination(kTerminationSignal, SIGTRAP);
break;
default:
FAIL();
break;
}
}
}
void MachMultiprocessChild() override {
CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo();
// This is “leaked” to crashpad_info.
SimpleStringDictionary* simple_annotations = new SimpleStringDictionary();
simple_annotations->SetKeyValue("#TEST# pad", "break");
simple_annotations->SetKeyValue("#TEST# key", "value");
simple_annotations->SetKeyValue("#TEST# pad", "crash");
simple_annotations->SetKeyValue("#TEST# x", "y");
simple_annotations->SetKeyValue("#TEST# longer", "shorter");
simple_annotations->SetKeyValue("#TEST# empty_value", "");
crashpad_info->set_simple_annotations(simple_annotations);
// Tell the parent that the environment has been set up.
char c = '\0';
CheckedWriteFD(WritePipeFD(), &c, sizeof(c));
// Wait for the parent to indicate that its safe to crash.
CheckedReadFD(ReadPipeFD(), &c, sizeof(c));
// Direct an exception message to the exception server running in the
// parent.
ExceptionPorts exception_ports(ExceptionPorts::kTargetTypeTask,
mach_task_self());
ASSERT_TRUE(exception_ports.SetExceptionPort(
EXC_MASK_CRASH, RemotePort(), EXCEPTION_DEFAULT, THREAD_STATE_NONE));
switch (test_type_) {
case kDontCrash:
break;
case kCrashAbort:
abort();
break;
case kCrashDyld: {
// Set DYLD_INSERT_LIBRARIES to contain a library that does not exist.
// Unable to load it, dyld will abort with a fatal error.
ASSERT_EQ(
0,
setenv(
"DYLD_INSERT_LIBRARIES", "/var/empty/NoDirectory/NoLibrary", 1))
<< ErrnoMessage("setenv");
// The actual executable doesnt matter very much, because dyld wont
// ever launch it. It just needs to be an executable that uses dyld as
// its LC_LOAD_DYLINKER (all normal executables do). /usr/bin/true is on
// every system, so use it.
ASSERT_EQ(0, execl("/usr/bin/true", "true", nullptr))
<< ErrnoMessage("execl");
break;
}
default:
break;
}
}
TestType test_type_;
DISALLOW_COPY_AND_ASSIGN(TestMachOImageAnnotationsReader);
};
TEST(MachOImageAnnotationsReader, DontCrash) {
TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader(
TestMachOImageAnnotationsReader::kDontCrash);
test_mach_o_image_annotations_reader.Run();
}
TEST(MachOImageAnnotationsReader, CrashAbort) {
TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader(
TestMachOImageAnnotationsReader::kCrashAbort);
test_mach_o_image_annotations_reader.Run();
}
TEST(MachOImageAnnotationsReader, CrashDyld) {
TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader(
TestMachOImageAnnotationsReader::kCrashDyld);
test_mach_o_image_annotations_reader.Run();
}
} // namespace
} // namespace test
} // namespace crashpad

View File

@ -39,6 +39,8 @@ class ProcessReader;
//! This class is capable of reading both 32-bit (`mach_header`/`MH_MAGIC`) and
//! 64-bit (`mach_header_64`/`MH_MAGIC_64`) images based on the bitness of the
//! remote process.
//!
//! \sa MachOImageAnnotationsReader
class MachOImageReader {
public:
MachOImageReader();

View File

@ -400,13 +400,14 @@ void ProcessReader::InitializeModules() {
//
// TODO(mark): It may be possible to recover from these situations by looking
// through memory mappings for Mach-O images.
//
// Continue along when this situation is detected, because even without any
// images in infoArray, dyldImageLoadAddress may be set, and it may be
// possible to recover some information from dyld.
if (all_image_infos.infoArrayCount == 0) {
LOG(WARNING) << "all_image_infos.infoArrayCount is zero";
return;
}
if (!all_image_infos.infoArray) {
} else if (!all_image_infos.infoArray) {
LOG(WARNING) << "all_image_infos.infoArray is nullptr";
return;
}
std::vector<process_types::dyld_image_info> image_info_vector(

View File

@ -19,6 +19,7 @@
// snapshot/mac/process_types.cc to produce process type struct definitions and
// accessors.
#include "snapshot/mac/process_types/crashpad_info.proctype"
#include "snapshot/mac/process_types/crashreporterclient.proctype"
#include "snapshot/mac/process_types/dyld_images.proctype"
#include "snapshot/mac/process_types/loader.proctype"

View File

@ -0,0 +1,34 @@
// Copyright 2014 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The file corresponds to Crashpads client/crashpad_info.h.
//
// This file is intended to be included multiple times in the same translation
// unit, so #include guards are intentionally absent.
//
// This file is included by snapshot/mac/process_types.h and
// snapshot/mac/process_types.cc to produce process type struct definitions and
// accessors.
// Client Mach-O images will contain a __DATA,__crashpad_info section formatted
// according to this structure.
PROCESS_TYPE_STRUCT_BEGIN(CrashpadInfo)
PROCESS_TYPE_STRUCT_MEMBER(uint32_t, signature)
PROCESS_TYPE_STRUCT_MEMBER(uint32_t, size)
PROCESS_TYPE_STRUCT_MEMBER(uint32_t, version)
PROCESS_TYPE_STRUCT_MEMBER(uint32_t, padding_0)
// SimpleStringDictionary*
PROCESS_TYPE_STRUCT_MEMBER(Pointer, simple_annotations)
PROCESS_TYPE_STRUCT_END(CrashpadInfo)

View File

@ -18,6 +18,7 @@
'target_name': 'snapshot',
'type': 'static_library',
'dependencies': [
'../client/client.gyp:client',
'../compat/compat.gyp:compat',
'../third_party/mini_chromium/mini_chromium/base/base.gyp:base',
'../util/util.gyp:util',
@ -34,6 +35,8 @@
'mac/cpu_context_mac.h',
'mac/exception_snapshot_mac.cc',
'mac/exception_snapshot_mac.h',
'mac/mach_o_image_annotations_reader.cc',
'mac/mach_o_image_annotations_reader.h',
'mac/mach_o_image_reader.cc',
'mac/mach_o_image_reader.h',
'mac/mach_o_image_segment_reader.cc',
@ -47,6 +50,7 @@
'mac/process_types.cc',
'mac/process_types.h',
'mac/process_types/all.proctype',
'mac/process_types/crashpad_info.proctype',
'mac/process_types/crashreporterclient.proctype',
'mac/process_types/custom.cc',
'mac/process_types/dyld_images.proctype',
@ -71,6 +75,7 @@
'type': 'executable',
'dependencies': [
'snapshot',
'../client/client.gyp:client',
'../compat/compat.gyp:compat',
'../third_party/gtest/gtest.gyp:gtest',
'../third_party/gtest/gtest.gyp:gtest_main',
@ -83,6 +88,7 @@
],
'sources': [
'mac/cpu_context_mac_test.cc',
'mac/mach_o_image_annotations_reader_test.cc',
'mac/mach_o_image_reader_test.cc',
'mac/mach_o_image_segment_reader_test.cc',
'mac/process_reader_test.cc',