bug 379518 - sync up with breakpad svn, to revision 157. r=mento

This commit is contained in:
ted.mielczarek@gmail.com 2007-05-06 18:03:38 -07:00
parent 97956838c7
commit e0c30c9f78
34 changed files with 1552 additions and 343 deletions

View File

@ -47,11 +47,12 @@ XPI_NAME = crashreporter
LOCAL_INCLUDES = -I$(srcdir)/../../..
CPPSRCS = \
exception_handler.cc \
minidump_generator.cc \
../../minidump_file_writer.cc \
$(NULL)
CPPSRCS = \
exception_handler.cc \
minidump_generator.cc \
dynamic_images.cc \
../../minidump_file_writer.cc \
$(NULL)
# need static lib
FORCE_STATIC_LIB = 1

View File

@ -0,0 +1,239 @@
// Copyright (c) 2007, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <mach-o/nlist.h>
#include <stdlib.h>
#include <stdio.h>
#include <algorithm>
#include "client/mac/handler/dynamic_images.h"
namespace google_breakpad {
//==============================================================================
// Reads an address range from another task. A block of memory is malloced
// and should be freed by the caller.
void* ReadTaskMemory(task_port_t target_task,
const void* address,
size_t length) {
void* result = NULL;
mach_vm_address_t page_address = (uint32_t)address & (-4096);
mach_vm_address_t last_page_address =
((uint32_t)address + length + 4095) & (-4096);
mach_vm_size_t page_size = last_page_address - page_address;
uint8_t* local_start;
uint32_t local_length;
kern_return_t r = vm_read(target_task,
page_address,
page_size,
reinterpret_cast<vm_offset_t*>(&local_start),
&local_length);
if (r == KERN_SUCCESS) {
result = malloc(length);
if (result != NULL) {
memcpy(result, &local_start[(uint32_t)address - page_address], length);
}
vm_deallocate(mach_task_self(), (uintptr_t)local_start, local_length);
}
return result;
}
#pragma mark -
//==============================================================================
// Initializes vmaddr_, vmsize_, and slide_
void DynamicImage::CalculateMemoryInfo() {
mach_header *header = GetMachHeader();
const struct load_command *cmd =
reinterpret_cast<const struct load_command *>(header + 1);
for (unsigned int i = 0; cmd && (i < header->ncmds); ++i) {
if (cmd->cmd == LC_SEGMENT) {
const struct segment_command *seg =
reinterpret_cast<const struct segment_command *>(cmd);
if (!strcmp(seg->segname, "__TEXT")) {
vmaddr_ = seg->vmaddr;
vmsize_ = seg->vmsize;
slide_ = 0;
if (seg->fileoff == 0 && seg->filesize != 0) {
slide_ = (uintptr_t)GetLoadAddress() - (uintptr_t)seg->vmaddr;
}
return;
}
}
cmd = reinterpret_cast<const struct load_command *>
(reinterpret_cast<const char *>(cmd) + cmd->cmdsize);
}
// we failed - a call to IsValid() will return false
vmaddr_ = 0;
vmsize_ = 0;
slide_ = 0;
}
#pragma mark -
//==============================================================================
// Loads information about dynamically loaded code in the given task.
DynamicImages::DynamicImages(mach_port_t task)
: task_(task) {
ReadImageInfoForTask();
}
//==============================================================================
// This code was written using dyld_debug.c (from Darwin) as a guide.
void DynamicImages::ReadImageInfoForTask() {
struct nlist l[8];
memset(l, 0, sizeof(l) );
// First we lookup the address of the "_dyld_all_image_infos" struct
// which lives in "dyld". This structure contains information about all
// of the loaded dynamic images.
struct nlist &list = l[0];
list.n_un.n_name = "_dyld_all_image_infos";
nlist("/usr/lib/dyld", &list);
if (list.n_value) {
// Read the structure inside of dyld that contains information about
// loaded images. We're reading from the desired task's address space.
// Here we make the assumption that dyld loaded at the same address in
// the crashed process vs. this one. This is an assumption made in
// "dyld_debug.c" and is said to be nearly always valid.
dyld_all_image_infos *dyldInfo = reinterpret_cast<dyld_all_image_infos*>
(ReadTaskMemory(task_,
reinterpret_cast<void*>(list.n_value),
sizeof(dyld_all_image_infos)));
if (dyldInfo) {
// number of loaded images
int count = dyldInfo->infoArrayCount;
// Read an array of dyld_image_info structures each containing
// information about a loaded image.
dyld_image_info *infoArray = reinterpret_cast<dyld_image_info*>
(ReadTaskMemory(task_,
dyldInfo->infoArray,
count*sizeof(dyld_image_info)));
image_list_.reserve(count);
for (int i = 0; i < count; ++i) {
dyld_image_info &info = infoArray[i];
// First read just the mach_header from the image in the task.
mach_header *header = reinterpret_cast<mach_header*>
(ReadTaskMemory(task_, info.load_address_, sizeof(mach_header)));
if (!header)
break; // bail on this dynamic image
// Now determine the total amount we really want to read based on the
// size of the load commands. We need the header plus all of the
// load commands.
unsigned int header_size = sizeof(mach_header) + header->sizeofcmds;
free(header);
header = reinterpret_cast<mach_header*>
(ReadTaskMemory(task_, info.load_address_, header_size));
// Read the file name from the task's memory space.
char *file_path = NULL;
if (info.file_path_) {
// Although we're reading 0x2000 bytes, this is copied in the
// the DynamicImage constructor below with the correct string length,
// so it's not really wasting memory.
file_path = reinterpret_cast<char*>
(ReadTaskMemory(task_,
info.file_path_,
0x2000));
}
// Create an object representing this image and add it to our list.
DynamicImage *new_image = new DynamicImage(header,
header_size,
info.load_address_,
file_path,
info.file_mod_date_,
task_);
if (new_image->IsValid()) {
image_list_.push_back(DynamicImageRef(new_image));
} else {
delete new_image;
}
if (file_path) {
free(file_path);
}
}
free(dyldInfo);
free(infoArray);
// sorts based on loading address
sort(image_list_.begin(), image_list_.end() );
}
}
}
//==============================================================================
DynamicImage *DynamicImages::GetExecutableImage() {
int executable_index = GetExecutableImageIndex();
if (executable_index >= 0) {
return GetImage(executable_index);
}
return NULL;
}
//==============================================================================
// returns -1 if failure to find executable
int DynamicImages::GetExecutableImageIndex() {
int image_count = GetImageCount();
for (int i = 0; i < image_count; ++i) {
DynamicImage *image = GetImage(i);
if (image->GetMachHeader()->filetype == MH_EXECUTE) {
return i;
}
}
return -1;
}
} // namespace google_breakpad

View File

@ -0,0 +1,303 @@
// Copyright (c) 2007, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// dynamic_images.h
//
// Implements most of the function of the dyld API, but allowing an
// arbitrary task to be introspected, unlike the dyld API which
// only allows operation on the current task. The current implementation
// is limited to use by 32-bit tasks.
#ifndef CLIENT_MAC_HANDLER_DYNAMIC_IMAGES_H__
#define CLIENT_MAC_HANDLER_DYNAMIC_IMAGES_H__
#include <mach/mach.h>
#include <mach-o/dyld.h>
#include <mach-o/loader.h>
#include <sys/types.h>
#include <vector>
namespace google_breakpad {
using std::vector;
//==============================================================================
// The memory layout of this struct matches the dyld_image_info struct
// defined in "dyld_gdb.h" in the darwin source.
typedef struct dyld_image_info {
struct mach_header *load_address_;
char *file_path_;
uintptr_t file_mod_date_;
} dyld_image_info;
//==============================================================================
// This is as defined in "dyld_gdb.h" in the darwin source.
// _dyld_all_image_infos (in dyld) is a structure of this type
// which will be used to determine which dynamic code has been loaded.
typedef struct dyld_all_image_infos {
uint32_t version; // == 1 in Mac OS X 10.4
uint32_t infoArrayCount;
const struct dyld_image_info *infoArray;
void* notification;
bool processDetachedFromSharedRegion;
} dyld_all_image_infos;
//==============================================================================
// A simple wrapper for a mach_header
//
// This could be fleshed out with some more interesting methods.
class MachHeader {
public:
explicit MachHeader(const mach_header &header) : header_(header) {}
void Print() {
printf("magic\t\t: %4x\n", header_.magic);
printf("cputype\t\t: %d\n", header_.cputype);
printf("cpusubtype\t: %d\n", header_.cpusubtype);
printf("filetype\t: %d\n", header_.filetype);
printf("ncmds\t\t: %d\n", header_.ncmds);
printf("sizeofcmds\t: %d\n", header_.sizeofcmds);
printf("flags\t\t: %d\n", header_.flags);
}
mach_header header_;
};
//==============================================================================
// Represents a single dynamically loaded mach-o image
class DynamicImage {
public:
DynamicImage(mach_header *header, // we take ownership
int header_size, // includes load commands
mach_header *load_address,
char *inFilePath,
uintptr_t image_mod_date,
mach_port_t task)
: header_(header),
header_size_(header_size),
load_address_(load_address),
file_mod_date_(image_mod_date),
task_(task) {
InitializeFilePath(inFilePath);
CalculateMemoryInfo();
}
~DynamicImage() {
if (file_path_) {
free(file_path_);
}
free(header_);
}
// Returns pointer to a local copy of the mach_header plus load commands
mach_header *GetMachHeader() {return header_;}
// Size of mach_header plus load commands
int GetHeaderSize() const {return header_size_;}
// Full path to mach-o binary
char *GetFilePath() {return file_path_;}
uintptr_t GetModDate() const {return file_mod_date_;}
// Actual address where the image was loaded
mach_header *GetLoadAddress() const {return load_address_;}
// Address where the image should be loaded
uint32_t GetVMAddr() const {return vmaddr_;}
// Difference between GetLoadAddress() and GetVMAddr()
ptrdiff_t GetVMAddrSlide() const {return slide_;}
// Size of the image
uint32_t GetVMSize() const {return vmsize_;}
// Task owning this loaded image
mach_port_t GetTask() {return task_;}
// For sorting
bool operator<(const DynamicImage &inInfo) {
return GetLoadAddress() < inInfo.GetLoadAddress();
}
// Debugging
void Print() {
char *path = GetFilePath();
if (!path) {
path = "(unknown)";
}
printf("%p: %s\n", GetLoadAddress(), path);
mach_header *header = GetMachHeader();
MachHeader(*header).Print();
printf("vmaddr\t\t: %p\n", reinterpret_cast<void*>(GetVMAddr()));
printf("vmsize\t\t: %d\n", GetVMSize());
printf("slide\t\t: %d\n", GetVMAddrSlide());
}
private:
friend class DynamicImages;
// Sanity checking
bool IsValid() {return GetVMAddr() != 0;}
// Makes local copy of file path to mach-o binary
void InitializeFilePath(char *inFilePath) {
if (inFilePath) {
file_path_ = reinterpret_cast<char*>(malloc(strlen(inFilePath)));
strcpy(file_path_, inFilePath);
} else {
file_path_ = NULL;
}
}
// Initializes vmaddr_, vmsize_, and slide_
void CalculateMemoryInfo();
#if 0 // currently not needed
// Copy constructor: we don't want this to be invoked,
// but here's the code in case we need to make it public some day.
DynamicImage(DynamicImage &inInfo)
: load_address_(inInfo.load_address_),
vmaddr_(inInfo.vmaddr_),
vmsize_(inInfo.vmsize_),
slide_(inInfo.slide_),
file_mod_date_(inInfo.file_mod_date_),
task_(inInfo.task_) {
// copy file path string
InitializeFilePath(inInfo.GetFilePath());
// copy mach_header and load commands
header_ = reinterpret_cast<mach_header*>(malloc(inInfo.header_size_));
memcpy(header_, inInfo.header_, inInfo.header_size_);
header_size_ = inInfo.header_size_;
}
#endif
mach_header *header_; // our local copy of the header
int header_size_; // mach_header plus load commands
mach_header *load_address_; // base address image is mapped into
uint32_t vmaddr_;
uint32_t vmsize_;
ptrdiff_t slide_;
char *file_path_; // path dyld used to load the image
uintptr_t file_mod_date_; // time_t of image file
mach_port_t task_;
};
//==============================================================================
// DynamicImageRef is just a simple wrapper for a pointer to
// DynamicImage. The reason we use it instead of a simple typedef is so
// that we can use stl::sort() on a vector of DynamicImageRefs
// and simple class pointers can't implement operator<().
//
class DynamicImageRef {
public:
explicit DynamicImageRef(DynamicImage *inP) : p(inP) {}
DynamicImageRef(const DynamicImageRef &inRef) : p(inRef.p) {} // STL required
bool operator<(const DynamicImageRef &inRef) const {
return (*const_cast<DynamicImageRef*>(this)->p)
< (*const_cast<DynamicImageRef&>(inRef).p);
}
// Be just like DynamicImage*
DynamicImage *operator->() {return p;}
operator DynamicImage*() {return p;}
private:
DynamicImage *p;
};
//==============================================================================
// An object of type DynamicImages may be created to allow introspection of
// an arbitrary task's dynamically loaded mach-o binaries. This makes the
// assumption that the current task has send rights to the target task.
class DynamicImages {
public:
explicit DynamicImages(mach_port_t task);
~DynamicImages() {
for (int i = 0; i < (int)image_list_.size(); ++i) {
delete image_list_[i];
}
}
// Returns the number of dynamically loaded mach-o images.
int GetImageCount() const {return image_list_.size();}
// Returns an individual image.
DynamicImage *GetImage(int i) {
if (i < (int)image_list_.size()) {
return image_list_[i];
}
return NULL;
}
// Returns the image corresponding to the main executable.
DynamicImage *GetExecutableImage();
int GetExecutableImageIndex();
// Returns the task which we're looking at.
mach_port_t GetTask() const {return task_;}
// Debugging
void Print() {
for (int i = 0; i < (int)image_list_.size(); ++i) {
image_list_[i]->Print();
}
}
void TestPrint() {
for (int i = 0; i < (int)image_list_.size(); ++i) {
printf("dyld: %p: name = %s\n", _dyld_get_image_header(i),
_dyld_get_image_name(i) );
const mach_header *header = _dyld_get_image_header(i);
MachHeader(*header).Print();
}
}
private:
bool IsOurTask() {return task_ == mach_task_self();}
// Initialization
void ReadImageInfoForTask();
mach_port_t task_;
vector<DynamicImageRef> image_list_;
};
// Returns a malloced block containing the contents of memory at a particular
// location in another task.
void* ReadTaskMemory(task_port_t target_task, const void* address, size_t len);
} // namespace google_breakpad
#endif // CLIENT_MAC_HANDLER_DYNAMIC_IMAGES_H__

View File

@ -32,6 +32,7 @@
#include "client/mac/handler/exception_handler.h"
#include "client/mac/handler/minidump_generator.h"
#include "common/mac/macho_utilities.h"
namespace google_breakpad {
@ -133,6 +134,7 @@ ExceptionHandler::ExceptionHandler(const string &dump_path,
filter_(filter),
callback_(callback),
callback_context_(callback_context),
directCallback_(NULL),
handler_thread_(NULL),
handler_port_(0),
previous_(NULL),
@ -146,6 +148,27 @@ ExceptionHandler::ExceptionHandler(const string &dump_path,
Setup(install_handler);
}
// special constructor if we want to bypass minidump writing and
// simply get a callback with the exception information
ExceptionHandler::ExceptionHandler(DirectCallback callback,
void *callback_context,
bool install_handler)
: dump_path_(),
filter_(NULL),
callback_(NULL),
callback_context_(callback_context),
directCallback_(callback),
handler_thread_(NULL),
handler_port_(0),
previous_(NULL),
installed_exception_handler_(false),
is_in_teardown_(false),
last_minidump_write_result_(false),
use_minidump_write_mutex_(false) {
MinidumpGenerator::GatherSystemInformation();
Setup(install_handler);
}
ExceptionHandler::~ExceptionHandler() {
Teardown();
}
@ -186,36 +209,47 @@ bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
int exception_code,
mach_port_t thread_name) {
bool result = false;
string minidump_id;
// Putting the MinidumpGenerator in its own context will ensure that the
// destructor is executed, closing the newly created minidump file.
if (!dump_path_.empty()) {
MinidumpGenerator md;
if (exception_type && exception_code) {
// If this is a real exception, give the filter (if any) a chance to
// decided if this should be sent
if (filter_ && !filter_(callback_context_))
return false;
md.SetExceptionInformation(exception_type, exception_code, thread_name);
}
result = md.Write(next_minidump_path_c_);
}
// Call user specified callback (if any)
if (callback_) {
// If the user callback returned true and we're handling an exception
// (rather than just writing out the file), then we should exit without
// forwarding the exception to the next handler.
if (callback_(dump_path_c_, next_minidump_id_c_, callback_context_,
result)) {
if (directCallback_) {
if (directCallback_(callback_context_,
exception_type,
exception_code,
thread_name) ) {
if (exception_type && exception_code)
exit(exception_type);
}
}
} else {
string minidump_id;
// Putting the MinidumpGenerator in its own context will ensure that the
// destructor is executed, closing the newly created minidump file.
if (!dump_path_.empty()) {
MinidumpGenerator md;
if (exception_type && exception_code) {
// If this is a real exception, give the filter (if any) a chance to
// decided if this should be sent
if (filter_ && !filter_(callback_context_))
return false;
md.SetExceptionInformation(exception_type, exception_code, thread_name);
}
result = md.Write(next_minidump_path_c_);
}
// Call user specified callback (if any)
if (callback_) {
// If the user callback returned true and we're handling an exception
// (rather than just writing out the file), then we should exit without
// forwarding the exception to the next handler.
if (callback_(dump_path_c_, next_minidump_id_c_, callback_context_,
result)) {
if (exception_type && exception_code)
exit(exception_type);
}
}
}
return result;
}
@ -257,7 +291,7 @@ kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread,
thread_state_flavor_t target_flavor = current.flavors[found];
mach_msg_type_number_t thread_state_count = THREAD_STATE_MAX;
thread_state_data_t thread_state;
breakpad_thread_state_data_t thread_state;
switch (target_behavior) {
case EXCEPTION_DEFAULT:
result = exception_raise(target_port, failed_thread, task, exception,
@ -326,14 +360,11 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
MACH_RCV_MSG | MACH_RCV_LARGE, 0,
sizeof(receive), self->handler_port_,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
if (result == KERN_SUCCESS) {
// Uninstall our handler so that we don't get in a loop if the process of
// writing out a minidump causes an exception. However, if the exception
// was caused by a fork'd process, don't uninstall things
if (receive.task.name == mach_task_self())
self->UninstallHandler();
// If the actual exception code is zero, then we're calling this handler
// in a way that indicates that we want to either exit this thread or
// generate a minidump
@ -342,6 +373,8 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
// to avoid misleading stacks. If appropriate they will be resumed
// afterwards.
if (!receive.exception) {
self->UninstallHandler(false);
if (self->is_in_teardown_)
return NULL;
@ -356,6 +389,8 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
if (self->use_minidump_write_mutex_)
pthread_mutex_unlock(&self->minidump_write_mutex_);
} else {
self->UninstallHandler(true);
// When forking a child process with the exception handler installed,
// if the child crashes, it will send the exception back to the parent
// process. The check for task == self_task() ensures that only
@ -419,7 +454,7 @@ bool ExceptionHandler::InstallHandler() {
return installed_exception_handler_;
}
bool ExceptionHandler::UninstallHandler() {
bool ExceptionHandler::UninstallHandler(bool in_exception) {
kern_return_t result = KERN_SUCCESS;
if (installed_exception_handler_) {
@ -435,7 +470,11 @@ bool ExceptionHandler::UninstallHandler() {
return false;
}
delete previous_;
// this delete should NOT happen if an exception just occurred!
if (!in_exception) {
delete previous_;
}
previous_ = NULL;
installed_exception_handler_ = false;
}
@ -479,7 +518,7 @@ bool ExceptionHandler::Teardown() {
kern_return_t result = KERN_SUCCESS;
is_in_teardown_ = true;
if (!UninstallHandler())
if (!UninstallHandler(false))
return false;
// Send an empty message so that the handler_thread exits

View File

@ -71,6 +71,14 @@ class ExceptionHandler {
const char *minidump_id,
void *context, bool succeeded);
// A callback function which will be called directly if an exception occurs.
// This bypasses the minidump file writing and simply gives the client
// the exception information.
typedef bool (*DirectCallback)( void *context,
int exception_type,
int exception_code,
mach_port_t thread_name);
// Creates a new ExceptionHandler instance to handle writing minidumps.
// Minidump files will be written to dump_path, and the optional callback
// is called after writing the dump file, as described above.
@ -80,6 +88,13 @@ class ExceptionHandler {
ExceptionHandler(const string &dump_path,
FilterCallback filter, MinidumpCallback callback,
void *callback_context, bool install_handler);
// A special constructor if we want to bypass minidump writing and
// simply get a callback with the exception information.
ExceptionHandler(DirectCallback callback,
void *callback_context,
bool install_handler);
~ExceptionHandler();
// Get and set the minidump path.
@ -104,7 +119,7 @@ class ExceptionHandler {
bool InstallHandler();
// Uninstall the mach exception handler (if any)
bool UninstallHandler();
bool UninstallHandler(bool in_exception);
// Setup the handler thread, and if |install_handler| is true, install the
// mach exception port handler
@ -159,6 +174,10 @@ class ExceptionHandler {
MinidumpCallback callback_;
void *callback_context_;
// The callback function to be passed back when we don't want a minidump
// file to be written
DirectCallback directCallback_;
// The thread that is created for the handler
pthread_t handler_thread_;

View File

@ -34,6 +34,7 @@
#include <mach-o/dyld.h>
#include <mach-o/loader.h>
#include <sys/sysctl.h>
#include <sys/resource.h>
#include <CoreFoundation/CoreFoundation.h>
@ -50,7 +51,21 @@ namespace google_breakpad {
MinidumpGenerator::MinidumpGenerator()
: exception_type_(0),
exception_code_(0),
exception_thread_(0) {
exception_thread_(0),
crashing_task_(mach_task_self()),
handler_thread_(mach_thread_self()) {
dynamic_images_ = new DynamicImages(mach_task_self());
GatherSystemInformation();
}
MinidumpGenerator::MinidumpGenerator(mach_port_t crashing_task, mach_port_t handler_thread)
: exception_type_(0),
exception_code_(0),
exception_thread_(0),
crashing_task_(crashing_task),
handler_thread_(handler_thread) {
dynamic_images_ = new DynamicImages(crashing_task_);
GatherSystemInformation();
}
MinidumpGenerator::~MinidumpGenerator() {
@ -184,14 +199,14 @@ bool MinidumpGenerator::Write(const char *path) {
return result;
}
static size_t CalculateStackSize(vm_address_t start_addr) {
size_t MinidumpGenerator::CalculateStackSize(vm_address_t start_addr) {
vm_address_t stack_region_base = start_addr;
vm_size_t stack_region_size;
natural_t nesting_level = 0;
vm_region_submap_info submap_info;
mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT;
kern_return_t result =
vm_region_recurse(mach_task_self(), &stack_region_base, &stack_region_size,
vm_region_recurse(crashing_task_, &stack_region_base, &stack_region_size,
&nesting_level,
reinterpret_cast<vm_region_recurse_info_t>(&submap_info),
&info_count);
@ -225,7 +240,13 @@ bool MinidumpGenerator::WriteStackFromStartAddress(
if (!memory.Allocate(size))
return false;
bool result = memory.Copy(reinterpret_cast<const void *>(start_addr), size);
void *stack_memory = ReadTaskMemory(crashing_task_, (void*)start_addr, size);
bool result = memory.Copy(stack_memory, size);
free(stack_memory);
stack_location->start_of_memory_range = start_addr;
stack_location->memory = memory.location();
@ -233,7 +254,7 @@ bool MinidumpGenerator::WriteStackFromStartAddress(
}
#if TARGET_CPU_PPC
bool MinidumpGenerator::WriteStack(thread_state_data_t state,
bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location) {
ppc_thread_state_t *machine_state =
reinterpret_cast<ppc_thread_state_t *>(state);
@ -241,14 +262,14 @@ bool MinidumpGenerator::WriteStack(thread_state_data_t state,
return WriteStackFromStartAddress(start_addr, stack_location);
}
u_int64_t MinidumpGenerator::CurrentPCForStack(thread_state_data_t state) {
u_int64_t MinidumpGenerator::CurrentPCForStack(breakpad_thread_state_data_t state) {
ppc_thread_state_t *machine_state =
reinterpret_cast<ppc_thread_state_t *>(state);
return machine_state->srr0;
}
bool MinidumpGenerator::WriteContext(thread_state_data_t state,
bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location) {
TypedMDRVA<MDRawContextPPC> context(&writer_);
ppc_thread_state_t *machine_state =
@ -307,7 +328,7 @@ bool MinidumpGenerator::WriteContext(thread_state_data_t state,
}
#elif TARGET_CPU_X86
bool MinidumpGenerator::WriteStack(thread_state_data_t state,
bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location) {
x86_thread_state_t *machine_state =
reinterpret_cast<x86_thread_state_t *>(state);
@ -315,14 +336,14 @@ bool MinidumpGenerator::WriteStack(thread_state_data_t state,
return WriteStackFromStartAddress(start_addr, stack_location);
}
u_int64_t MinidumpGenerator::CurrentPCForStack(thread_state_data_t state) {
u_int64_t MinidumpGenerator::CurrentPCForStack(breakpad_thread_state_data_t state) {
x86_thread_state_t *machine_state =
reinterpret_cast<x86_thread_state_t *>(state);
return machine_state->uts.ts32.eip;
}
bool MinidumpGenerator::WriteContext(thread_state_data_t state,
bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location) {
TypedMDRVA<MDRawContextX86> context(&writer_);
x86_thread_state_t *machine_state =
@ -358,7 +379,7 @@ bool MinidumpGenerator::WriteContext(thread_state_data_t state,
bool MinidumpGenerator::WriteThreadStream(mach_port_t thread_id,
MDRawThread *thread) {
thread_state_data_t state;
breakpad_thread_state_data_t state;
mach_msg_type_number_t state_count = sizeof(state);
if (thread_get_state(thread_id, MACHINE_THREAD_STATE, state, &state_count) ==
@ -384,7 +405,7 @@ bool MinidumpGenerator::WriteThreadListStream(
mach_msg_type_number_t thread_count;
int non_generator_thread_count;
if (task_threads(mach_task_self(), &threads_for_task, &thread_count))
if (task_threads(crashing_task_, &threads_for_task, &thread_count))
return false;
// Don't include the generator thread
@ -404,7 +425,7 @@ bool MinidumpGenerator::WriteThreadListStream(
for (unsigned int i = 0; i < thread_count; ++i) {
memset(&thread, 0, sizeof(MDRawThread));
if (threads_for_task[i] != mach_thread_self()) {
if (threads_for_task[i] != handler_thread_) {
if (!WriteThreadStream(threads_for_task[i], &thread))
return false;
@ -431,7 +452,7 @@ bool MinidumpGenerator::WriteExceptionStream(MDRawDirectory *exception_stream) {
exception_ptr->exception_record.exception_code = exception_type_;
exception_ptr->exception_record.exception_flags = exception_code_;
thread_state_data_t state;
breakpad_thread_state_data_t state;
mach_msg_type_number_t stateCount = sizeof(state);
if (thread_get_state(exception_thread_, MACHINE_THREAD_STATE, state,
@ -496,57 +517,45 @@ bool MinidumpGenerator::WriteSystemInfoStream(
bool MinidumpGenerator::WriteModuleStream(unsigned int index,
MDRawModule *module) {
const struct mach_header *header = _dyld_get_image_header(index);
DynamicImage *image = dynamic_images_->GetImage(index);
if (!image)
return false;
const mach_header *header = image->GetMachHeader();
if (!header)
return false;
int cpu_type = header->cputype;
unsigned long slide = _dyld_get_image_vmaddr_slide(index);
const char* name = _dyld_get_image_name(index);
const struct load_command *cmd =
reinterpret_cast<const struct load_command *>(header + 1);
memset(module, 0, sizeof(MDRawModule));
for (unsigned int i = 0; cmd && (i < header->ncmds); i++) {
if (cmd->cmd == LC_SEGMENT) {
const struct segment_command *seg =
reinterpret_cast<const struct segment_command *>(cmd);
if (!strcmp(seg->segname, "__TEXT")) {
MDLocationDescriptor string_location;
MDLocationDescriptor string_location;
if (!writer_.WriteString(name, 0, &string_location))
return false;
const char* name = image->GetFilePath();
if (!writer_.WriteString(name, 0, &string_location))
return false;
module->base_of_image = seg->vmaddr + slide;
module->size_of_image = seg->vmsize;
module->module_name_rva = string_location.rva;
module->base_of_image = image->GetVMAddr() + image->GetVMAddrSlide();
module->size_of_image = image->GetVMSize();
module->module_name_rva = string_location.rva;
if (!WriteCVRecord(module, cpu_type, name))
return false;
return true;
}
}
cmd = reinterpret_cast<struct load_command *>((char *)cmd + cmd->cmdsize);
if (!WriteCVRecord(module, cpu_type, name)) {
return false;
}
return true;
}
static int FindExecutableModule() {
int image_count = _dyld_image_count();
const struct mach_header *header;
int MinidumpGenerator::FindExecutableModule() {
int index = dynamic_images_->GetExecutableImageIndex();
for (int i = 0; i < image_count; ++i) {
header = _dyld_get_image_header(i);
if (header->filetype == MH_EXECUTE)
return i;
if (index >= 0) {
return index;
}
// failed - just use the first image
return 0;
}
@ -606,7 +615,7 @@ bool MinidumpGenerator::WriteModuleListStream(
if (!_dyld_present())
return false;
int image_count = _dyld_image_count();
int image_count = dynamic_images_->GetImageCount();
if (!list.AllocateObjectAndArray(image_count, MD_MODULE_SIZE))
return false;
@ -619,16 +628,18 @@ bool MinidumpGenerator::WriteModuleListStream(
MDRawModule module;
int executableIndex = FindExecutableModule();
if (!WriteModuleStream(executableIndex, &module))
if (!WriteModuleStream(executableIndex, &module)) {
return false;
}
list.CopyIndexAfterObject(0, &module, MD_MODULE_SIZE);
int destinationIndex = 1; // Write all other modules after this one
for (int i = 0; i < image_count; ++i) {
if (i != executableIndex) {
if (!WriteModuleStream(i, &module))
if (!WriteModuleStream(i, &module)) {
return false;
}
list.CopyIndexAfterObject(destinationIndex++, &module, MD_MODULE_SIZE);
}
@ -701,11 +712,11 @@ bool MinidumpGenerator::WriteBreakpadInfoStream(
if (exception_thread_ && exception_type_) {
info_ptr->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID |
MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID;
info_ptr->dump_thread_id = mach_thread_self();
info_ptr->dump_thread_id = handler_thread_;
info_ptr->requesting_thread_id = exception_thread_;
} else {
info_ptr->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID;
info_ptr->dump_thread_id = mach_thread_self();
info_ptr->dump_thread_id = handler_thread_;
info_ptr->requesting_thread_id = 0;
}

View File

@ -38,6 +38,9 @@
#include "client/minidump_file_writer.h"
#include "google_breakpad/common/minidump_format.h"
#include "common/mac/macho_utilities.h"
#include "dynamic_images.h"
namespace google_breakpad {
@ -53,6 +56,8 @@ using std::string;
class MinidumpGenerator {
public:
MinidumpGenerator();
MinidumpGenerator(mach_port_t crashing_task, mach_port_t handler_thread);
~MinidumpGenerator();
// Return <dir>/<unique_name>.dmp
@ -87,17 +92,19 @@ class MinidumpGenerator {
bool WriteBreakpadInfoStream(MDRawDirectory *breakpad_info_stream);
// Helpers
u_int64_t CurrentPCForStack(thread_state_data_t state);
u_int64_t CurrentPCForStack(breakpad_thread_state_data_t state);
bool WriteStackFromStartAddress(vm_address_t start_addr,
MDMemoryDescriptor *stack_location);
bool WriteStack(thread_state_data_t state,
bool WriteStack(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location);
bool WriteContext(thread_state_data_t state,
bool WriteContext(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location);
bool WriteThreadStream(mach_port_t thread_id, MDRawThread *thread);
bool WriteCVRecord(MDRawModule *module, int cpu_type,
const char *module_path);
bool WriteModuleStream(unsigned int index, MDRawModule *module);
size_t CalculateStackSize(vm_address_t start_addr);
int FindExecutableModule();
// disallow copy ctor and operator=
explicit MinidumpGenerator(const MinidumpGenerator &);
@ -110,12 +117,17 @@ class MinidumpGenerator {
int exception_type_;
int exception_code_;
mach_port_t exception_thread_;
mach_port_t crashing_task_;
mach_port_t handler_thread_;
// System information
static char build_string_[16];
static int os_major_version_;
static int os_minor_version_;
static int os_build_number_;
// Information about dynamically loaded code
DynamicImages *dynamic_images_;
};
} // namespace google_breakpad

View File

@ -477,7 +477,10 @@ void ExceptionHandler::UpdateNextID() {
wchar_t minidump_path[MAX_PATH];
swprintf(minidump_path, MAX_PATH, L"%s\\%s.dmp",
dump_path_c_, next_minidump_id_c_);
GB_WSU_SAFE_SWPRINTF_TERMINATE(minidump_path, MAX_PATH);
// remove when VC++7.1 is no longer supported
minidump_path[MAX_PATH - 1] = L'\0';
next_minidump_path_ = minidump_path;
next_minidump_path_c_ = next_minidump_path_.c_str();
}

View File

@ -493,7 +493,7 @@ bool WriteModuleInfo(int fd, ElfW(Half) arch, const std::string &obj_file) {
size_t slash_pos = obj_file.find_last_of("/");
if (slash_pos != std::string::npos)
filename = obj_file.substr(slash_pos + 1);
return WriteFormat(fd, "MODULE Linux %s %s 1 %s\n", arch_name,
return WriteFormat(fd, "MODULE Linux %s %s %s\n", arch_name,
id_no_dash, filename.c_str());
}
return false;
@ -616,7 +616,7 @@ class MmapWrapper {
namespace google_breakpad {
bool DumpSymbols::WriteSymbolFile(const std::string &obj_file,
const std::string &symbol_file) {
int sym_fd) {
int obj_fd = open(obj_file.c_str(), O_RDONLY);
if (obj_fd < 0)
return false;
@ -636,16 +636,10 @@ bool DumpSymbols::WriteSymbolFile(const std::string &obj_file,
if (!LoadSymbols(elf_header, &symbols))
return false;
// Write to symbol file.
int sym_fd = open(symbol_file.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666);
if (sym_fd < 0)
return false;
FDWrapper sym_fd_wrapper(sym_fd);
if (WriteModuleInfo(sym_fd, elf_header->e_machine, obj_file) &&
DumpStabSymbols(sym_fd, symbols))
return true;
// Remove the symbol file if failed to write the symbols.
unlink(symbol_file.c_str());
return false;
}

View File

@ -40,7 +40,7 @@ namespace google_breakpad {
class DumpSymbols {
public:
bool WriteSymbolFile(const std::string &obj_file,
const std::string &symbol_file);
int sym_fd);
};
} // namespace google_breakpad

View File

@ -38,11 +38,11 @@
#include <fcntl.h>
#include <link.h>
#include <sys/mman.h>
#include <openssl/md5.h>
#include <string.h>
#include <unistd.h>
#include "common/linux/file_id.h"
#include "common/linux/md5.h"
namespace google_breakpad {
@ -109,10 +109,12 @@ bool FileID::ElfFileIdentifier(unsigned char identifier[16]) {
const void *text_section = NULL;
int text_size = 0;
if (FindElfTextSection(base, &text_section, &text_size) && (text_size > 0)) {
MD5_CTX md5;
MD5_Init(&md5);
MD5_Update(&md5, text_section, text_size);
MD5_Final(identifier, &md5);
struct MD5Context md5;
MD5Init(&md5);
MD5Update(&md5,
static_cast<const unsigned char*>(text_section),
text_size);
MD5Final(identifier, &md5);
success = true;
}

View File

@ -0,0 +1,246 @@
/*
* written by Colin Plumb in 1993, no copyright is claimed.
* This code is in the public domain; do with it what you wish.
*
* Equivalent code is available from RSA Data Security, Inc.
* This code has been tested against that, and is equivalent,
* except that you don't need to include two pages of legalese
* with every copy.
*
* To compute the message digest of a chunk of bytes, declare an
* MD5Context structure, pass it to MD5Init, call MD5Update as
* needed on buffers full of bytes, and then call MD5Final, which
* will fill a supplied 16-byte array with the digest.
*/
#include <string.h>
#include "common/linux/md5.h"
#ifndef WORDS_BIGENDIAN
#define byteReverse(buf, len) /* Nothing */
#else
/*
* Note: this code is harmless on little-endian machines.
*/
static void byteReverse(unsigned char *buf, unsigned longs)
{
u32 t;
do {
t = (u32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
((unsigned) buf[1] << 8 | buf[0]);
*(u32 *) buf = t;
buf += 4;
} while (--longs);
}
#endif
static void MD5Transform(u32 buf[4], u32 const in[16]);
/*
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
* initialization constants.
*/
void MD5Init(struct MD5Context *ctx)
{
ctx->buf[0] = 0x67452301;
ctx->buf[1] = 0xefcdab89;
ctx->buf[2] = 0x98badcfe;
ctx->buf[3] = 0x10325476;
ctx->bits[0] = 0;
ctx->bits[1] = 0;
}
/*
* Update context to reflect the concatenation of another buffer full
* of bytes.
*/
void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
{
u32 t;
/* Update bitcount */
t = ctx->bits[0];
if ((ctx->bits[0] = t + ((u32) len << 3)) < t)
ctx->bits[1]++; /* Carry from low to high */
ctx->bits[1] += len >> 29;
t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
/* Handle any leading odd-sized chunks */
if (t) {
unsigned char *p = (unsigned char *) ctx->in + t;
t = 64 - t;
if (len < t) {
memcpy(p, buf, len);
return;
}
memcpy(p, buf, t);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (u32 *) ctx->in);
buf += t;
len -= t;
}
/* Process data in 64-byte chunks */
while (len >= 64) {
memcpy(ctx->in, buf, 64);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (u32 *) ctx->in);
buf += 64;
len -= 64;
}
/* Handle any remaining bytes of data. */
memcpy(ctx->in, buf, len);
}
/*
* Final wrapup - pad to 64-byte boundary with the bit pattern
* 1 0* (64-bit count of bits processed, MSB-first)
*/
void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
{
unsigned count;
unsigned char *p;
/* Compute number of bytes mod 64 */
count = (ctx->bits[0] >> 3) & 0x3F;
/* Set the first char of padding to 0x80. This is safe since there is
always at least one byte free */
p = ctx->in + count;
*p++ = 0x80;
/* Bytes of padding needed to make 64 bytes */
count = 64 - 1 - count;
/* Pad out to 56 mod 64 */
if (count < 8) {
/* Two lots of padding: Pad the first block to 64 bytes */
memset(p, 0, count);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (u32 *) ctx->in);
/* Now fill the next block with 56 bytes */
memset(ctx->in, 0, 56);
} else {
/* Pad block to 56 bytes */
memset(p, 0, count - 8);
}
byteReverse(ctx->in, 14);
/* Append length in bits and transform */
((u32 *) ctx->in)[14] = ctx->bits[0];
((u32 *) ctx->in)[15] = ctx->bits[1];
MD5Transform(ctx->buf, (u32 *) ctx->in);
byteReverse((unsigned char *) ctx->buf, 4);
memcpy(digest, ctx->buf, 16);
memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
}
/* The four core functions - F1 is optimized somewhat */
/* #define F1(x, y, z) (x & y | ~x & z) */
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))
/* This is the central step in the MD5 algorithm. */
#define MD5STEP(f, w, x, y, z, data, s) \
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
/*
* The core of the MD5 algorithm, this alters an existing MD5 hash to
* reflect the addition of 16 longwords of new data. MD5Update blocks
* the data and converts bytes into longwords for this routine.
*/
static void MD5Transform(u32 buf[4], u32 const in[16])
{
register u32 a, b, c, d;
a = buf[0];
b = buf[1];
c = buf[2];
d = buf[3];
MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
buf[0] += a;
buf[1] += b;
buf[2] += c;
buf[3] += d;
}

View File

@ -0,0 +1,31 @@
// Copyright 2007 Google Inc. All Rights Reserved.
// Author: liuli@google.com (Liu Li)
#ifndef COMMON_LINUX_MD5_H__
#define COMMON_LINUX_MD5_H__
#include <stdint.h>
typedef uint32_t u32;
typedef uint8_t u8;
struct MD5Context {
u32 buf[4];
u32 bits[2];
u8 in[64];
};
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
void MD5Init(struct MD5Context *ctx);
void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len);
void MD5Final(unsigned char digest[16], struct MD5Context *ctx);
#ifdef __cplusplus
}
#endif
#endif // COMMON_LINUX_MD5_H__

View File

@ -55,6 +55,7 @@ CPPSRCS = \
macho_id.cc \
macho_walker.cc \
string_utilities.cc \
macho_utilities.cc \
$(NULL)
CMSRCS = \

View File

@ -39,11 +39,12 @@
NSString *sourcePath_; // Source of symbols (STRONG)
NSString *architecture_; // Architecture to extract (STRONG)
NSMutableDictionary *addresses_; // Addresses and symbols (STRONG)
NSMutableSet *functionAddresses_; // Function addresses (STRONG)
NSMutableDictionary *sources_; // Address and Source file paths (STRONG)
NSMutableArray *cppAddresses_; // Addresses of C++ symbols (STRONG)
NSMutableDictionary *headers_; // Mach-o header information (STRONG)
NSMutableDictionary *lastFunctionStartDict_; // Keyed by section# (STRONG)
NSMutableDictionary *sectionNumbers_; // Keyed by seg/sect name (STRONG)
uint32_t lastStartAddress_;
}
- (id)initWithContentsOfFile:(NSString *)machoFile;

View File

@ -31,6 +31,8 @@
#include <unistd.h>
#include <signal.h>
#include <cxxabi.h>
#include <stdlib.h>
#include <mach/machine.h>
#include <mach-o/arch.h>
@ -38,11 +40,13 @@
#include <mach-o/loader.h>
#include <mach-o/nlist.h>
#include <mach-o/stab.h>
#include <fcntl.h>
#import <Foundation/Foundation.h>
#import "dump_syms.h"
#import "common/mac/file_id.h"
#import "common/mac/macho_utilities.h"
using google_breakpad::FileID;
@ -62,8 +66,6 @@ static NSString *kUnknownSymbol = @"???";
static const int kTextSection = 1;
@interface DumpSymbols(PrivateMethods)
- (NSString *)stringFromTask:(NSString *)action args:(NSArray *)args
standardIn:(NSFileHandle *)standardIn;
- (NSArray *)convertCPlusPlusSymbols:(NSArray *)symbols;
- (void)convertSymbols;
- (void)addFunction:(NSString *)name line:(int)line address:(uint64_t)address section:(int)section;
@ -77,113 +79,31 @@ static const int kTextSection = 1;
- (BOOL)loadModuleInfo;
@end
static BOOL StringHeadMatches(NSString *str, NSString *head) {
int headLen = [head length];
int strLen = [str length];
if (headLen > strLen)
return NO;
return [[str substringToIndex:headLen] isEqualToString:head];
}
static BOOL StringTailMatches(NSString *str, NSString *tail) {
int tailLen = [tail length];
int strLen = [str length];
if (tailLen > strLen)
return NO;
return [[str substringFromIndex:strLen - tailLen] isEqualToString:tail];
}
@implementation DumpSymbols
//=============================================================================
- (NSString *)stringFromTask:(NSString *)action args:(NSArray *)args
standardIn:(NSFileHandle *)standardIn {
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:action];
NSPipe *pipe = [NSPipe pipe];
[task setStandardOutput:pipe];
NSFileHandle *output = [pipe fileHandleForReading];
if (standardIn)
[task setStandardInput:standardIn];
if (args)
[task setArguments:args];
[task launch];
// This seems a bit strange, but when using [task waitUntilExit], it hangs
// waiting for data, but there isn't any. So, we'll poll for data,
// take a short nap, and then ask again
BOOL done = NO;
NSMutableData *allData = [NSMutableData data];
NSData *data = nil;
int exceptionCount = 0;
while (!done) {
data = nil;
// If there's a communications problem with the task, this might throw
// an exception. We'll catch and keep trying.
@try {
data = [output availableData];
}
@catch (NSException *e) {
++exceptionCount;
}
[allData appendData:data];
// Loop over the data until we're no longer returning data. If we're
// still receiving data, sleep for 1/2 second and let the task
// continue. If we keep receiving exceptions, bail out
if (![data length] && data || exceptionCount > 10)
done = YES;
else
usleep(500);
}
// Gather any remaining data
[task waitUntilExit];
data = [output availableData];
[allData appendData:data];
[task release];
return [[[NSString alloc] initWithData:allData
encoding:NSUTF8StringEncoding] autorelease];
}
//=============================================================================
- (NSArray *)convertCPlusPlusSymbols:(NSArray *)symbols {
NSString *action = @"/usr/bin/c++filt";
unsigned int count = [symbols count];
NSMutableArray *symbols_demangled = [[NSMutableArray alloc]
initWithCapacity:[symbols count]];
// __cxa_demangle will realloc this if needed
char *buffer = (char *)malloc(1024);
size_t buffer_size = 1024;
int result;
// It's possible that we have too many symbols on the command line.
// Unfortunately, c++filt doesn't take a file containing names, so we'll
// copy the symbols to a temporary file and use that as stdin.
char buffer[PATH_MAX];
snprintf(buffer, sizeof(buffer), "/tmp/dump_syms_filtXXXXX");
int fd = mkstemp(buffer);
char nl = '\n';
for (unsigned int i = 0; i < count; ++i) {
const char *symbol = [[symbols objectAtIndex:i] UTF8String];
write(fd, symbol, strlen(symbol));
write(fd, &nl, 1);
NSEnumerator *enumerator = [symbols objectEnumerator];
id symbolObject;
while ((symbolObject = [enumerator nextObject])) {
const char *symbol = [symbolObject UTF8String];
buffer = abi::__cxa_demangle(symbol, buffer, &buffer_size, &result);
if (result == 0) {
[symbols_demangled addObject:[NSString stringWithUTF8String:buffer]];
} else {
// unable to demangle - use mangled name instead
[symbols_demangled addObject:symbolObject];
}
}
free(buffer);
// Reset to the beginning and wrap up with a file handle
lseek(fd, 0, SEEK_SET);
NSArray *args = [NSArray arrayWithObject:@"-n"];
NSFileHandle *fh = [[NSFileHandle alloc] initWithFileDescriptor:fd
closeOnDealloc:YES];
NSArray *result = [[self stringFromTask:action args:args standardIn:fh]
componentsSeparatedByString:@"\n"];
[fh release];
return result;
return symbols_demangled;
}
//=============================================================================
@ -207,18 +127,40 @@ static BOOL StringTailMatches(NSString *str, NSString *tail) {
[symbols addObject:symbol];
}
NSArray *converted = [self convertCPlusPlusSymbols:symbols];
[symbols release];
// In order to deal with crashing problems in c++filt, we setup
// a while loop to handle the case where convertCPlusPlusSymbols
// only returns partial results.
// We then attempt to continue from the point where c++filt failed
// and add the partial results to the total results until we're
// completely done.
unsigned int totalIndex = 0;
unsigned int totalCount = count;
while (totalIndex < totalCount) {
NSRange range = NSMakeRange(totalIndex, totalCount - totalIndex);
NSArray *subarray = [symbols subarrayWithRange:range];
NSArray *converted = [self convertCPlusPlusSymbols:subarray];
unsigned int convertedCount = [converted count];
for (unsigned int i = 0; i < count; ++i) {
NSMutableDictionary *dict = [addresses_ objectForKey:
[addresses objectAtIndex:i]];
NSString *symbol = [converted objectAtIndex:i];
if (convertedCount == 0) {
break; // we give up at this point
}
for (unsigned int convertedIndex = 0;
convertedIndex < convertedCount && totalIndex < totalCount;
++totalIndex, ++convertedIndex) {
NSMutableDictionary *dict = [addresses_ objectForKey:
[addresses objectAtIndex:totalIndex]];
NSString *symbol = [converted objectAtIndex:convertedIndex];
// Only add if this is a non-zero length symbol
if ([symbol length])
[dict setObject:symbol forKey:kAddressConvertedSymbolKey];
// Only add if this is a non-zero length symbol
if ([symbol length])
[dict setObject:symbol forKey:kAddressConvertedSymbolKey];
}
}
[symbols release];
}
//=============================================================================
@ -232,24 +174,44 @@ static BOOL StringTailMatches(NSString *str, NSString *tail) {
// addresses to run through the c++filt
BOOL isCPP = NO;
if (StringHeadMatches(name, @"__Z")) {
if ([name hasPrefix:@"__Z"]) {
// Remove the leading underscore
name = [name substringFromIndex:1];
isCPP = YES;
} else if (StringHeadMatches(name, @"_Z")) {
} else if ([name hasPrefix:@"_Z"]) {
isCPP = YES;
}
// Filter out non-functions
if (StringTailMatches(name, @".eh"))
if ([name hasSuffix:@".eh"])
return;
if (StringTailMatches(name, @"__func__"))
if ([name hasSuffix:@"__func__"])
return;
if (StringTailMatches(name, @"GCC_except_table"))
if ([name hasSuffix:@"GCC_except_table"])
return;
if (isCPP) {
// OBJCPP_MANGLING_HACK
// There are cases where ObjC++ mangles up an ObjC name using quasi-C++
// mangling:
// @implementation Foozles + (void)barzles {
// static int Baz = 0;
// } @end
// gives you _ZZ18+[Foozles barzles]E3Baz
// c++filt won't parse this properly, and will crash in certain cases.
// Logged as radar:
// 5129938: c++filt does not deal with ObjC++ symbols
// If 5129938 ever gets fixed, we can remove this, but for now this prevents
// c++filt from attempting to demangle names it doesn't know how to handle.
// This is with c++filt 2.16
NSCharacterSet *objcppCharSet = [NSCharacterSet characterSetWithCharactersInString:@"-+[]: "];
NSRange emptyRange = { NSNotFound, 0 };
NSRange objcppRange = [name rangeOfCharacterFromSet:objcppCharSet];
isCPP = NSEqualRanges(objcppRange, emptyRange);
}
if (isCPP) {
if (!cppAddresses_)
cppAddresses_ = [[NSMutableArray alloc] init];
@ -269,17 +231,16 @@ static BOOL StringTailMatches(NSString *str, NSString *tail) {
[dict release];
}
if (name && ![dict objectForKey:kAddressSymbolKey])
if (name && ![dict objectForKey:kAddressSymbolKey]) {
[dict setObject:name forKey:kAddressSymbolKey];
// only functions, not line number addresses
[functionAddresses_ addObject:addressNum];
}
if (line && ![dict objectForKey:kAddressSourceLineKey])
[dict setObject:[NSNumber numberWithUnsignedInt:line]
forKey:kAddressSourceLineKey];
// Save the function name so that we can add the end of function address
if ([name length]) {
[lastFunctionStartDict_ setObject:addressNum forKey:[NSNumber numberWithUnsignedInt:section] ];
}
}
//=============================================================================
@ -287,10 +248,32 @@ static BOOL StringTailMatches(NSString *str, NSString *tail) {
uint32_t n_strx = list->n_un.n_strx;
BOOL result = NO;
// We don't care about non-section specific information
if (list->n_sect == 0 )
// We don't care about non-section specific information except function length
if (list->n_sect == 0 && list->n_type != N_FUN )
return NO;
if (list->n_type == N_FUN) {
if (list->n_sect != 0) {
// we get the function address from the first N_FUN
lastStartAddress_ = list->n_value;
}
else {
// an N_FUN from section 0 may follow the initial N_FUN
// giving us function length information
NSMutableDictionary *dict = [addresses_ objectForKey:
[NSNumber numberWithUnsignedLong:lastStartAddress_]];
assert(dict);
// only set the function size the first time
// (sometimes multiple section 0 N_FUN entries appear!)
if (![dict objectForKey:kFunctionSizeKey]) {
[dict setObject:[NSNumber numberWithUnsignedLongLong:list->n_value]
forKey:kFunctionSizeKey];
}
}
}
int line = list->n_desc;
// We only care about line number information in __TEXT __text
@ -298,7 +281,7 @@ static BOOL StringTailMatches(NSString *str, NSString *tail) {
if(list->n_sect != mainSection) {
line = 0;
}
// Extract debugging information:
// Doc: http://developer.apple.com/documentation/DeveloperTools/gdb/stabs/stabs_toc.html
// Header: /usr/include/mach-o/stab.h:
@ -336,24 +319,9 @@ static BOOL StringTailMatches(NSString *str, NSString *tail) {
} else if (((list->n_type & N_TYPE) == N_SECT) && !(list->n_type & N_STAB)) {
// Regular symbols or ones that are external
NSString *fn = [NSString stringWithUTF8String:&table[n_strx]];
[self addFunction:fn line:0 address:list->n_value section:list->n_sect ];
result = YES;
} else if (list->n_type == N_ENSYM && list->n_sect == mainSection ) {
NSNumber *lastFunctionStart = [lastFunctionStartDict_
objectForKey:[NSNumber numberWithUnsignedLongLong:list->n_sect] ];
if (lastFunctionStart) {
unsigned long long start = [lastFunctionStart unsignedLongLongValue];
unsigned long long size = list->n_value - start;
NSMutableDictionary *dict = [addresses_ objectForKey:lastFunctionStart];
assert(dict);
assert(list->n_value > start);
[dict setObject:[NSNumber numberWithUnsignedLongLong:size]
forKey:kFunctionSizeKey];
[lastFunctionStartDict_ removeObjectForKey:[NSNumber numberWithUnsignedLongLong:list->n_sect] ];
}
}
return result;
@ -392,7 +360,7 @@ static BOOL StringTailMatches(NSString *str, NSString *tail) {
nlist64.n_un.n_strx = SwapLongIfNeeded(list->n_un.n_strx);
nlist64.n_type = list->n_type;
nlist64.n_sect = list->n_sect;
nlist64.n_desc = SwapIntIfNeeded(list->n_desc);
nlist64.n_desc = SwapShortIfNeeded(list->n_desc);
nlist64.n_value = (uint64_t)SwapLongIfNeeded(list->n_value);
if ([self processSymbolItem:&nlist64 stringTable:strtab])
@ -437,7 +405,7 @@ static BOOL StringTailMatches(NSString *str, NSString *tail) {
nlist64.n_un.n_strx = SwapLongIfNeeded(list->n_un.n_strx);
nlist64.n_type = list->n_type;
nlist64.n_sect = list->n_sect;
nlist64.n_desc = SwapIntIfNeeded(list->n_desc);
nlist64.n_desc = SwapShortIfNeeded(list->n_desc);
nlist64.n_value = SwapLongLongIfNeeded(list->n_value);
if ([self processSymbolItem:&nlist64 stringTable:strtab])
@ -668,15 +636,6 @@ static BOOL WriteFormat(int fd, const char *fmt, ...) {
uint64_t moduleSize =
moduleSizeNum ? [moduleSizeNum unsignedLongLongValue] : 0;
[lastFunctionStartDict_ removeAllObjects];
// Gather the information
[self loadSymbolInfoForArchitecture];
[self convertSymbols];
NSArray *sortedAddresses = [[addresses_ allKeys]
sortedArrayUsingSelector:@selector(compare:)];
// UUID
FileID file_id([sourcePath_ fileSystemRepresentation]);
unsigned char identifier[16];
@ -688,9 +647,27 @@ static BOOL WriteFormat(int fd, const char *fmt, ...) {
sizeof(identifierStr));
}
else {
strlcpy(identifierStr, moduleName, sizeof(identifierStr));
fprintf(stderr, "Unable to calculate UUID of mach-o binary!\n");
return NO;
}
// keep track exclusively of function addresses
// for sanity checking function lengths
functionAddresses_ = [[NSMutableSet alloc] init];
// Gather the information
[self loadSymbolInfoForArchitecture];
[self convertSymbols];
NSArray *sortedAddresses = [[addresses_ allKeys]
sortedArrayUsingSelector:@selector(compare:)];
NSArray *sortedFunctionAddresses = [[functionAddresses_ allObjects]
sortedArrayUsingSelector:@selector(compare:)];
// position ourselves at the 2nd function
unsigned int funcIndex = 1;
// Remove the dashes from the string
NSMutableString *compactedStr =
[NSMutableString stringWithCString:identifierStr encoding:NSASCIIStringEncoding];
@ -733,7 +710,7 @@ static BOOL WriteFormat(int fd, const char *fmt, ...) {
// The symbol reader doesn't want a trailing newline
terminatingChar = '\0';
}
NSDictionary *dict = [addresses_ objectForKey:address];
NSNumber *line = [dict objectForKey:kAddressSourceLineKey];
NSString *symbol = [dict objectForKey:kAddressConvertedSymbolKey];
@ -741,29 +718,38 @@ static BOOL WriteFormat(int fd, const char *fmt, ...) {
if (!symbol)
symbol = [dict objectForKey:kAddressSymbolKey];
// sanity check the function length by making sure it doesn't
// run beyond the next function entry
uint64_t nextFunctionAddress = 0;
if (symbol && funcIndex < [sortedFunctionAddresses count]) {
nextFunctionAddress = [[sortedFunctionAddresses objectAtIndex:funcIndex]
unsignedLongLongValue] - baseAddress;
++funcIndex;
}
// Skip some symbols
if (StringHeadMatches(symbol, @"vtable for"))
if ([symbol hasPrefix:@"vtable for"])
continue;
if (StringHeadMatches(symbol, @"__static_initialization_and_destruction_0"))
if ([symbol hasPrefix:@"__static_initialization_and_destruction_0"])
continue;
if (StringHeadMatches(symbol, @"_GLOBAL__I__"))
if ([symbol hasPrefix:@"_GLOBAL__I__"])
continue;
if (StringHeadMatches(symbol, @"__func__."))
if ([symbol hasPrefix:@"__func__."])
continue;
if (StringHeadMatches(symbol, @"__gnu"))
if ([symbol hasPrefix:@"__gnu"])
continue;
if (StringHeadMatches(symbol, @"typeinfo "))
if ([symbol hasPrefix:@"typeinfo "])
continue;
if (StringHeadMatches(symbol, @"EH_frame"))
if ([symbol hasPrefix:@"EH_frame"])
continue;
if (StringHeadMatches(symbol, @"GCC_except_table"))
if ([symbol hasPrefix:@"GCC_except_table"])
continue;
// Find the source file (if any) that contains this address
@ -785,6 +771,16 @@ static BOOL WriteFormat(int fd, const char *fmt, ...) {
if (line) {
if (symbol && functionLength) {
uint64_t functionLengthVal = [functionLength unsignedLongLongValue];
// sanity check to make sure the length we were told does not exceed
// the space between this function and the next
if (nextFunctionAddress != 0) {
uint64_t functionLengthVal2 = nextFunctionAddress - addressVal;
if(functionLengthVal > functionLengthVal2 ) {
functionLengthVal = functionLengthVal2;
}
}
// Function
if (!WriteFormat(fd, "FUNC %llx %llx 0 %s\n", addressVal,
@ -824,10 +820,6 @@ static BOOL WriteFormat(int fd, const char *fmt, ...) {
return nil;
}
// keep track of last function start address on a per-section basis
if (!lastFunctionStartDict_)
lastFunctionStartDict_ = [[NSMutableDictionary alloc] init];
// If there's more than one, use the native one
if ([headers_ count] > 1) {
const NXArchInfo *localArchInfo = NXGetLocalArchInfo();
@ -863,10 +855,10 @@ static BOOL WriteFormat(int fd, const char *fmt, ...) {
[sourcePath_ release];
[architecture_ release];
[addresses_ release];
[functionAddresses_ release];
[sources_ release];
[headers_ release];
[lastFunctionStartDict_ release];
[super dealloc];
}

View File

@ -47,6 +47,7 @@
#include "common/mac/macho_id.h"
#include "common/mac/macho_walker.h"
#include "common/mac/macho_utilities.h"
namespace MacFileUtilities {
@ -142,7 +143,7 @@ void MachoID::Update(MachoWalker *walker, unsigned long offset, size_t size) {
}
bool MachoID::UUIDCommand(int cpu_type, unsigned char bytes[16]) {
struct uuid_command uuid_cmd;
struct breakpad_uuid_command uuid_cmd;
MachoWalker walker(path_, UUIDWalkerCB, &uuid_cmd);
uuid_cmd.cmd = 0;
@ -284,7 +285,7 @@ bool MachoID::WalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,
return false;
if (swap)
swap_segment_command_64(&seg64, NXHostByteOrder());
breakpad_swap_segment_command_64(&seg64, NXHostByteOrder());
struct mach_header_64 header;
off_t header_offset;
@ -301,7 +302,7 @@ bool MachoID::WalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,
return false;
if (swap)
swap_section_64(&sec64, 1, NXHostByteOrder());
breakpad_swap_section_64(&sec64, 1, NXHostByteOrder());
macho_id->Update(walker, header_offset + sec64.offset, sec64.size);
offset += sizeof(struct section_64);
@ -316,13 +317,15 @@ bool MachoID::WalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,
bool MachoID::UUIDWalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,
bool swap, void *context) {
if (cmd->cmd == LC_UUID) {
struct uuid_command *uuid_cmd = (struct uuid_command *)context;
struct breakpad_uuid_command *uuid_cmd =
(struct breakpad_uuid_command *)context;
if (!walker->ReadBytes(uuid_cmd, sizeof(struct uuid_command), offset))
if (!walker->ReadBytes(uuid_cmd, sizeof(struct breakpad_uuid_command),
offset))
return false;
if (swap)
swap_uuid_command(uuid_cmd, NXHostByteOrder());
breakpad_swap_uuid_command(uuid_cmd, NXHostByteOrder());
return false;
}

View File

@ -0,0 +1,89 @@
// Copyright (c) 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// macho_utilties.cc: Utilities for dealing with mach-o files
//
// Author: Dave Camp
#include "common/mac/macho_utilities.h"
void breakpad_swap_uuid_command(struct breakpad_uuid_command *uc,
enum NXByteOrder target_byte_order)
{
uc->cmd = NXSwapLong(uc->cmd);
uc->cmdsize = NXSwapLong(uc->cmdsize);
}
void breakpad_swap_segment_command_64(struct segment_command_64 *sg,
enum NXByteOrder target_byte_order)
{
sg->cmd = NXSwapLong(sg->cmd);
sg->cmdsize = NXSwapLong(sg->cmdsize);
sg->vmaddr = NXSwapLongLong(sg->vmaddr);
sg->vmsize = NXSwapLongLong(sg->vmsize);
sg->fileoff = NXSwapLongLong(sg->fileoff);
sg->filesize = NXSwapLongLong(sg->filesize);
sg->maxprot = NXSwapLong(sg->maxprot);
sg->initprot = NXSwapLong(sg->initprot);
sg->nsects = NXSwapLong(sg->nsects);
sg->flags = NXSwapLong(sg->flags);
}
void breakpad_swap_mach_header_64(struct mach_header_64 *mh,
enum NXByteOrder target_byte_order)
{
mh->magic = NXSwapLong(mh->magic);
mh->cputype = NXSwapLong(mh->cputype);
mh->cpusubtype = NXSwapLong(mh->cpusubtype);
mh->filetype = NXSwapLong(mh->filetype);
mh->ncmds = NXSwapLong(mh->ncmds);
mh->sizeofcmds = NXSwapLong(mh->sizeofcmds);
mh->flags = NXSwapLong(mh->flags);
mh->reserved = NXSwapLong(mh->reserved);
}
void breakpad_swap_section_64(struct section_64 *s,
uint32_t nsects,
enum NXByteOrder target_byte_order)
{
for (uint32_t i = 0; i < nsects; i++) {
s[i].addr = NXSwapLongLong(s[i].addr);
s[i].size = NXSwapLongLong(s[i].size);
s[i].offset = NXSwapLong(s[i].offset);
s[i].align = NXSwapLong(s[i].align);
s[i].reloff = NXSwapLong(s[i].reloff);
s[i].nreloc = NXSwapLong(s[i].nreloc);
s[i].flags = NXSwapLong(s[i].flags);
s[i].reserved1 = NXSwapLong(s[i].reserved1);
s[i].reserved2 = NXSwapLong(s[i].reserved2);
}
}

View File

@ -0,0 +1,85 @@
// Copyright (c) 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// macho_utilities.h: Utilities for dealing with mach-o files
//
// Author: Dave Camp
#ifndef COMMON_MAC_MACHO_UTILITIES_H__
#define COMMON_MAC_MACHO_UTILITIES_H__
#include <mach-o/loader.h>
#include <mach/thread_status.h>
/* Some #defines and structs that aren't defined in older SDKs */
#ifndef CPU_ARCH_ABI64
# define CPU_ARCH_ABI64 0x01000000
#endif
#ifndef CPU_TYPE_X86
# define CPU_TYPE_X86 CPU_TYPE_I386
#endif
#ifndef CPU_TYPE_POWERPC64
# define CPU_TYPE_POWERPC64 (CPU_TYPE_POWERPC | CPU_ARCH_ABI64)
#endif
#ifndef LC_UUID
# define LC_UUID 0x1b /* the uuid */
#endif
// The uuid_command struct/swap routines were added during the 10.4 series.
// Their presence isn't guaranteed.
struct breakpad_uuid_command {
uint32_t cmd; /* LC_UUID */
uint32_t cmdsize; /* sizeof(struct uuid_command) */
uint8_t uuid[16]; /* the 128-bit uuid */
};
void breakpad_swap_uuid_command(struct breakpad_uuid_command *uc,
enum NXByteOrder target_byte_order);
// Older SDKs defines thread_state_data_t as an int[] instead
// of the natural_t[] it should be.
typedef natural_t breakpad_thread_state_data_t[THREAD_STATE_MAX];
// The 64-bit swap routines were added during the 10.4 series, their
// presence isn't guaranteed.
void breakpad_swap_segment_command_64(struct segment_command_64 *sg,
enum NXByteOrder target_byte_order);
void breakpad_swap_mach_header_64(struct mach_header_64 *mh,
enum NXByteOrder target_byte_order);
void breakpad_swap_section_64(struct section_64 *s,
uint32_t nsects,
enum NXByteOrder target_byte_order);
#endif

View File

@ -42,6 +42,7 @@
#include <unistd.h>
#include "common/mac/macho_walker.h"
#include "common/mac/macho_utilities.h"
namespace MacFileUtilities {
@ -130,6 +131,9 @@ bool MachoWalker::FindHeader(int cpu_type, off_t &offset) {
if (!ReadBytes(&header_cpu_type, sizeof(header_cpu_type), offset))
return false;
if (magic == MH_CIGAM || magic == MH_CIGAM_64)
header_cpu_type = NXSwapInt(header_cpu_type);
if (valid_cpu_type != header_cpu_type)
return false;
@ -201,7 +205,7 @@ bool MachoWalker::WalkHeader64AtOffset(off_t offset) {
bool swap = (header.magic == MH_CIGAM_64);
if (swap)
swap_mach_header_64(&header, NXHostByteOrder());
breakpad_swap_mach_header_64(&header, NXHostByteOrder());
current_header_ = &header;
current_header_size_ = sizeof(header);

View File

@ -49,8 +49,10 @@ wstring GUIDString::GUIDToWString(GUID *guid) {
guid->Data4[0], guid->Data4[1], guid->Data4[2],
guid->Data4[3], guid->Data4[4], guid->Data4[5],
guid->Data4[6], guid->Data4[7]);
GB_WSU_SAFE_SWPRINTF_TERMINATE(guid_string,
sizeof(guid_string) / sizeof(guid_string[0]));
// remove when VC++7.1 is no longer supported
guid_string[sizeof(guid_string) / sizeof(guid_string[0]) - 1] = L'\0';
return wstring(guid_string);
}
@ -64,8 +66,10 @@ wstring GUIDString::GUIDToSymbolServerWString(GUID *guid) {
guid->Data4[0], guid->Data4[1], guid->Data4[2],
guid->Data4[3], guid->Data4[4], guid->Data4[5],
guid->Data4[6], guid->Data4[7]);
GB_WSU_SAFE_SWPRINTF_TERMINATE(guid_string,
sizeof(guid_string) / sizeof(guid_string[0]));
// remove when VC++7.1 is no longer supported
guid_string[sizeof(guid_string) / sizeof(guid_string[0]) - 1] = L'\0';
return wstring(guid_string);
}

View File

@ -230,7 +230,10 @@ wstring HTTPUpload::GenerateMultipartBoundary() {
wchar_t temp[kBoundaryLength];
swprintf(temp, kBoundaryLength, L"%s%08X%08X", kBoundaryPrefix, r0, r1);
GB_WSU_SAFE_SWPRINTF_TERMINATE(temp, kBoundaryLength);
// remove when VC++7.1 is no longer supported
temp[kBoundaryLength - 1] = L'\0';
return wstring(temp);
}

View File

@ -615,7 +615,8 @@ int PDBSourceLineWriter::GetFunctionStackParamSize(IDiaSymbol *function) {
goto next_child;
}
if (FAILED(child->get_type(&child_type))) {
// IDiaSymbol::get_type can succeed but still pass back a NULL value.
if (FAILED(child->get_type(&child_type)) || !child_type) {
goto next_child;
}
@ -725,8 +726,9 @@ bool PDBSourceLineWriter::GetModuleInfo(PDBModuleInfo *info) {
wchar_t age_string[9];
swprintf(age_string, sizeof(age_string) / sizeof(age_string[0]),
L"%x", age);
GB_WSU_SAFE_SWPRINTF_TERMINATE(age_string,
sizeof(age_string) / sizeof(age_string[0]));
// remove when VC++7.1 is no longer supported
age_string[sizeof(age_string) / sizeof(age_string[0]) - 1] = L'\0';
info->debug_identifier = GUIDString::GUIDToSymbolServerWString(&guid);
info->debug_identifier.append(age_string);
@ -742,9 +744,11 @@ bool PDBSourceLineWriter::GetModuleInfo(PDBModuleInfo *info) {
swprintf(identifier_string,
sizeof(identifier_string) / sizeof(identifier_string[0]),
L"%08X%x", signature, age);
GB_WSU_SAFE_SWPRINTF_TERMINATE(identifier_string,
sizeof(identifier_string) /
sizeof(identifier_string[0]));
// remove when VC++7.1 is no longer supported
identifier_string[sizeof(identifier_string) /
sizeof(identifier_string[0]) - 1] = L'\0';
info->debug_identifier = identifier_string;
}

View File

@ -49,18 +49,14 @@
#define WIN_STRING_FORMAT_LL "I64"
#endif // MSC_VER >= 1400
// When using swprintf, call GB_WSU_SWPRINTF_TERMINATE afterwards using the
// first two arguments to swprintf. This will ensure that the buffer is
// 0-terminated. MSVC8's swprintf always 0-terminates the buffer, so the
// macro is a no-op. This is done in a macro rather than a function
// because the function approach relies on vswprintf, which is incompatible
// with some analysis tools.
#if _MSC_VER >= 1400 // MSVC 2005/8
#define GB_WSU_SAFE_SWPRINTF_TERMINATE(buffer, count);
#else // _MSC_VER >= 1400
#define GB_WSU_SAFE_SWPRINTF_TERMINATE(buffer, count); \
(buffer)[(count) - 1] = L'\0';
#endif // _MSC_VER >= 1400
// A nonconforming version of swprintf, without the length argument, was
// included with the CRT prior to MSVC8. Although a conforming version was
// also available via an overload, it is not reliably chosen. _snwprintf
// behaves as a standards-confirming swprintf should, so force the use of
// _snwprintf when using older CRTs.
#if _MSC_VER < 1400 // MSVC 2005/8
#define swprintf _snwprintf
#endif // MSC_VER < 1400
namespace google_breakpad {

View File

@ -103,6 +103,10 @@ class Stackwalker {
// get information from the stack.
MemoryRegion *memory_;
// A list of modules, for populating each StackFrame's module information.
// This field is optional and may be NULL.
const CodeModules *modules_;
private:
// Obtains the context frame, the innermost called procedure in a stack
// trace. Returns NULL on failure. GetContextFrame allocates a new
@ -122,10 +126,6 @@ class Stackwalker {
const CallStack *stack,
const vector< linked_ptr<StackFrameInfo> > &stack_frame_info) = 0;
// A list of modules, for populating each StackFrame's module information.
// This field is optional and may be NULL.
const CodeModules *modules_;
// The optional SymbolSupplier for resolving source line info.
SymbolSupplier *supplier_;

View File

@ -139,6 +139,9 @@ Stackwalker* Stackwalker::StackwalkerForCPU(
const CodeModules *modules,
SymbolSupplier *supplier,
SourceLineResolverInterface *resolver) {
if (!context)
return NULL;
Stackwalker *cpu_stackwalker = NULL;
u_int32_t cpu = context->GetContextCPU();

View File

@ -38,6 +38,7 @@
#include "processor/stackwalker_x86.h"
#include "google_breakpad/processor/call_stack.h"
#include "google_breakpad/processor/code_modules.h"
#include "google_breakpad/processor/memory_region.h"
#include "google_breakpad/processor/stack_frame_cpu.h"
#include "processor/linked_ptr.h"
@ -163,15 +164,23 @@ StackFrame* StackwalkerX86::GetCallerFrame(
// postfix notation and will be passed to PostfixEvaluator::Evaluate.
// Given the dictionary and the program string, it is possible to compute
// the return address and the values of other registers in the calling
// function.
// function. When encountering a nontraditional frame (one which takes
// advantage of FPO), the stack may need to be scanned for these values.
// For traditional frames, simple deterministic dereferencing suffices
// without any need for scanning. The results of program string evaluation
// will be used to determine whether to scan for better values.
string program_string;
bool traditional_frame = true;
bool recover_ebp = true;
if (last_frame_info && last_frame_info->valid == StackFrameInfo::VALID_ALL) {
// FPO data available.
traditional_frame = false;
if (!last_frame_info->program_string.empty()) {
// The FPO data has its own program string, which will tell us how to
// get to the caller frame, and may even fill in the values of
// nonvolatile registers and provide pointers to local variables and
// parameters.
// parameters. In some cases, particularly with program strings that use
// .raSearchStart, the stack may need to be scanned afterward.
program_string = last_frame_info->program_string;
} else if (last_frame_info->allocates_base_pointer) {
// The function corresponding to the last frame doesn't use the frame
@ -197,6 +206,15 @@ StackFrame* StackwalkerX86::GetCallerFrame(
// the caller is at a known location in the saved-register area of
// the stack frame.
//
// For this type of frame, MSVC 14 (from Visual Studio 8/2005) in
// link-time code generation mode (/LTCG and /GL) can generate erroneous
// debugging data. The reported size of saved registers can be 0,
// which is clearly an error because these frames must, at the very
// least, save %ebp. For this reason, in addition to those given above
// about the use of .raSearchStart, the stack may need to be scanned
// for a better return address and a better frame pointer after the
// program string is evaluated.
//
// %eip_new = *(%esp_old + callee_params + saved_regs + locals)
// %ebp_new = *(%esp_old + callee_params + saved_regs - 8)
// %esp_new = %esp_old + callee_params + saved_regs + locals + 4
@ -217,12 +235,18 @@ StackFrame* StackwalkerX86::GetCallerFrame(
// is the value that it had in the caller, so it can be carried
// straight through without bringing its validity into question.
//
// Because of the use of .raSearchStart, the stack will possibly be
// examined to locate a better return address after program string
// evaluation. The stack will not be examined to locate a saved
// %ebp value, because these frames do not save (or use) %ebp.
//
// %eip_new = *(%esp_old + callee_params + saved_regs + locals)
// %esp_new = %esp_old + callee_params + saved_regs + locals + 4
// %ebp_new = %ebp_old
program_string = "$eip .raSearchStart ^ = "
"$esp .raSearchStart 4 + = "
"$ebp $ebp =";
recover_ebp = false;
}
} else {
// No FPO information is available for the last frame. Assume that the
@ -244,6 +268,10 @@ StackFrame* StackwalkerX86::GetCallerFrame(
// CALL itself, and 4 bytes for the callee's PUSH of the caller's frame
// pointer.
//
// Instruction and frame pointer recovery for these traditional frames is
// entirely deterministic, and the stack will not be scanned after
// recovering these values.
//
// %eip_new = *(%ebp_old + 4)
// %esp_new = %ebp_old + 8
// %ebp_new = *(%ebp_old)
@ -264,6 +292,91 @@ StackFrame* StackwalkerX86::GetCallerFrame(
return NULL;
}
// If this stack frame did not use %ebp in a traditional way, locating the
// return address isn't entirely deterministic. In that case, the stack
// can be scanned to locate the return address.
//
// Even in nontraditional frames, if program string evaluation resulted in
// both %eip and %ebp values of 0, trust that the end of the stack has been
// reached and don't scan for anything else.
if (!traditional_frame &&
(dictionary["$eip"] != 0 || dictionary["$ebp"] != 0)) {
int offset = 0;
// This scan can only be done if a CodeModules object is available, to
// check that candidate return addresses are in fact inside a module.
//
// TODO(mmentovai): This ignores dynamically-generated code. One possible
// solution is to check the minidump's memory map to see if the candidate
// %eip value comes from a mapped executable page, although this would
// require dumps that contain MINIDUMP_MEMORY_INFO, which the Breakpad
// client doesn't currently write (it would need to call MiniDumpWriteDump
// with the MiniDumpWithFullMemoryInfo type bit set). Even given this
// ability, older OSes (pre-XP SP2) and CPUs (pre-P4) don't enforce
// an independent execute privilege on memory pages.
u_int32_t eip = dictionary["$eip"];
if (modules_ && !modules_->GetModuleForAddress(eip)) {
const int kRASearchWords = 15;
// The instruction pointer at .raSearchStart was invalid, so start
// looking one 32-bit word above that location.
u_int32_t location_start = dictionary[".raSearchStart"] + 4;
for (u_int32_t location = location_start;
location <= location_start + kRASearchWords * 4;
location += 4) {
if (!memory_->GetMemoryAtAddress(location, &eip))
break;
if (modules_->GetModuleForAddress(eip)) {
// This is a better return address that what program string
// evaluation found. Use it, and set %esp to the location above the
// one where the return address was found.
//
// TODO(mmentovai): The return-address check can be made even
// stronger in modules for which debugging data is available. In
// that case, it's possible to check that the candidate return
// address is inside a known function.
dictionary["$eip"] = eip;
dictionary["$esp"] = location + 4;
offset = location - location_start;
break;
}
}
}
// When trying to recover the previous value of the frame pointer (%ebp),
// start looking at the lowest possible address in the saved-register
// area, and look at the entire saved register area, increased by the
// size of |offset| to account for additional data that may be on the
// stack. The scan is performed from the highest possible address to
// the lowest, because we expect that the function's prolog would have
// saved %ebp early.
u_int32_t ebp = dictionary["$ebp"];
u_int32_t value; // throwaway variable to check pointer validity
if (recover_ebp && !memory_->GetMemoryAtAddress(ebp, &value)) {
int fp_search_bytes = last_frame_info->saved_register_size + offset;
u_int32_t location_end = last_frame->context.esp +
last_frame_callee_parameter_size;
for (u_int32_t location = location_end + fp_search_bytes;
location >= location_end;
location -= 4) {
if (!memory_->GetMemoryAtAddress(location, &ebp))
break;
if (memory_->GetMemoryAtAddress(ebp, &value)) {
// The candidate value is a pointer to the same memory region
// (the stack). Prefer it as a recovered %ebp result.
dictionary["$ebp"] = ebp;
break;
}
}
}
}
// Treat an instruction address of 0 as end-of-stack. Treat incorrect stack
// direction as end-of-stack to enforce progress and avoid infinite loops.
if (dictionary["$eip"] == 0 ||

View File

@ -28,29 +28,25 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <string>
#include <cstdio>
#include "common/linux/dump_symbols.h"
using namespace google_breakpad;
int main(int argc, char **argv) {
if (argc < 2 || argc > 3) {
fprintf(stderr,
"Usage: %s <binary-with-stab-symbol> [output-symbol-file]\n",
argv[0]);
if (argc != 2) {
fprintf(stderr, "Usage: %s <binary-with-stab-symbol>\n", argv[0]);
return 1;
}
const char *binary = argv[1];
std::string symbol_file(binary);
symbol_file += ".sym";
if (argc == 3)
symbol_file = argv[2];
DumpSymbols dumper;
if (dumper.WriteSymbolFile(binary, symbol_file))
printf("Symbol file successfully written: %s\n", symbol_file.c_str());
else
printf("Failed to write symbol file.\n");
if (!dumper.WriteSymbolFile(binary, fileno(stdout))) {
fprintf(stderr, "Failed to write symbol file.\n");
return 1;
}
return 0;
}

View File

@ -75,12 +75,12 @@ static void TokenizeByChar(const std::string &source_string,
}
//=============================================================================
// Parse out the module line which have 6 parts.
// MODULE <os> <cpu> <uuid> <age> <module-name>
// Parse out the module line which have 5 parts.
// MODULE <os> <cpu> <uuid> <module-name>
static bool ModuleDataForSymbolFile(const std::string &file,
std::vector<std::string> *module_parts) {
assert(module_parts);
const size_t kModulePartNumber = 6;
const size_t kModulePartNumber = 5;
FILE *fp = fopen(file.c_str(), "r");
if (fp) {
char buffer[1024];
@ -105,14 +105,12 @@ static bool ModuleDataForSymbolFile(const std::string &file,
}
//=============================================================================
static std::string CompactIdentifier(const std::string &uuid,
const std::string &age) {
static std::string CompactIdentifier(const std::string &uuid) {
std::vector<std::string> components;
TokenizeByChar(uuid, '-', &components);
std::string result;
for (size_t i = 0; i < components.size(); ++i)
result += components[i];
result += age;
return result;
}
@ -126,20 +124,18 @@ static void Start(Options *options) {
return;
}
std::string compacted_id = CompactIdentifier(module_parts[3],
module_parts[4]);
std::string compacted_id = CompactIdentifier(module_parts[3]);
// Add parameters
if (!options->version.empty())
parameters["version"] = options->version;
// MODULE <os> <cpu> <uuid> <age> <module-name>
// 0 1 2 3 4 5
parameters["age"] = "1";
// MODULE <os> <cpu> <uuid> <module-name>
// 0 1 2 3 4
parameters["os"] = module_parts[1];
parameters["cpu"] = module_parts[2];
parameters["debug_file"] = module_parts[5];
parameters["code_file"] = module_parts[5];
parameters["debug_file"] = module_parts[4];
parameters["code_file"] = module_parts[4];
parameters["debug_identifier"] = compacted_id;
std::string response;
bool success = HTTPUpload::SendRequest(options->uploadURLStr,

View File

@ -7,6 +7,8 @@
objects = {
/* Begin PBXBuildFile section */
557800400BE1F28500EC23E0 /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = 5578003E0BE1F28500EC23E0 /* macho_utilities.cc */; };
557800410BE1F28500EC23E0 /* macho_utilities.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5578003F0BE1F28500EC23E0 /* macho_utilities.h */; };
8DD76F9A0486AA7600D96B5E /* crash_report.mm in Sources */ = {isa = PBXBuildFile; fileRef = 08FB7796FE84155DC02AAC07 /* crash_report.mm */; settings = {ATTRIBUTES = (); }; };
8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08FB779EFE84155DC02AAC07 /* Foundation.framework */; };
9B35FEE40B2675F9008DE8C7 /* code_module.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9B35FEE20B2675F9008DE8C7 /* code_module.h */; };
@ -65,6 +67,7 @@
9BE650B50B52FE3000611104 /* macho_id.h in CopyFiles */,
9BE650B70B52FE3000611104 /* macho_walker.h in CopyFiles */,
9B44619E0B66C66B00BBB817 /* system_info.h in CopyFiles */,
557800410BE1F28500EC23E0 /* macho_utilities.h in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 1;
};
@ -73,6 +76,8 @@
/* Begin PBXFileReference section */
08FB7796FE84155DC02AAC07 /* crash_report.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = crash_report.mm; sourceTree = "<group>"; };
08FB779EFE84155DC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
5578003E0BE1F28500EC23E0 /* macho_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = macho_utilities.cc; path = ../../../common/mac/macho_utilities.cc; sourceTree = SOURCE_ROOT; };
5578003F0BE1F28500EC23E0 /* macho_utilities.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = macho_utilities.h; path = ../../../common/mac/macho_utilities.h; sourceTree = SOURCE_ROOT; };
8DD76FA10486AA7600D96B5E /* crash_report */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = crash_report; sourceTree = BUILT_PRODUCTS_DIR; };
9B35FEE20B2675F9008DE8C7 /* code_module.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = code_module.h; path = ../../../google_breakpad/processor/code_module.h; sourceTree = SOURCE_ROOT; };
9B35FEE30B2675F9008DE8C7 /* code_modules.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = code_modules.h; path = ../../../google_breakpad/processor/code_modules.h; sourceTree = SOURCE_ROOT; };
@ -133,6 +138,8 @@
08FB7794FE84155DC02AAC07 /* crash_report */ = {
isa = PBXGroup;
children = (
5578003E0BE1F28500EC23E0 /* macho_utilities.cc */,
5578003F0BE1F28500EC23E0 /* macho_utilities.h */,
9BDF192D0B1BC15D00F8391B /* dump_syms.h */,
9BDF192E0B1BC15D00F8391B /* dump_syms.mm */,
08FB7796FE84155DC02AAC07 /* crash_report.mm */,
@ -304,6 +311,7 @@
9BE650B20B52FE3000611104 /* file_id.cc in Sources */,
9BE650B40B52FE3000611104 /* macho_id.cc in Sources */,
9BE650B60B52FE3000611104 /* macho_walker.cc in Sources */,
557800400BE1F28500EC23E0 /* macho_utilities.cc in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -7,6 +7,8 @@
objects = {
/* Begin PBXBuildFile section */
5578008B0BE1F3AB00EC23E0 /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = 557800890BE1F3AB00EC23E0 /* macho_utilities.cc */; };
5578008C0BE1F3AB00EC23E0 /* macho_utilities.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5578008A0BE1F3AB00EC23E0 /* macho_utilities.h */; };
8DD76F9A0486AA7600D96B5E /* dump_syms.mm in Sources */ = {isa = PBXBuildFile; fileRef = 08FB7796FE84155DC02AAC07 /* dump_syms.mm */; settings = {ATTRIBUTES = (); }; };
8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08FB779EFE84155DC02AAC07 /* Foundation.framework */; };
9BDF186F0B1BB43700F8391B /* dump_syms.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BDF186D0B1BB43700F8391B /* dump_syms.h */; };
@ -30,6 +32,7 @@
9BE650480B52F6D800611104 /* file_id.h in CopyFiles */,
9BE6504A0B52F6D800611104 /* macho_id.h in CopyFiles */,
9BE6504C0B52F6D800611104 /* macho_walker.h in CopyFiles */,
5578008C0BE1F3AB00EC23E0 /* macho_utilities.h in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 1;
};
@ -38,6 +41,8 @@
/* Begin PBXFileReference section */
08FB7796FE84155DC02AAC07 /* dump_syms.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = dump_syms.mm; path = ../../../common/mac/dump_syms.mm; sourceTree = "<group>"; };
08FB779EFE84155DC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
557800890BE1F3AB00EC23E0 /* macho_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = macho_utilities.cc; path = ../../../common/mac/macho_utilities.cc; sourceTree = SOURCE_ROOT; };
5578008A0BE1F3AB00EC23E0 /* macho_utilities.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = macho_utilities.h; path = ../../../common/mac/macho_utilities.h; sourceTree = SOURCE_ROOT; };
8DD76FA10486AA7600D96B5E /* dump_syms */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dump_syms; sourceTree = BUILT_PRODUCTS_DIR; };
9BDF186D0B1BB43700F8391B /* dump_syms.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dump_syms.h; path = ../../../common/mac/dump_syms.h; sourceTree = "<group>"; };
9BDF186E0B1BB43700F8391B /* dump_syms_tool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = dump_syms_tool.m; sourceTree = "<group>"; };
@ -64,6 +69,8 @@
08FB7794FE84155DC02AAC07 /* dump_syms */ = {
isa = PBXGroup;
children = (
557800890BE1F3AB00EC23E0 /* macho_utilities.cc */,
5578008A0BE1F3AB00EC23E0 /* macho_utilities.h */,
9BE650410B52F6D800611104 /* file_id.cc */,
9BE650420B52F6D800611104 /* file_id.h */,
9BE650430B52F6D800611104 /* macho_id.cc */,
@ -141,6 +148,7 @@
9BE650470B52F6D800611104 /* file_id.cc in Sources */,
9BE650490B52F6D800611104 /* macho_id.cc in Sources */,
9BE6504B0B52F6D800611104 /* macho_walker.cc in Sources */,
5578008B0BE1F3AB00EC23E0 /* macho_utilities.cc in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -34,6 +34,7 @@
#include <mach-o/arch.h>
#include "dump_syms.h"
#include "common/mac/macho_utilities.h"
typedef struct {
NSString *srcPath;

View File

@ -31,7 +31,7 @@
/* Begin PBXFileReference section */
08FB7796FE84155DC02AAC07 /* symupload.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = symupload.m; sourceTree = "<group>"; };
08FB779EFE84155DC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
8DD76FA10486AA7600D96B5E /* symupload */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = "compiled.mach-o.executable"; path = symupload; sourceTree = BUILT_PRODUCTS_DIR; };
8DD76FA10486AA7600D96B5E /* symupload */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = symupload; sourceTree = BUILT_PRODUCTS_DIR; };
9BD833680B03E4080055103E /* HTTPMultipartUpload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HTTPMultipartUpload.h; path = ../../../common/mac/HTTPMultipartUpload.h; sourceTree = "<group>"; };
9BD833690B03E4080055103E /* HTTPMultipartUpload.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HTTPMultipartUpload.m; path = ../../../common/mac/HTTPMultipartUpload.m; sourceTree = "<group>"; };
9BD835FB0B0544950055103E /* minidump_upload */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = minidump_upload; sourceTree = BUILT_PRODUCTS_DIR; };

View File

@ -97,8 +97,10 @@ static bool GetFileVersionString(const wchar_t *filename, wstring *version) {
file_info->dwFileVersionMS & 0xffff,
file_info->dwFileVersionLS >> 16,
file_info->dwFileVersionLS & 0xffff);
GB_WSU_SAFE_SWPRINTF_TERMINATE(ver_string,
sizeof(ver_string) / sizeof(ver_string[0]));
// remove when VC++7.1 is no longer supported
ver_string[sizeof(ver_string) / sizeof(ver_string[0]) - 1] = L'\0';
*version = ver_string;
return true;
}