зеркало из https://github.com/mozilla/pjs.git
bug 379518 - sync up with breakpad svn, to revision 157. r=mento
This commit is contained in:
Родитель
0194438973
Коммит
9ae83b0619
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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__
|
|
@ -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
|
||||
|
|
|
@ -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_;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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__
|
|
@ -55,6 +55,7 @@ CPPSRCS = \
|
|||
macho_id.cc \
|
||||
macho_walker.cc \
|
||||
string_utilities.cc \
|
||||
macho_utilities.cc \
|
||||
$(NULL)
|
||||
|
||||
CMSRCS = \
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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_;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 ||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <mach-o/arch.h>
|
||||
|
||||
#include "dump_syms.h"
|
||||
#include "common/mac/macho_utilities.h"
|
||||
|
||||
typedef struct {
|
||||
NSString *srcPath;
|
||||
|
|
|
@ -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; };
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче