Allow out-of-process minidump generation to work on processes of a different CPU architecture

R=mark at http://breakpad.appspot.com/241001

git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@746 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
ted.mielczarek@gmail.com 2010-12-15 21:55:56 +00:00
parent 0d9bd40775
commit 0344a368de
10 changed files with 1262 additions and 653 deletions

View file

@ -53,6 +53,8 @@
8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; };
D23F4B2E12A7E13200686C8D /* minidump_generator_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D23F4B2C12A7E13200686C8D /* minidump_generator_test.cc */; };
D23F4B3312A7E17700686C8D /* libgtest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D2F9A41512131EF0002747C1 /* libgtest.a */; };
D23F4BB112A868CB00686C8D /* minidump_generator_test_helper.cc in Sources */ = {isa = PBXBuildFile; fileRef = D23F4B9A12A8688800686C8D /* minidump_generator_test_helper.cc */; };
D23F4BB812A868F700686C8D /* MachIPC.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C53790ECCE635009BE4BA /* MachIPC.mm */; };
D244536A12426F00009BBCE0 /* logging.cc in Sources */ = {isa = PBXBuildFile; fileRef = D244535112426EBB009BBCE0 /* logging.cc */; };
D244536B12426F00009BBCE0 /* minidump.cc in Sources */ = {isa = PBXBuildFile; fileRef = D244535212426EBB009BBCE0 /* minidump.cc */; };
D244536C12426F00009BBCE0 /* pathname_stripper.cc in Sources */ = {isa = PBXBuildFile; fileRef = D244535312426EBB009BBCE0 /* pathname_stripper.cc */; };
@ -324,6 +326,13 @@
remoteGlobalIDString = D2F9A41412131EF0002747C1;
remoteInfo = gtest;
};
D23F4BB912A8694C00686C8D /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
proxyType = 1;
remoteGlobalIDString = D23F4BAA12A868A500686C8D;
remoteInfo = minidump_generator_test_helper;
};
D2F9A44212131F80002747C1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
@ -525,6 +534,8 @@
8B4BDAA7120124EA009C7060 /* libcrypto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.dylib; path = usr/lib/libcrypto.dylib; sourceTree = SDKROOT; };
8DC2EF5B0486A6940098B216 /* Breakpad.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Breakpad.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D23F4B2C12A7E13200686C8D /* minidump_generator_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_generator_test.cc; path = tests/minidump_generator_test.cc; sourceTree = "<group>"; };
D23F4B9A12A8688800686C8D /* minidump_generator_test_helper.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_generator_test_helper.cc; path = tests/minidump_generator_test_helper.cc; sourceTree = "<group>"; };
D23F4BAB12A868A500686C8D /* minidump_generator_test_helper */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = minidump_generator_test_helper; sourceTree = BUILT_PRODUCTS_DIR; };
D244534F12426E98009BBCE0 /* basic_code_modules.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = basic_code_modules.cc; path = ../../processor/basic_code_modules.cc; sourceTree = SOURCE_ROOT; };
D244535112426EBB009BBCE0 /* logging.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = logging.cc; path = ../../processor/logging.cc; sourceTree = SOURCE_ROOT; };
D244535212426EBB009BBCE0 /* minidump.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump.cc; path = ../../processor/minidump.cc; sourceTree = SOURCE_ROOT; };
@ -656,6 +667,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
D23F4BA912A868A500686C8D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
D2F9A41312131EF0002747C1 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@ -767,6 +785,7 @@
F93DE32C0F82C55600608B94 /* handler_test */,
D2F9A41512131EF0002747C1 /* libgtest.a */,
D2F9A546121383A1002747C1 /* crash_generation_server_test */,
D23F4BAB12A868A500686C8D /* minidump_generator_test_helper */,
);
name = Products;
sourceTree = "<group>";
@ -1021,6 +1040,7 @@
F9C77DDF0F7DD7CF0045F7DB /* tests */ = {
isa = PBXGroup;
children = (
D23F4B9A12A8688800686C8D /* minidump_generator_test_helper.cc */,
D23F4B2C12A7E13200686C8D /* minidump_generator_test.cc */,
D2F9A4CE121336F7002747C1 /* crash_generation_server_test.cc */,
D2F9A3D41212F87C002747C1 /* exception_handler_test.cc */,
@ -1095,6 +1115,22 @@
productReference = 8DC2EF5B0486A6940098B216 /* Breakpad.framework */;
productType = "com.apple.product-type.framework";
};
D23F4BAA12A868A500686C8D /* minidump_generator_test_helper */ = {
isa = PBXNativeTarget;
buildConfigurationList = D23F4BB012A868C400686C8D /* Build configuration list for PBXNativeTarget "minidump_generator_test_helper" */;
buildPhases = (
D23F4BA812A868A500686C8D /* Sources */,
D23F4BA912A868A500686C8D /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = minidump_generator_test_helper;
productName = minidump_generator_test_helper;
productReference = D23F4BAB12A868A500686C8D /* minidump_generator_test_helper */;
productType = "com.apple.product-type.tool";
};
D2F9A41412131EF0002747C1 /* gtest */ = {
isa = PBXNativeTarget;
buildConfigurationList = D2F9A42D12131F0E002747C1 /* Build configuration list for PBXNativeTarget "gtest" */;
@ -1192,6 +1228,7 @@
);
dependencies = (
D23F4B3012A7E16200686C8D /* PBXTargetDependency */,
D23F4BBA12A8694C00686C8D /* PBXTargetDependency */,
);
name = generator_test;
productName = generator_test;
@ -1325,6 +1362,7 @@
F93DE32B0F82C55600608B94 /* handler_test */,
D2F9A41412131EF0002747C1 /* gtest */,
D2F9A52A121383A1002747C1 /* crash_generation_server_test */,
D23F4BAA12A868A500686C8D /* minidump_generator_test_helper */,
);
};
/* End PBXProject section */
@ -1559,6 +1597,15 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
D23F4BA812A868A500686C8D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D23F4BB112A868CB00686C8D /* minidump_generator_test_helper.cc in Sources */,
D23F4BB812A868F700686C8D /* MachIPC.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
D2F9A41212131EF0002747C1 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@ -1756,6 +1803,11 @@
target = D2F9A41412131EF0002747C1 /* gtest */;
targetProxy = D23F4B2F12A7E16200686C8D /* PBXContainerItemProxy */;
};
D23F4BBA12A8694C00686C8D /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = D23F4BAA12A868A500686C8D /* minidump_generator_test_helper */;
targetProxy = D23F4BB912A8694C00686C8D /* PBXContainerItemProxy */;
};
D2F9A44312131F80002747C1 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = D2F9A41412131EF0002747C1 /* gtest */;
@ -1953,6 +2005,10 @@
baseConfigurationReference = 8B31027711F0D3AF00FCF3E4 /* BreakpadDebug.xcconfig */;
buildSettings = {
GCC_TREAT_WARNINGS_AS_ERRORS = NO;
SDKROOT = macosx10.5;
"SDKROOT[arch=i386]" = macosx10.4;
"SDKROOT[arch=ppc]" = macosx10.4;
"SDKROOT[arch=x86_64]" = macosx10.6;
};
name = Debug;
};
@ -1964,6 +2020,49 @@
};
name = Release;
};
D23F4BAD12A868A600686C8D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_FIX_AND_CONTINUE = YES;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
HEADER_SEARCH_PATHS = ../..;
INSTALL_PATH = /usr/local/bin;
PREBINDING = NO;
PRODUCT_NAME = minidump_generator_test_helper;
};
name = Debug;
};
D23F4BAE12A868A600686C8D /* Debug With Code Coverage */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
GCC_ENABLE_FIX_AND_CONTINUE = YES;
GCC_MODEL_TUNING = G5;
INSTALL_PATH = /usr/local/bin;
PREBINDING = NO;
PRODUCT_NAME = minidump_generator_test_helper;
};
name = "Debug With Code Coverage";
};
D23F4BAF12A868A600686C8D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_MODEL_TUNING = G5;
INSTALL_PATH = /usr/local/bin;
PREBINDING = NO;
PRODUCT_NAME = minidump_generator_test_helper;
ZERO_LINK = NO;
};
name = Release;
};
D2F9A41612131EF0002747C1 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -2387,6 +2486,16 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
D23F4BB012A868C400686C8D /* Build configuration list for PBXNativeTarget "minidump_generator_test_helper" */ = {
isa = XCConfigurationList;
buildConfigurations = (
D23F4BAD12A868A600686C8D /* Debug */,
D23F4BAE12A868A600686C8D /* Debug With Code Coverage */,
D23F4BAF12A868A600686C8D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
D2F9A42D12131F0E002747C1 /* Build configuration list for PBXNativeTarget "gtest" */ = {
isa = XCConfigurationList;
buildConfigurations = (

View file

@ -54,7 +54,7 @@
*/
/* nealsid:
/*
* This file was copied from libc/gen/nlist.c from Darwin's source code
* The version of nlist used as a base is from 10.5.2, libc-498
* http://www.opensource.apple.com/darwinsource/10.5.2/Libc-498/gen/nlist.c
@ -62,24 +62,22 @@
* The full tarball is at:
* http://www.opensource.apple.com/darwinsource/tarballs/apsl/Libc-498.tar.gz
*
* I've modified it to be compatible with 64-bit images. However,
* 32-bit compatibility has not been retained.
* I've modified it to be compatible with 64-bit images.
*/
#ifdef __LP64__
#include "breakpad_nlist_64.h"
#include <fcntl.h>
#include <mach-o/nlist.h>
#include <mach-o/loader.h>
#include <mach-o/fat.h>
#include <mach/mach.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include "breakpad_nlist_64.h"
#include <TargetConditionals.h>
#include <stdio.h>
#include <mach/mach.h>
#include <unistd.h>
/* Stuff lifted from <a.out.h> and <sys/exec.h> since they are gone */
/*
@ -108,44 +106,77 @@ struct exec {
#define N_SYMOFF(x) \
(N_TXTOFF(x) + (x).a_text+(x).a_data + (x).a_trsize+(x).a_drsize)
// Traits structs for specializing function templates to handle
// 32-bit/64-bit Mach-O files.
template<typename T>
struct MachBits {};
typedef struct nlist nlist32;
typedef struct nlist_64 nlist64;
template<>
struct MachBits<nlist32> {
typedef mach_header mach_header_type;
typedef uint32_t word_type;
static const uint32_t magic = MH_MAGIC;
};
template<>
struct MachBits<nlist64> {
typedef mach_header_64 mach_header_type;
typedef uint64_t word_type;
static const uint32_t magic = MH_MAGIC_64;
};
template<typename nlist_type>
int
__breakpad_fdnlist_64(int fd, breakpad_nlist *list, const char **symbolNames);
__breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames,
cpu_type_t cpu_type);
/*
* nlist - retreive attributes from name list (string table version)
*/
int
breakpad_nlist_64(const char *name,
breakpad_nlist *list,
const char **symbolNames) {
int fd, n;
fd = open(name, O_RDONLY, 0);
template <typename nlist_type>
int breakpad_nlist_common(const char *name,
nlist_type *list,
const char **symbolNames,
cpu_type_t cpu_type) {
int fd = open(name, O_RDONLY, 0);
if (fd < 0)
return (-1);
n = __breakpad_fdnlist_64(fd, list, symbolNames);
(void)close(fd);
return (n);
return -1;
int n = __breakpad_fdnlist(fd, list, symbolNames, cpu_type);
close(fd);
return n;
}
int breakpad_nlist(const char *name,
struct nlist *list,
const char **symbolNames,
cpu_type_t cpu_type) {
return breakpad_nlist_common(name, list, symbolNames, cpu_type);
}
int breakpad_nlist(const char *name,
struct nlist_64 *list,
const char **symbolNames,
cpu_type_t cpu_type) {
return breakpad_nlist_common(name, list, symbolNames, cpu_type);
}
/* Note: __fdnlist() is called from kvm_nlist in libkvm's kvm.c */
int
__breakpad_fdnlist_64(int fd, breakpad_nlist *list, const char **symbolNames) {
register breakpad_nlist *p, *q;
breakpad_nlist space[BUFSIZ/sizeof (breakpad_nlist)];
template<typename nlist_type>
int __breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames,
cpu_type_t cpu_type) {
typedef typename MachBits<nlist_type>::mach_header_type mach_header_type;
typedef typename MachBits<nlist_type>::word_type word_type;
const register char *s1, *s2;
register register_t n, m;
int maxlen, nreq;
off_t sa; /* symbol address */
off_t ss; /* start of strings */
struct exec buf;
unsigned arch_offset = 0;
const uint32_t magic = MachBits<nlist_type>::magic;
maxlen = 500;
for (q = list, nreq = 0;
int maxlen = 500;
int nreq = 0;
for (nlist_type* q = list;
symbolNames[q-list] && symbolNames[q-list][0];
q++, nreq++) {
@ -156,61 +187,61 @@ __breakpad_fdnlist_64(int fd, breakpad_nlist *list, const char **symbolNames) {
q->n_un.n_strx = 0;
}
struct exec buf;
if (read(fd, (char *)&buf, sizeof(buf)) != sizeof(buf) ||
(N_BADMAG(buf) && *((long *)&buf) != MH_MAGIC &&
(N_BADMAG(buf) && *((long *)&buf) != magic &&
NXSwapBigLongToHost(*((long *)&buf)) != FAT_MAGIC) &&
/* nealsid: The following is the big-endian ppc64 check */
/* The following is the big-endian ppc64 check */
(*((long*)&buf)) != FAT_MAGIC) {
return (-1);
return -1;
}
/* Deal with fat file if necessary */
unsigned arch_offset = 0;
if (NXSwapBigLongToHost(*((long *)&buf)) == FAT_MAGIC ||
/* nealsid: The following is the big-endian ppc64 check */
/* The following is the big-endian ppc64 check */
*((unsigned int *)&buf) == FAT_MAGIC) {
/* Get host info */
host_t host = mach_host_self();
unsigned i = HOST_BASIC_INFO_COUNT;
struct host_basic_info hbi;
struct fat_header fh;
struct fat_arch *fat_archs, *fap;
unsigned i;
host_t host;
/* Get our host info */
host = mach_host_self();
i = HOST_BASIC_INFO_COUNT;
kern_return_t kr;
if ((kr=host_info(host, HOST_BASIC_INFO,
(host_info_t)(&hbi), &i)) != KERN_SUCCESS) {
return (-1);
if ((kr = host_info(host, HOST_BASIC_INFO,
(host_info_t)(&hbi), &i)) != KERN_SUCCESS) {
return -1;
}
mach_port_deallocate(mach_task_self(), host);
/* Read in the fat header */
lseek(fd, 0, SEEK_SET);
struct fat_header fh;
if (lseek(fd, 0, SEEK_SET) == -1) {
return -1;
}
if (read(fd, (char *)&fh, sizeof(fh)) != sizeof(fh)) {
return (-1);
return -1;
}
/* Convert fat_narchs to host byte order */
fh.nfat_arch = NXSwapBigIntToHost(fh.nfat_arch);
/* Read in the fat archs */
fat_archs = (struct fat_arch *)malloc(fh.nfat_arch *
sizeof(struct fat_arch));
struct fat_arch *fat_archs =
(struct fat_arch *)malloc(fh.nfat_arch * sizeof(struct fat_arch));
if (fat_archs == NULL) {
return (-1);
return -1;
}
if (read(fd, (char *)fat_archs,
sizeof(struct fat_arch) * fh.nfat_arch) !=
(ssize_t)sizeof(struct fat_arch) * fh.nfat_arch) {
free(fat_archs);
return (-1);
return -1;
}
/*
* Convert archs to host byte ordering (a constraint of
* cpusubtype_getbestarch()
*/
for (i = 0; i < fh.nfat_arch; i++) {
for (unsigned i = 0; i < fh.nfat_arch; i++) {
fat_archs[i].cputype =
NXSwapBigIntToHost(fat_archs[i].cputype);
fat_archs[i].cpusubtype =
@ -223,159 +254,159 @@ __breakpad_fdnlist_64(int fd, breakpad_nlist *list, const char **symbolNames) {
NXSwapBigIntToHost(fat_archs[i].align);
}
fap = NULL;
for (i = 0; i < fh.nfat_arch; i++) {
/* nealsid: Although the original Apple code uses host_info */
/* to retrieve the CPU type, the host_info will still return */
/* CPU_TYPE_X86 even if running as an x86_64 binary. Given that */
/* this code isn't necessary on i386, I've decided to hardcode */
/* looking for a 64-bit binary */
#if TARGET_CPU_X86_64
if (fat_archs[i].cputype == CPU_TYPE_X86_64) {
#elif TARGET_CPU_PPC64
if (fat_archs[i].cputype == CPU_TYPE_POWERPC64) {
#else
#error undefined cpu!
{
#endif
fap = &fat_archs[i];
break;
}
}
if (!fap) {
free(fat_archs);
return (-1);
}
arch_offset = fap->offset;
free(fat_archs);
/* Read in the beginning of the architecture-specific file */
lseek(fd, arch_offset, SEEK_SET);
if (read(fd, (char *)&buf, sizeof(buf)) != sizeof(buf)) {
return (-1);
}
}
if (*((unsigned int *)&buf) == MH_MAGIC_64) {
struct mach_header_64 mh;
struct load_command *load_commands, *lcp;
struct symtab_command *stp;
long i;
lseek(fd, arch_offset, SEEK_SET);
if (read(fd, (char *)&mh, sizeof(mh)) != sizeof(mh)) {
return (-1);
}
load_commands = (struct load_command *)malloc(mh.sizeofcmds);
if (load_commands == NULL) {
return (-1);
}
if (read(fd, (char *)load_commands, mh.sizeofcmds) !=
mh.sizeofcmds) {
free(load_commands);
return (-1);
}
stp = NULL;
lcp = load_commands;
// nealsid:iterate through all load commands, looking for
// LC_SYMTAB load command
for (i = 0; i < mh.ncmds; i++) {
if (lcp->cmdsize % sizeof(long) != 0 ||
lcp->cmdsize <= 0 ||
(char *)lcp + lcp->cmdsize >
(char *)load_commands + mh.sizeofcmds) {
free(load_commands);
return (-1);
}
if (lcp->cmd == LC_SYMTAB) {
if (lcp->cmdsize !=
sizeof(struct symtab_command)) {
free(load_commands);
return (-1);
}
stp = (struct symtab_command *)lcp;
break;
}
lcp = (struct load_command *)
((char *)lcp + lcp->cmdsize);
}
if (stp == NULL) {
free(load_commands);
return (-1);
}
// sa points to the beginning of the symbol table
sa = stp->symoff + arch_offset;
// ss points to the beginning of the string table
ss = stp->stroff + arch_offset;
// n is the number of bytes in the symbol table
// each symbol table entry is an nlist structure
n = stp->nsyms * sizeof(breakpad_nlist);
free(load_commands);
}
else {
sa = N_SYMOFF(buf) + arch_offset;
ss = sa + buf.a_syms + arch_offset;
n = buf.a_syms;
}
lseek(fd, sa, SEEK_SET);
// the algorithm here is to read the nlist entries in m-sized
// chunks into q. q is then iterated over. for each entry in q,
// use the string table index(q->n_un.n_strx) to read the symbol
// name, then scan the nlist entries passed in by the user(via p),
// and look for a match
while (n) {
long savpos;
m = sizeof (space);
if (n < m)
m = n;
if (read(fd, (char *)space, m) != m)
struct fat_arch *fap = NULL;
for (unsigned i = 0; i < fh.nfat_arch; i++) {
if (fat_archs[i].cputype == cpu_type) {
fap = &fat_archs[i];
break;
n -= m;
savpos = lseek(fd, 0, SEEK_CUR);
for (q = space; (m -= sizeof(breakpad_nlist)) >= 0; q++) {
char nambuf[BUFSIZ];
if (q->n_un.n_strx == 0 || q->n_type & N_STAB)
continue;
// seek to the location in the binary where the symbol
// name is stored & read it into memory
lseek(fd, ss+q->n_un.n_strx, SEEK_SET);
read(fd, nambuf, maxlen+1);
s2 = nambuf;
for (p = list;
symbolNames[p-list] &&
symbolNames[p-list][0];
p++) {
// get the symbol name the user has passed in that
// corresponds to the nlist entry that we're looking at
s1 = symbolNames[p - list];
while (*s1) {
if (*s1++ != *s2++)
goto cont;
}
if (*s2)
goto cont;
p->n_value = q->n_value;
p->n_type = q->n_type;
p->n_desc = q->n_desc;
p->n_sect = q->n_sect;
p->n_un.n_strx = q->n_un.n_strx;
if (--nreq == 0)
return (nreq);
break;
cont: ;
}
}
lseek(fd, savpos, SEEK_SET);
}
return (nreq);
if (!fap) {
free(fat_archs);
return -1;
}
arch_offset = fap->offset;
free(fat_archs);
/* Read in the beginning of the architecture-specific file */
if (lseek(fd, arch_offset, SEEK_SET) == -1) {
return -1;
}
if (read(fd, (char *)&buf, sizeof(buf)) != sizeof(buf)) {
return -1;
}
}
#endif /* __LP64__ */
off_t sa; /* symbol address */
off_t ss; /* start of strings */
register register_t n;
if (*((unsigned int *)&buf) == magic) {
if (lseek(fd, arch_offset, SEEK_SET) == -1) {
return -1;
}
mach_header_type mh;
if (read(fd, (char *)&mh, sizeof(mh)) != sizeof(mh)) {
return -1;
}
struct load_command *load_commands =
(struct load_command *)malloc(mh.sizeofcmds);
if (load_commands == NULL) {
return -1;
}
if (read(fd, (char *)load_commands, mh.sizeofcmds) !=
mh.sizeofcmds) {
free(load_commands);
return -1;
}
struct symtab_command *stp = NULL;
struct load_command *lcp = load_commands;
// iterate through all load commands, looking for
// LC_SYMTAB load command
for (long i = 0; i < mh.ncmds; i++) {
if (lcp->cmdsize % sizeof(word_type) != 0 ||
lcp->cmdsize <= 0 ||
(char *)lcp + lcp->cmdsize >
(char *)load_commands + mh.sizeofcmds) {
free(load_commands);
return -1;
}
if (lcp->cmd == LC_SYMTAB) {
if (lcp->cmdsize !=
sizeof(struct symtab_command)) {
free(load_commands);
return -1;
}
stp = (struct symtab_command *)lcp;
break;
}
lcp = (struct load_command *)
((char *)lcp + lcp->cmdsize);
}
if (stp == NULL) {
free(load_commands);
return -1;
}
// sa points to the beginning of the symbol table
sa = stp->symoff + arch_offset;
// ss points to the beginning of the string table
ss = stp->stroff + arch_offset;
// n is the number of bytes in the symbol table
// each symbol table entry is an nlist structure
n = stp->nsyms * sizeof(nlist_type);
free(load_commands);
} else {
sa = N_SYMOFF(buf) + arch_offset;
ss = sa + buf.a_syms + arch_offset;
n = buf.a_syms;
}
if (lseek(fd, sa, SEEK_SET) == -1) {
return -1;
}
// the algorithm here is to read the nlist entries in m-sized
// chunks into q. q is then iterated over. for each entry in q,
// use the string table index(q->n_un.n_strx) to read the symbol
// name, then scan the nlist entries passed in by the user(via p),
// and look for a match
while (n) {
nlist_type space[BUFSIZ/sizeof (nlist_type)];
register register_t m = sizeof (space);
if (n < m)
m = n;
if (read(fd, (char *)space, m) != m)
break;
n -= m;
long savpos = lseek(fd, 0, SEEK_CUR);
if (savpos == -1) {
return -1;
}
for (nlist_type* q = space; (m -= sizeof(nlist_type)) >= 0; q++) {
char nambuf[BUFSIZ];
if (q->n_un.n_strx == 0 || q->n_type & N_STAB)
continue;
// seek to the location in the binary where the symbol
// name is stored & read it into memory
if (lseek(fd, ss+q->n_un.n_strx, SEEK_SET) == -1) {
return -1;
}
if (read(fd, nambuf, maxlen+1) == -1) {
return -1;
}
const char *s2 = nambuf;
for (nlist_type *p = list;
symbolNames[p-list] && symbolNames[p-list][0];
p++) {
// get the symbol name the user has passed in that
// corresponds to the nlist entry that we're looking at
const char *s1 = symbolNames[p - list];
while (*s1) {
if (*s1++ != *s2++)
goto cont;
}
if (*s2)
goto cont;
p->n_value = q->n_value;
p->n_type = q->n_type;
p->n_desc = q->n_desc;
p->n_sect = q->n_sect;
p->n_un.n_strx = q->n_un.n_strx;
if (--nreq == 0)
return nreq;
break;
cont: ;
}
}
if (lseek(fd, savpos, SEEK_SET) == -1) {
return -1;
}
}
return nreq;
}

View file

@ -33,11 +33,15 @@
#ifndef CLIENT_MAC_HANDLER_BREAKPAD_NLIST_H__
typedef struct nlist_64 breakpad_nlist;
#include <mach/machine.h>
int
breakpad_nlist_64(const char *name,
breakpad_nlist *list,
const char **symbolNames);
int breakpad_nlist(const char *name,
struct nlist *list,
const char **symbolNames,
cpu_type_t cpu_type);
int breakpad_nlist(const char *name,
struct nlist_64 *list,
const char **symbolNames,
cpu_type_t cpu_type);
#endif /* CLIENT_MAC_HANDLER_BREAKPAD_NLIST_H__ */

View file

@ -27,6 +27,8 @@
// (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 "client/mac/handler/dynamic_images.h"
extern "C" { // needed to compile on Leopard
#include <mach-o/nlist.h>
#include <stdlib.h>
@ -37,11 +39,17 @@ extern "C" { // needed to compile on Leopard
#include <assert.h>
#include <dlfcn.h>
#include <mach/mach_vm.h>
#include <sys/sysctl.h>
#include <algorithm>
#include "client/mac/handler/dynamic_images.h"
#include <string>
#include <vector>
namespace google_breakpad {
using std::string;
using std::vector;
//==============================================================================
// Returns the size of the memory region containing |address| and the
// number of bytes from |address| to the end of the region.
@ -51,7 +59,7 @@ namespace google_breakpad {
// straddle two vm regions.
//
static mach_vm_size_t GetMemoryRegionSize(task_port_t target_task,
const void* address,
const uint64_t address,
mach_vm_size_t *size_to_end) {
mach_vm_address_t region_base = (mach_vm_address_t)address;
mach_vm_size_t region_size;
@ -116,8 +124,8 @@ static mach_vm_size_t GetMemoryRegionSize(task_port_t target_task,
//
// Warning! This will not read any strings longer than kMaxStringLength-1
//
static void* ReadTaskString(task_port_t target_task,
const void* address) {
static string ReadTaskString(task_port_t target_task,
const uint64_t address) {
// The problem is we don't know how much to read until we know how long
// the string is. And we don't know how long the string is, until we've read
// the memory! So, we'll try to read kMaxStringLength bytes
@ -129,68 +137,140 @@ static void* ReadTaskString(task_port_t target_task,
mach_vm_size_t size_to_read =
size_to_end > kMaxStringLength ? kMaxStringLength : size_to_end;
kern_return_t kr;
return ReadTaskMemory(target_task, address, (size_t)size_to_read, &kr);
vector<uint8_t> bytes;
if (ReadTaskMemory(target_task, address, (size_t)size_to_read, bytes) !=
KERN_SUCCESS)
return string();
return string(reinterpret_cast<const char*>(&bytes[0]));
}
return NULL;
return string();
}
//==============================================================================
// 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,
kern_return_t *kr) {
void* result = NULL;
// Reads an address range from another task. The bytes read will be returned
// in bytes, which will be resized as necessary.
kern_return_t ReadTaskMemory(task_port_t target_task,
const uint64_t address,
size_t length,
vector<uint8_t> &bytes) {
int systemPageSize = getpagesize();
// use the negative of the page size for the mask to find the page address
mach_vm_address_t page_address =
reinterpret_cast<mach_vm_address_t>(address) & (-systemPageSize);
mach_vm_address_t page_address = address & (-systemPageSize);
mach_vm_address_t last_page_address =
(reinterpret_cast<mach_vm_address_t>(address) + length +
(systemPageSize - 1)) & (-systemPageSize);
(address + length + (systemPageSize - 1)) & (-systemPageSize);
mach_vm_size_t page_size = last_page_address - page_address;
uint8_t* local_start;
uint32_t local_length;
kern_return_t r;
kern_return_t r = mach_vm_read(target_task,
page_address,
page_size,
reinterpret_cast<vm_offset_t*>(&local_start),
&local_length);
r = mach_vm_read(target_task,
page_address,
page_size,
reinterpret_cast<vm_offset_t*>(&local_start),
&local_length);
if (r != KERN_SUCCESS)
return r;
if (kr != NULL) {
*kr = r;
}
if (r == KERN_SUCCESS) {
result = malloc(length);
if (result != NULL) {
memcpy(result,
&local_start[(mach_vm_address_t)address - page_address],
length);
}
mach_vm_deallocate(mach_task_self(), (uintptr_t)local_start, local_length);
}
return result;
bytes.resize(length);
memcpy(&bytes[0],
&local_start[(mach_vm_address_t)address - page_address],
length);
mach_vm_deallocate(mach_task_self(), (uintptr_t)local_start, local_length);
return KERN_SUCCESS;
}
#pragma mark -
//==============================================================================
// Traits structs for specializing function templates to handle
// 32-bit/64-bit Mach-O files.
struct MachO32 {
typedef mach_header mach_header_type;
typedef segment_command mach_segment_command_type;
typedef dyld_image_info32 dyld_image_info;
typedef dyld_all_image_infos32 dyld_all_image_infos;
typedef struct nlist nlist_type;
static const uint32_t magic = MH_MAGIC;
static const uint32_t segment_load_command = LC_SEGMENT;
};
struct MachO64 {
typedef mach_header_64 mach_header_type;
typedef segment_command_64 mach_segment_command_type;
typedef dyld_image_info64 dyld_image_info;
typedef dyld_all_image_infos64 dyld_all_image_infos;
typedef struct nlist_64 nlist_type;
static const uint32_t magic = MH_MAGIC_64;
static const uint32_t segment_load_command = LC_SEGMENT_64;
};
template<typename MachBits>
bool FindTextSection(DynamicImage& image) {
typedef typename MachBits::mach_header_type mach_header_type;
typedef typename MachBits::mach_segment_command_type
mach_segment_command_type;
const mach_header_type* header =
reinterpret_cast<const mach_header_type*>(&image.header_[0]);
if(header->magic != MachBits::magic) {
return false;
}
const struct load_command *cmd =
reinterpret_cast<const struct load_command *>(header + 1);
bool found_text_section = false;
bool found_dylib_id_command = false;
for (unsigned int i = 0; cmd && (i < header->ncmds); ++i) {
if (!found_text_section) {
if (cmd->cmd == MachBits::segment_load_command) {
const mach_segment_command_type *seg =
reinterpret_cast<const mach_segment_command_type *>(cmd);
if (!strcmp(seg->segname, "__TEXT")) {
image.vmaddr_ = seg->vmaddr;
image.vmsize_ = seg->vmsize;
image.slide_ = 0;
if (seg->fileoff == 0 && seg->filesize != 0) {
image.slide_ =
(uintptr_t)image.GetLoadAddress() - (uintptr_t)seg->vmaddr;
}
found_text_section = true;
}
}
}
if (!found_dylib_id_command) {
if (cmd->cmd == LC_ID_DYLIB) {
const struct dylib_command *dc =
reinterpret_cast<const struct dylib_command *>(cmd);
image.version_ = dc->dylib.current_version;
found_dylib_id_command = true;
}
}
if (found_dylib_id_command && found_text_section) {
return true;
}
cmd = reinterpret_cast<const struct load_command *>
(reinterpret_cast<const char *>(cmd) + cmd->cmdsize);
}
return false;
}
//==============================================================================
// Initializes vmaddr_, vmsize_, and slide_
void DynamicImage::CalculateMemoryAndVersionInfo() {
breakpad_mach_header *header = GetMachHeader();
// unless we can process the header, ensure that calls to
// IsValid() will return false
vmaddr_ = 0;
@ -198,78 +278,29 @@ void DynamicImage::CalculateMemoryAndVersionInfo() {
slide_ = 0;
version_ = 0;
bool foundTextSection = false;
bool foundDylibIDCommand = false;
#if __LP64__
if(header->magic != MH_MAGIC_64) {
return;
}
#else
if(header->magic != MH_MAGIC) {
return;
}
#endif
#ifdef __LP64__
const uint32_t segmentLoadCommand = LC_SEGMENT_64;
#else
const uint32_t segmentLoadCommand = LC_SEGMENT;
#endif
const struct load_command *cmd =
reinterpret_cast<const struct load_command *>(header + 1);
for (unsigned int i = 0; cmd && (i < header->ncmds); ++i) {
if (!foundTextSection) {
if (cmd->cmd == segmentLoadCommand) {
const breakpad_mach_segment_command *seg =
reinterpret_cast<const breakpad_mach_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;
}
foundTextSection = true;
}
}
}
if (!foundDylibIDCommand) {
if (cmd->cmd == LC_ID_DYLIB) {
const struct dylib_command *dc =
reinterpret_cast<const struct dylib_command *>(cmd);
version_ = dc->dylib.current_version;
foundDylibIDCommand = true;
}
}
if (foundDylibIDCommand && foundTextSection) {
return;
}
cmd = reinterpret_cast<const struct load_command *>
(reinterpret_cast<const char *>(cmd) + cmd->cmdsize);
}
// The function template above does all the real work.
if (Is64Bit())
FindTextSection<MachO64>(*this);
else
FindTextSection<MachO32>(*this);
}
void DynamicImage::Print() {
const char *path = GetFilePath();
if (!path) {
path = "(unknown)";
}
printf("%p: %s\n", GetLoadAddress(), path);
breakpad_mach_header *header = GetMachHeader();
MachHeader(*header).Print();
printf("vmaddr\t\t: %p\n", reinterpret_cast<void*>(GetVMAddr()));
printf("vmsize\t\t: %llu\n", GetVMSize());
printf("slide\t\t: %td\n", GetVMAddrSlide());
//==============================================================================
// The helper function template abstracts the 32/64-bit differences.
template<typename MachBits>
uint32_t GetFileTypeFromHeader(DynamicImage& image) {
typedef typename MachBits::mach_header_type mach_header_type;
const mach_header_type* header =
reinterpret_cast<const mach_header_type*>(&image.header_[0]);
return header->filetype;
}
uint32_t DynamicImage::GetFileType() {
if (Is64Bit())
return GetFileTypeFromHeader<MachO64>(*this);
return GetFileTypeFromHeader<MachO32>(*this);
}
#pragma mark -
@ -277,144 +308,158 @@ void DynamicImage::Print() {
//==============================================================================
// Loads information about dynamically loaded code in the given task.
DynamicImages::DynamicImages(mach_port_t task)
: task_(task), image_list_() {
: task_(task),
cpu_type_(DetermineTaskCPUType(task)),
image_list_() {
ReadImageInfoForTask();
}
void* DynamicImages::GetDyldAllImageInfosPointer() {
const char *imageSymbolName = "_dyld_all_image_infos";
const char *dyldPath = "/usr/lib/dyld";
#ifndef __LP64__
struct nlist l[8];
memset(l, 0, sizeof(l) );
template<typename MachBits>
static uint64_t LookupSymbol(const char* symbol_name,
const char* filename,
cpu_type_t cpu_type) {
typedef typename MachBits::nlist_type nlist_type;
// 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 = const_cast<char *>(imageSymbolName);
nlist(dyldPath,&list);
if(list.n_value) {
return reinterpret_cast<void*>(list.n_value);
}
return NULL;
#else
struct nlist_64 l[8];
struct nlist_64 &list = l[0];
memset(l, 0, sizeof(l) );
const char *symbolNames[2] = { imageSymbolName, "\0" };
int invalidEntriesCount = breakpad_nlist_64(dyldPath,&list,symbolNames);
nlist_type symbol_info[8] = {};
const char *symbolNames[2] = { symbol_name, "\0" };
nlist_type &list = symbol_info[0];
int invalidEntriesCount = breakpad_nlist(filename,
&list,
symbolNames,
cpu_type);
if(invalidEntriesCount != 0) {
return NULL;
return 0;
}
assert(list.n_value);
return reinterpret_cast<void*>(list.n_value);
#endif
assert(list.n_value);
return list.n_value;
}
uint64_t DynamicImages::GetDyldAllImageInfosPointer() {
const char *imageSymbolName = "_dyld_all_image_infos";
const char *dyldPath = "/usr/lib/dyld";
if (Is64Bit())
return LookupSymbol<MachO64>(imageSymbolName, dyldPath, cpu_type_);
return LookupSymbol<MachO32>(imageSymbolName, dyldPath, cpu_type_);
}
//==============================================================================
// This code was written using dyld_debug.c (from Darwin) as a guide.
void DynamicImages::ReadImageInfoForTask() {
void *imageList = GetDyldAllImageInfosPointer();
if (imageList) {
kern_return_t kr;
// Read the structure inside of dyld that contains information about
// loaded images. We're reading from the desired task's address space.
template<typename MachBits>
void ReadImageInfo(DynamicImages& images,
uint64_t image_list_address) {
typedef typename MachBits::dyld_image_info dyld_image_info;
typedef typename MachBits::dyld_all_image_infos dyld_all_image_infos;
typedef typename MachBits::mach_header_type mach_header_type;
// 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*>(imageList),
sizeof(dyld_all_image_infos), &kr));
// Read the structure inside of dyld that contains information about
// loaded images. We're reading from the desired task's address space.
if (dyldInfo) {
// number of loaded images
int count = dyldInfo->infoArrayCount;
// 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.
vector<uint8_t> dyld_all_info_bytes;
if (ReadTaskMemory(images.task_,
image_list_address,
sizeof(dyld_all_image_infos),
dyld_all_info_bytes) != KERN_SUCCESS)
return;
// 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), &kr));
dyld_all_image_infos *dyldInfo =
reinterpret_cast<dyld_all_image_infos*>(&dyld_all_info_bytes[0]);
image_list_.reserve(count);
// number of loaded images
int count = dyldInfo->infoArrayCount;
for (int i = 0; i < count; ++i) {
dyld_image_info &info = infoArray[i];
// Read an array of dyld_image_info structures each containing
// information about a loaded image.
vector<uint8_t> dyld_info_array_bytes;
if (ReadTaskMemory(images.task_,
dyldInfo->infoArray,
count * sizeof(dyld_image_info),
dyld_info_array_bytes) != KERN_SUCCESS)
return;
// First read just the mach_header from the image in the task.
breakpad_mach_header *header = reinterpret_cast<breakpad_mach_header*>
(ReadTaskMemory(task_,
info.load_address_,
sizeof(breakpad_mach_header), &kr));
dyld_image_info *infoArray =
reinterpret_cast<dyld_image_info*>(&dyld_info_array_bytes[0]);
images.image_list_.reserve(count);
if (!header)
break; // bail on this dynamic image
for (int i = 0; i < count; ++i) {
dyld_image_info &info = infoArray[i];
// 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.
size_t header_size =
sizeof(breakpad_mach_header) + header->sizeofcmds;
// First read just the mach_header from the image in the task.
vector<uint8_t> mach_header_bytes;
if (ReadTaskMemory(images.task_,
info.load_address_,
sizeof(mach_header_type),
mach_header_bytes) != KERN_SUCCESS)
continue; // bail on this dynamic image
free(header);
mach_header_type *header =
reinterpret_cast<mach_header_type*>(&mach_header_bytes[0]);
header = reinterpret_cast<breakpad_mach_header*>
(ReadTaskMemory(task_, info.load_address_, header_size, &kr));
// Now determine the total amount necessary to read the header
// plus all of the load commands.
size_t header_size =
sizeof(mach_header_type) + header->sizeofcmds;
// Read the file name from the task's memory space.
char *file_path = NULL;
if (info.file_path_) {
// Although we're reading kMaxStringLength bytes, it's copied in the
// the DynamicImage constructor below with the correct string length,
// so it's not really wasting memory.
file_path = reinterpret_cast<char*>
(ReadTaskString(task_, info.file_path_));
}
if (ReadTaskMemory(images.task_,
info.load_address_,
header_size,
mach_header_bytes) != KERN_SUCCESS)
continue;
// Create an object representing this image and add it to our list.
DynamicImage *new_image;
new_image = new DynamicImage(header,
header_size,
(breakpad_mach_header*)info.load_address_,
file_path,
info.file_mod_date_,
task_);
header = reinterpret_cast<mach_header_type*>(&mach_header_bytes[0]);
if (new_image->IsValid()) {
image_list_.push_back(DynamicImageRef(new_image));
} else {
delete new_image;
}
if (file_path) {
free(file_path);
}
// Read the file name from the task's memory space.
string file_path;
if (info.file_path_) {
// Although we're reading kMaxStringLength bytes, it's copied in the
// the DynamicImage constructor below with the correct string length,
// so it's not really wasting memory.
file_path = ReadTaskString(images.task_, info.file_path_);
}
free(dyldInfo);
free(infoArray);
// Create an object representing this image and add it to our list.
DynamicImage *new_image;
new_image = new DynamicImage(&mach_header_bytes[0],
header_size,
info.load_address_,
file_path,
info.file_mod_date_,
images.task_,
images.cpu_type_);
// sorts based on loading address
sort(image_list_.begin(), image_list_.end() );
// remove duplicates - this happens in certain strange cases
// You can see it in DashboardClient when Google Gadgets plugin
// is installed. Apple's crash reporter log and gdb "info shared"
// both show the same library multiple times at the same address
vector<DynamicImageRef>::iterator it = unique(image_list_.begin(),
image_list_.end() );
image_list_.erase(it, image_list_.end());
if (new_image->IsValid()) {
images.image_list_.push_back(DynamicImageRef(new_image));
} else {
delete new_image;
}
}
// sorts based on loading address
sort(images.image_list_.begin(), images.image_list_.end());
// remove duplicates - this happens in certain strange cases
// You can see it in DashboardClient when Google Gadgets plugin
// is installed. Apple's crash reporter log and gdb "info shared"
// both show the same library multiple times at the same address
vector<DynamicImageRef>::iterator it = unique(images.image_list_.begin(),
images.image_list_.end());
images.image_list_.erase(it, images.image_list_.end());
}
void DynamicImages::ReadImageInfoForTask() {
uint64_t imageList = GetDyldAllImageInfosPointer();
if (imageList) {
if (Is64Bit())
ReadImageInfo<MachO64>(*this, imageList);
else
ReadImageInfo<MachO32>(*this, imageList);
}
}
@ -436,7 +481,7 @@ int DynamicImages::GetExecutableImageIndex() {
for (int i = 0; i < image_count; ++i) {
DynamicImage *image = GetImage(i);
if (image->GetMachHeader()->filetype == MH_EXECUTE) {
if (image->GetFileType() == MH_EXECUTE) {
return i;
}
}
@ -444,4 +489,27 @@ int DynamicImages::GetExecutableImageIndex() {
return -1;
}
//==============================================================================
// static
cpu_type_t DynamicImages::DetermineTaskCPUType(task_t task) {
if (task == mach_task_self())
return GetNativeCPUType();
int mib[CTL_MAXNAME];
size_t mibLen = CTL_MAXNAME;
int err = sysctlnametomib("sysctl.proc_cputype", mib, &mibLen);
if (err == 0) {
assert(mibLen < CTL_MAXNAME);
pid_for_task(task, &mib[mibLen]);
mibLen += 1;
cpu_type_t cpu_type;
size_t cpuTypeSize = sizeof(cpu_type);
sysctl(mib, mibLen, &cpu_type, &cpuTypeSize, 0, 0);
return cpu_type;
}
return GetNativeCPUType();
}
} // namespace google_breakpad

View file

@ -41,32 +41,49 @@
#include <mach-o/dyld.h>
#include <mach-o/loader.h>
#include <sys/types.h>
#include <string>
#include <vector>
namespace google_breakpad {
using std::string;
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;
typedef struct dyld_image_info32 {
uint32_t load_address_; // struct mach_header*
uint32_t file_path_; // char*
uint32_t file_mod_date_;
} dyld_image_info32;
typedef struct dyld_image_info64 {
uint64_t load_address_; // struct mach_header*
uint64_t file_path_; // char*
uint64_t file_mod_date_;
} dyld_image_info64;
//==============================================================================
// 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 {
typedef struct dyld_all_image_infos32 {
uint32_t version; // == 1 in Mac OS X 10.4
uint32_t infoArrayCount;
const struct dyld_image_info *infoArray;
void* notification;
uint32_t infoArray; // const struct dyld_image_info*
uint32_t notification;
bool processDetachedFromSharedRegion;
} dyld_all_image_infos;
} dyld_all_image_infos32;
typedef struct dyld_all_image_infos64 {
uint32_t version; // == 1 in Mac OS X 10.4
uint32_t infoArrayCount;
uint64_t infoArray; // const struct dyld_image_info*
uint64_t notification;
bool processDetachedFromSharedRegion;
} dyld_all_image_infos64;
// some typedefs to isolate 64/32 bit differences
#ifdef __LP64__
@ -77,71 +94,49 @@ typedef mach_header breakpad_mach_header;
typedef segment_command breakpad_mach_segment_command;
#endif
//==============================================================================
// A simple wrapper for a mach_header
//
// This could be fleshed out with some more interesting methods.
class MachHeader {
public:
explicit MachHeader(const breakpad_mach_header &header) : header_(header) {}
// Helper functions to deal with 32-bit/64-bit Mach-O differences.
class DynamicImage;
template<typename MachBits>
bool FindTextSection(DynamicImage& image);
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);
}
breakpad_mach_header header_;
};
template<typename MachBits>
uint32_t GetFileTypeFromHeader(DynamicImage& image);
//==============================================================================
// Represents a single dynamically loaded mach-o image
class DynamicImage {
public:
DynamicImage(breakpad_mach_header *header, // we take ownership
size_t header_size, // includes load commands
breakpad_mach_header *load_address,
char *inFilePath,
DynamicImage(uint8_t *header, // data is copied
size_t header_size, // includes load commands
uint64_t load_address,
string file_path,
uintptr_t image_mod_date,
mach_port_t task)
: header_(header),
mach_port_t task,
cpu_type_t cpu_type)
: header_(header, header + header_size),
header_size_(header_size),
load_address_(load_address),
vmaddr_(0),
vmsize_(0),
slide_(0),
version_(0),
file_path_(NULL),
file_path_(file_path),
file_mod_date_(image_mod_date),
task_(task) {
InitializeFilePath(inFilePath);
task_(task),
cpu_type_(cpu_type) {
CalculateMemoryAndVersionInfo();
}
~DynamicImage() {
if (file_path_) {
free(file_path_);
}
free(header_);
}
// Returns pointer to a local copy of the mach_header plus load commands
breakpad_mach_header *GetMachHeader() {return header_;}
// Size of mach_header plus load commands
size_t GetHeaderSize() const {return header_size_;}
size_t GetHeaderSize() const {return header_.size();}
// Full path to mach-o binary
char *GetFilePath() {return file_path_;}
string GetFilePath() {return file_path_;}
uintptr_t GetModDate() const {return file_mod_date_;}
uint64_t GetModDate() const {return file_mod_date_;}
// Actual address where the image was loaded
breakpad_mach_header *GetLoadAddress() const {return load_address_;}
uint64_t GetLoadAddress() const {return load_address_;}
// Address where the image should be loaded
mach_vm_address_t GetVMAddr() const {return vmaddr_;}
@ -155,49 +150,49 @@ class DynamicImage {
// Task owning this loaded image
mach_port_t GetTask() {return task_;}
// CPU type of the task
cpu_type_t GetCPUType() {return cpu_type_;}
// filetype from the Mach-O header.
uint32_t GetFileType();
// Return true if the task is a 64-bit architecture.
bool Is64Bit() { return (GetCPUType() & CPU_ARCH_ABI64) == CPU_ARCH_ABI64; }
uint32_t GetVersion() {return version_;}
// For sorting
bool operator<(const DynamicImage &inInfo) {
return GetLoadAddress() < inInfo.GetLoadAddress();
}
// Debugging
void Print();
// Sanity checking
bool IsValid() {return GetVMSize() != 0;}
private:
DynamicImage(const DynamicImage &);
DynamicImage &operator=(const DynamicImage &);
friend class DynamicImages;
// Sanity checking
bool IsValid() {return GetVMSize() != 0;}
// Makes local copy of file path to mach-o binary
void InitializeFilePath(char *inFilePath) {
if (inFilePath) {
size_t path_size = 1 + strlen(inFilePath);
file_path_ = reinterpret_cast<char*>(malloc(path_size));
strlcpy(file_path_, inFilePath, path_size);
} else {
file_path_ = NULL;
}
}
template<typename MachBits>
friend bool FindTextSection(DynamicImage& image);
template<typename MachBits>
friend uint32_t GetFileTypeFromHeader(DynamicImage& image);
// Initializes vmaddr_, vmsize_, and slide_
void CalculateMemoryAndVersionInfo();
breakpad_mach_header *header_; // our local copy of the header
const vector<uint8_t> header_; // our local copy of the header
size_t header_size_; // mach_header plus load commands
breakpad_mach_header *load_address_; // base address image is mapped into
uint64_t load_address_; // base address image is mapped into
mach_vm_address_t vmaddr_;
mach_vm_size_t vmsize_;
ptrdiff_t slide_;
uint32_t version_; // Dylib version
char *file_path_; // path dyld used to load the image
string file_path_; // path dyld used to load the image
uintptr_t file_mod_date_; // time_t of image file
mach_port_t task_;
cpu_type_t cpu_type_; // CPU type of task_
};
//==============================================================================
@ -230,6 +225,11 @@ class DynamicImageRef {
DynamicImage *p;
};
// Helper function to deal with 32-bit/64-bit Mach-O differences.
class DynamicImages;
template<typename MachBits>
void ReadImageInfo(DynamicImages& images, uint64_t image_list_address);
//==============================================================================
// An object of type DynamicImages may be created to allow introspection of
// an arbitrary task's dynamically loaded mach-o binaries. This makes the
@ -262,43 +262,51 @@ class DynamicImages {
// Returns the task which we're looking at.
mach_port_t GetTask() const {return task_;}
// Debugging
void Print() {
for (int i = 0; i < GetImageCount(); ++i) {
image_list_[i]->Print();
}
}
// CPU type of the task
cpu_type_t GetCPUType() {return cpu_type_;}
void TestPrint() {
const breakpad_mach_header *header;
for (int i = 0; i < GetImageCount(); ++i) {
printf("dyld: %p: name = %s\n", _dyld_get_image_header(i),
_dyld_get_image_name(i) );
// Return true if the task is a 64-bit architecture.
bool Is64Bit() { return (GetCPUType() & CPU_ARCH_ABI64) == CPU_ARCH_ABI64; }
const void *imageHeader = _dyld_get_image_header(i);
header = reinterpret_cast<const breakpad_mach_header*>(imageHeader);
// Determine the CPU type of the task being dumped.
static cpu_type_t DetermineTaskCPUType(task_t task);
MachHeader(*header).Print();
}
// Get the native CPU type of this task.
static cpu_type_t GetNativeCPUType() {
#if defined(__i386__)
return CPU_TYPE_I386;
#elif defined(__x86_64__)
return CPU_TYPE_X86_64;
#elif defined(__ppc__)
return CPU_TYPE_POWERPC;
#elif defined(__ppc64__)
return CPU_TYPE_POWERPC64;
#else
#error "GetNativeCPUType not implemented for this architecture"
#endif
}
private:
template<typename MachBits>
friend void ReadImageInfo(DynamicImages& images, uint64_t image_list_address);
bool IsOurTask() {return task_ == mach_task_self();}
// Initialization
void ReadImageInfoForTask();
void* GetDyldAllImageInfosPointer();
uint64_t GetDyldAllImageInfosPointer();
mach_port_t task_;
cpu_type_t cpu_type_; // CPU type of task_
vector<DynamicImageRef> image_list_;
};
// Returns a malloced block containing the contents of memory at a particular
// Fill bytes with the contents of memory at a particular
// location in another task.
void* ReadTaskMemory(task_port_t target_task,
const void* address,
size_t len,
kern_return_t *kr);
kern_return_t ReadTaskMemory(task_port_t target_task,
const uint64_t address,
size_t length,
vector<uint8_t> &bytes);
} // namespace google_breakpad

View file

@ -31,7 +31,9 @@
#include <cstdio>
#include <mach/host_info.h>
#include <mach/i386/thread_status.h>
#include <mach/mach_vm.h>
#include <mach/ppc/thread_status.h>
#include <mach/vm_statistics.h>
#include <mach-o/dyld.h>
#include <mach-o/loader.h>
@ -65,6 +67,7 @@ MinidumpGenerator::MinidumpGenerator()
exception_thread_(0),
crashing_task_(mach_task_self()),
handler_thread_(mach_thread_self()),
cpu_type_(DynamicImages::GetNativeCPUType()),
dynamic_images_(NULL),
memory_blocks_(&allocator_) {
GatherSystemInformation();
@ -81,12 +84,15 @@ MinidumpGenerator::MinidumpGenerator(mach_port_t crashing_task,
exception_thread_(0),
crashing_task_(crashing_task),
handler_thread_(handler_thread),
cpu_type_(DynamicImages::GetNativeCPUType()),
dynamic_images_(NULL),
memory_blocks_(&allocator_) {
if (crashing_task != mach_task_self()) {
dynamic_images_ = new DynamicImages(crashing_task_);
cpu_type_ = dynamic_images_->GetCPUType();
} else {
dynamic_images_ = NULL;
cpu_type_ = DynamicImages::GetNativeCPUType();
}
GatherSystemInformation();
@ -254,8 +260,10 @@ size_t MinidumpGenerator::CalculateStackSize(mach_vm_address_t start_addr) {
return 0;
}
if ((stack_region_base + stack_region_size) == TOP_OF_THREAD0_STACK) {
if (((cpu_type_ & CPU_ARCH_ABI64) &&
(stack_region_base + stack_region_size) == TOP_OF_THREAD0_STACK_64BIT) ||
(!(cpu_type_ & CPU_ARCH_ABI64) &&
(stack_region_base + stack_region_size) == TOP_OF_THREAD0_STACK_32BIT)) {
// The stack for thread 0 needs to extend all the way to
// 0xc0000000 on 32 bit and 00007fff5fc00000 on 64bit. HOWEVER,
// for many processes, the stack is first created in one page
@ -305,20 +313,15 @@ bool MinidumpGenerator::WriteStackFromStartAddress(
return false;
if (dynamic_images_) {
kern_return_t kr;
void *stack_memory = ReadTaskMemory(crashing_task_,
(void*)start_addr,
size,
&kr);
if (stack_memory == NULL) {
vector<uint8_t> stack_memory;
if (ReadTaskMemory(crashing_task_,
start_addr,
size,
stack_memory) != KERN_SUCCESS) {
return false;
}
result = memory.Copy(stack_memory, size);
free(stack_memory);
result = memory.Copy(&stack_memory[0], size);
} else {
result = memory.Copy(reinterpret_cast<const void *>(start_addr), size);
}
@ -330,34 +333,99 @@ bool MinidumpGenerator::WriteStackFromStartAddress(
return result;
}
#if TARGET_CPU_PPC || TARGET_CPU_PPC64
bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location) {
breakpad_thread_state_t *machine_state =
reinterpret_cast<breakpad_thread_state_t *>(state);
switch (cpu_type_) {
case CPU_TYPE_POWERPC:
return WriteStackPPC(state, stack_location);
case CPU_TYPE_POWERPC64:
return WriteStackPPC64(state, stack_location);
case CPU_TYPE_I386:
return WriteStackX86(state, stack_location);
case CPU_TYPE_X86_64:
return WriteStackX86_64(state, stack_location);
default:
return false;
}
}
bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location) {
switch (cpu_type_) {
case CPU_TYPE_POWERPC:
return WriteContextPPC(state, register_location);
case CPU_TYPE_POWERPC64:
return WriteContextPPC64(state, register_location);
case CPU_TYPE_I386:
return WriteContextX86(state, register_location);
case CPU_TYPE_X86_64:
return WriteContextX86_64(state, register_location);
default:
return false;
}
}
u_int64_t MinidumpGenerator::CurrentPCForStack(
breakpad_thread_state_data_t state) {
switch (cpu_type_) {
case CPU_TYPE_POWERPC:
return CurrentPCForStackPPC(state);
case CPU_TYPE_POWERPC64:
return CurrentPCForStackPPC64(state);
case CPU_TYPE_I386:
return CurrentPCForStackX86(state);
case CPU_TYPE_X86_64:
return CurrentPCForStackX86_64(state);
default:
assert("Unknown CPU type!");
return 0;
}
}
bool MinidumpGenerator::WriteStackPPC(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location) {
ppc_thread_state_t *machine_state =
reinterpret_cast<ppc_thread_state_t *>(state);
mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, r1);
return WriteStackFromStartAddress(start_addr, stack_location);
}
bool MinidumpGenerator::WriteStackPPC64(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location) {
ppc_thread_state64_t *machine_state =
reinterpret_cast<ppc_thread_state64_t *>(state);
mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, r1);
return WriteStackFromStartAddress(start_addr, stack_location);
}
u_int64_t
MinidumpGenerator::CurrentPCForStack(breakpad_thread_state_data_t state) {
breakpad_thread_state_t *machine_state =
reinterpret_cast<breakpad_thread_state_t *>(state);
MinidumpGenerator::CurrentPCForStackPPC(breakpad_thread_state_data_t state) {
ppc_thread_state_t *machine_state =
reinterpret_cast<ppc_thread_state_t *>(state);
return REGISTER_FROM_THREADSTATE(machine_state, srr0);
}
bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location) {
TypedMDRVA<MinidumpContext> context(&writer_);
breakpad_thread_state_t *machine_state =
reinterpret_cast<breakpad_thread_state_t *>(state);
u_int64_t
MinidumpGenerator::CurrentPCForStackPPC64(breakpad_thread_state_data_t state) {
ppc_thread_state64_t *machine_state =
reinterpret_cast<ppc_thread_state64_t *>(state);
return REGISTER_FROM_THREADSTATE(machine_state, srr0);
}
bool MinidumpGenerator::WriteContextPPC(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location)
{
TypedMDRVA<MDRawContextPPC> context(&writer_);
ppc_thread_state_t *machine_state =
reinterpret_cast<ppc_thread_state_t *>(state);
if (!context.Allocate())
return false;
*register_location = context.location();
MinidumpContext *context_ptr = context.get();
MDRawContextPPC *context_ptr = context.get();
context_ptr->context_flags = MD_CONTEXT_PPC_BASE;
#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a)
@ -402,57 +470,124 @@ bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
AddGPR(29);
AddGPR(30);
AddGPR(31);
#if TARGET_CPU_PPC
/* The mq register is only for PPC */
AddReg(mq);
#endif
#undef AddReg
#undef AddGPR
return true;
}
#elif TARGET_CPU_X86 || TARGET_CPU_X86_64
bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location) {
breakpad_thread_state_t *machine_state =
reinterpret_cast<breakpad_thread_state_t *>(state);
#if TARGET_CPU_X86_64
mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, rsp);
#else
mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, esp);
#endif
return WriteStackFromStartAddress(start_addr, stack_location);
}
u_int64_t
MinidumpGenerator::CurrentPCForStack(breakpad_thread_state_data_t state) {
breakpad_thread_state_t *machine_state =
reinterpret_cast<breakpad_thread_state_t *>(state);
#if TARGET_CPU_X86_64
return REGISTER_FROM_THREADSTATE(machine_state, rip);
#else
return REGISTER_FROM_THREADSTATE(machine_state, eip);
#endif
}
bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location) {
TypedMDRVA<MinidumpContext> context(&writer_);
breakpad_thread_state_t *machine_state =
reinterpret_cast<breakpad_thread_state_t *>(state);
bool MinidumpGenerator::WriteContextPPC64(
breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location) {
TypedMDRVA<MDRawContextPPC64> context(&writer_);
ppc_thread_state64_t *machine_state =
reinterpret_cast<ppc_thread_state64_t *>(state);
if (!context.Allocate())
return false;
*register_location = context.location();
MinidumpContext *context_ptr = context.get();
MDRawContextPPC64 *context_ptr = context.get();
context_ptr->context_flags = MD_CONTEXT_PPC_BASE;
#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a)
#if TARGET_CPU_X86
#define AddGPR(a) context_ptr->gpr[a] = REGISTER_FROM_THREADSTATE(machine_state, r ## a)
AddReg(srr0);
AddReg(cr);
AddReg(xer);
AddReg(ctr);
AddReg(lr);
AddReg(vrsave);
AddGPR(0);
AddGPR(1);
AddGPR(2);
AddGPR(3);
AddGPR(4);
AddGPR(5);
AddGPR(6);
AddGPR(7);
AddGPR(8);
AddGPR(9);
AddGPR(10);
AddGPR(11);
AddGPR(12);
AddGPR(13);
AddGPR(14);
AddGPR(15);
AddGPR(16);
AddGPR(17);
AddGPR(18);
AddGPR(19);
AddGPR(20);
AddGPR(21);
AddGPR(22);
AddGPR(23);
AddGPR(24);
AddGPR(25);
AddGPR(26);
AddGPR(27);
AddGPR(28);
AddGPR(29);
AddGPR(30);
AddGPR(31);
#undef AddReg
#undef AddGPR
return true;
}
bool MinidumpGenerator::WriteStackX86(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location) {
i386_thread_state_t *machine_state =
reinterpret_cast<i386_thread_state_t *>(state);
mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, esp);
return WriteStackFromStartAddress(start_addr, stack_location);
}
bool MinidumpGenerator::WriteStackX86_64(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location) {
x86_thread_state64_t *machine_state =
reinterpret_cast<x86_thread_state64_t *>(state);
mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, rsp);
return WriteStackFromStartAddress(start_addr, stack_location);
}
u_int64_t
MinidumpGenerator::CurrentPCForStackX86(breakpad_thread_state_data_t state) {
i386_thread_state_t *machine_state =
reinterpret_cast<i386_thread_state_t *>(state);
return REGISTER_FROM_THREADSTATE(machine_state, eip);
}
u_int64_t
MinidumpGenerator::CurrentPCForStackX86_64(breakpad_thread_state_data_t state) {
x86_thread_state64_t *machine_state =
reinterpret_cast<x86_thread_state64_t *>(state);
return REGISTER_FROM_THREADSTATE(machine_state, rip);
}
bool MinidumpGenerator::WriteContextX86(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location)
{
TypedMDRVA<MDRawContextX86> context(&writer_);
i386_thread_state_t *machine_state =
reinterpret_cast<i386_thread_state_t *>(state);
if (!context.Allocate())
return false;
*register_location = context.location();
MDRawContextX86 *context_ptr = context.get();
#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a)
context_ptr->context_flags = MD_CONTEXT_X86;
AddReg(eax);
AddReg(ebx);
@ -472,7 +607,26 @@ bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
AddReg(eflags);
AddReg(eip);
#else
#undef AddReg(a)
return true;
}
bool MinidumpGenerator::WriteContextX86_64(
breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location) {
TypedMDRVA<MDRawContextAMD64> context(&writer_);
x86_thread_state64_t *machine_state =
reinterpret_cast<x86_thread_state64_t *>(state);
if (!context.Allocate())
return false;
*register_location = context.location();
MDRawContextAMD64 *context_ptr = context.get();
#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a)
context_ptr->context_flags = MD_CONTEXT_AMD64;
AddReg(rax);
AddReg(rbx);
@ -495,16 +649,38 @@ bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
// not used in the flags register. Since the minidump format
// specifies 32 bits for the flags register, we can truncate safely
// with no loss.
context_ptr->eflags = static_cast<u_int32_t>(machine_state->__rflags);
context_ptr->eflags = static_cast<u_int32_t>(REGISTER_FROM_THREADSTATE(machine_state, rflags));
AddReg(cs);
AddReg(fs);
AddReg(gs);
#endif
#undef AddReg(a)
return true;
}
#endif
bool MinidumpGenerator::GetThreadState(thread_act_t target_thread,
thread_state_t state,
mach_msg_type_number_t *count) {
thread_state_flavor_t flavor;
switch (cpu_type_) {
case CPU_TYPE_POWERPC:
flavor = PPC_THREAD_STATE;
break;
case CPU_TYPE_POWERPC64:
flavor = PPC_THREAD_STATE64;
break;
case CPU_TYPE_I386:
flavor = i386_THREAD_STATE;
break;
case CPU_TYPE_X86_64:
flavor = x86_THREAD_STATE64;
break;
default:
return false;
}
return thread_get_state(target_thread, flavor,
state, count) == KERN_SUCCESS;
}
bool MinidumpGenerator::WriteThreadStream(mach_port_t thread_id,
MDRawThread *thread) {
@ -512,9 +688,7 @@ bool MinidumpGenerator::WriteThreadStream(mach_port_t thread_id,
mach_msg_type_number_t state_count
= static_cast<mach_msg_type_number_t>(sizeof(state));
if (thread_get_state(thread_id, BREAKPAD_MACHINE_THREAD_STATE,
state, &state_count) ==
KERN_SUCCESS) {
if (GetThreadState(thread_id, state, &state_count)) {
if (!WriteStack(state, &thread->stack))
return false;
@ -653,21 +827,15 @@ bool MinidumpGenerator::WriteMemoryListStream(
if (dynamic_images_) {
// Out-of-process.
kern_return_t kr;
void *memory =
ReadTaskMemory(
crashing_task_,
reinterpret_cast<const void *>(ip_memory_d.start_of_memory_range),
ip_memory_d.memory.data_size,
&kr);
if (memory == NULL) {
vector<uint8_t> memory;
if (ReadTaskMemory(crashing_task_,
ip_memory_d.start_of_memory_range,
ip_memory_d.memory.data_size,
memory) != KERN_SUCCESS) {
return false;
}
ip_memory.Copy(memory, ip_memory_d.memory.data_size);
free(memory);
ip_memory.Copy(&memory[0], ip_memory_d.memory.data_size);
} else {
// In-process, just copy from local memory.
ip_memory.Copy(
@ -733,23 +901,23 @@ bool MinidumpGenerator::WriteSystemInfoStream(
system_info_stream->location = info.location();
// CPU Information
uint32_t cpu_type;
size_t len = sizeof(cpu_type);
sysctlbyname("hw.cputype", &cpu_type, &len, NULL, 0);
uint32_t number_of_processors;
len = sizeof(number_of_processors);
size_t len = sizeof(number_of_processors);
sysctlbyname("hw.ncpu", &number_of_processors, &len, NULL, 0);
MDRawSystemInfo *info_ptr = info.get();
switch (cpu_type) {
switch (cpu_type_) {
case CPU_TYPE_POWERPC:
case CPU_TYPE_POWERPC64:
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_PPC;
break;
case CPU_TYPE_I386:
case CPU_TYPE_X86_64:
// hw.cputype is currently always I386 even on an x86-64 system
if (cpu_type_ == CPU_TYPE_I386)
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_X86;
else
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_AMD64;
#ifdef __i386__
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_X86;
// ebx is used for PIC code, so we need
// to preserve it.
#define cpuid(op,eax,ebx,ecx,edx) \
@ -763,7 +931,7 @@ bool MinidumpGenerator::WriteSystemInfoStream(
"=d" (edx) \
: "0" (op))
#elif defined(__x86_64__)
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_AMD64;
#define cpuid(op,eax,ebx,ecx,edx) \
asm ("cpuid \n\t" \
: "=a" (eax), \
@ -837,19 +1005,12 @@ bool MinidumpGenerator::WriteModuleStream(unsigned int index,
if (!image)
return false;
const breakpad_mach_header *header = image->GetMachHeader();
if (!header)
return false;
int cpu_type = header->cputype;
memset(module, 0, sizeof(MDRawModule));
MDLocationDescriptor string_location;
const char* name = image->GetFilePath();
if (!writer_.WriteString(name, 0, &string_location))
string name = image->GetFilePath();
if (!writer_.WriteString(name.c_str(), 0, &string_location))
return false;
module->base_of_image = image->GetVMAddr() + image->GetVMAddrSlide();
@ -876,12 +1037,11 @@ bool MinidumpGenerator::WriteModuleStream(unsigned int index,
module->version_info.file_version_lo |= (modVersion & 0xff);
}
if (!WriteCVRecord(module, cpu_type, name)) {
if (!WriteCVRecord(module, image->GetCPUType(), name.c_str())) {
return false;
}
} else {
// we're getting module info in the crashed process
// Getting module info in the crashed process
const breakpad_mach_header *header;
header = (breakpad_mach_header*)_dyld_get_image_header(index);
if (!header)
@ -903,7 +1063,7 @@ bool MinidumpGenerator::WriteModuleStream(unsigned int index,
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);
reinterpret_cast<const struct load_command *>(header + 1);
memset(module, 0, sizeof(MDRawModule));
@ -911,7 +1071,7 @@ bool MinidumpGenerator::WriteModuleStream(unsigned int index,
if (cmd->cmd == LC_SEGMENT_ARCH) {
const breakpad_mach_segment_command *seg =
reinterpret_cast<const breakpad_mach_segment_command *>(cmd);
reinterpret_cast<const breakpad_mach_segment_command *>(cmd);
if (!strcmp(seg->segname, "__TEXT")) {
MDLocationDescriptor string_location;

View file

@ -47,25 +47,8 @@ namespace google_breakpad {
using std::string;
#if TARGET_CPU_X86_64 || TARGET_CPU_PPC64
#define TOP_OF_THREAD0_STACK 0x00007fff5fbff000
#else
#define TOP_OF_THREAD0_STACK 0xbffff000
#endif
#if TARGET_CPU_X86_64
typedef x86_thread_state64_t breakpad_thread_state_t;
typedef MDRawContextAMD64 MinidumpContext;
#elif TARGET_CPU_X86
typedef i386_thread_state_t breakpad_thread_state_t;
typedef MDRawContextX86 MinidumpContext;
#elif TARGET_CPU_PPC64
typedef ppc_thread_state64_t breakpad_thread_state_t;
typedef MDRawContextPPC64 MinidumpContext;
#elif TARGET_CPU_PPC
typedef ppc_thread_state_t breakpad_thread_state_t;
typedef MDRawContextPPC MinidumpContext;
#endif
const u_int64_t TOP_OF_THREAD0_STACK_64BIT = 0x00007fff5fbff000LL;
const u_int32_t TOP_OF_THREAD0_STACK_32BIT = 0xbffff000;
// Use the REGISTER_FROM_THREADSTATE to access a register name from the
// breakpad_thread_state_t structure.
@ -129,6 +112,8 @@ class MinidumpGenerator {
// Helpers
u_int64_t CurrentPCForStack(breakpad_thread_state_data_t state);
bool GetThreadState(thread_act_t target_thread, thread_state_t state,
mach_msg_type_number_t *count);
bool WriteStackFromStartAddress(mach_vm_address_t start_addr,
MDMemoryDescriptor *stack_location);
bool WriteStack(breakpad_thread_state_data_t state,
@ -139,11 +124,31 @@ class MinidumpGenerator {
bool WriteCVRecord(MDRawModule *module, int cpu_type,
const char *module_path);
bool WriteModuleStream(unsigned int index, MDRawModule *module);
size_t CalculateStackSize(mach_vm_address_t start_addr);
int FindExecutableModule();
// Per-CPU implementations of these methods
bool WriteStackPPC(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location);
bool WriteContextPPC(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location);
u_int64_t CurrentPCForStackPPC(breakpad_thread_state_data_t state);
bool WriteStackPPC64(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location);
bool WriteContextPPC64(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location);
u_int64_t CurrentPCForStackPPC64(breakpad_thread_state_data_t state);
bool WriteStackX86(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location);
bool WriteContextX86(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location);
u_int64_t CurrentPCForStackX86(breakpad_thread_state_data_t state);
bool WriteStackX86_64(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location);
bool WriteContextX86_64(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location);
u_int64_t CurrentPCForStackX86_64(breakpad_thread_state_data_t state);
// disallow copy ctor and operator=
explicit MinidumpGenerator(const MinidumpGenerator &);
void operator=(const MinidumpGenerator &);
@ -158,6 +163,9 @@ class MinidumpGenerator {
mach_port_t exception_thread_;
mach_port_t crashing_task_;
mach_port_t handler_thread_;
// CPU type of the task being dumped.
cpu_type_t cpu_type_;
// System information
static char build_string_[16];

View file

@ -29,10 +29,21 @@
// minidump_generator_test.cc: Unit tests for google_breakpad::MinidumpGenerator
#include <AvailabilityMacros.h>
#ifndef MAC_OS_X_VERSION_10_6
#define MAC_OS_X_VERSION_10_6 1060
#endif
#include <crt_externs.h>
#include <mach-o/dyld.h>
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6
#include <spawn.h>
#endif
#include <sys/stat.h>
#include <unistd.h>
#include <string>
#include <vector>
#include "breakpad_googletest_includes.h"
#include "client/mac/handler/minidump_generator.h"
#include "client/mac/tests/auto_tempdir.h"
@ -48,6 +59,7 @@ std::ostringstream info_log;
namespace {
using std::string;
using std::vector;
using google_breakpad::AutoTempDir;
using google_breakpad::MinidumpGenerator;
using google_breakpad::MachPortSender;
@ -161,7 +173,7 @@ TEST_F(MinidumpGeneratorTest, OutOfProcess) {
const int kTimeoutMs = 2000;
// Create a mach port to receive the child task on.
char machPortName[128];
sprintf(machPortName, "MinidumpGeneratorTest.%d", getpid());
sprintf(machPortName, "MinidumpGeneratorTest.OutOfProcess.%d", getpid());
ReceivePort parent_recv_port(machPortName);
// Give the child process a pipe to block on.
@ -248,4 +260,150 @@ TEST_F(MinidumpGeneratorTest, OutOfProcess) {
EXPECT_EQ(GetExecutablePath(), main_module->code_file());
}
static string GetHelperPath() {
string helper_path(GetExecutablePath());
size_t pos = helper_path.rfind('/');
if (pos == string::npos)
return "";
helper_path.erase(pos + 1);
helper_path += "minidump_generator_test_helper";
return helper_path;
}
// This test fails on 10.5, but I don't have easy access to a 10.5 machine,
// so it's simpler to just limit it to 10.6 for now.
#if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6) && \
(defined(__x86_64__) || defined(__i386__))
static pid_t spawn_child_process(const char** argv) {
posix_spawnattr_t spawnattr;
if (posix_spawnattr_init(&spawnattr) != 0)
return (pid_t)-1;
cpu_type_t pref_cpu_types[2] = {
#if defined(__x86_64__)
CPU_TYPE_X86,
#elif defined(__i386__)
CPU_TYPE_X86_64,
#endif
CPU_TYPE_ANY
};
// Set spawn attributes.
size_t attr_count = sizeof(pref_cpu_types) / sizeof(pref_cpu_types[0]);
size_t attr_ocount = 0;
if (posix_spawnattr_setbinpref_np(&spawnattr,
attr_count,
pref_cpu_types,
&attr_ocount) != 0 ||
attr_ocount != attr_count) {
posix_spawnattr_destroy(&spawnattr);
return (pid_t)-1;
}
// Create an argv array.
vector<char*> argv_v;
while (*argv) {
argv_v.push_back(strdup(*argv));
argv++;
}
argv_v.push_back(NULL);
pid_t new_pid = 0;
int result = posix_spawnp(&new_pid, argv_v[0], NULL, &spawnattr,
&argv_v[0], *_NSGetEnviron());
posix_spawnattr_destroy(&spawnattr);
for (unsigned i = 0; i < argv_v.size(); i++) {
free(argv_v[i]);
}
return result == 0 ? new_pid : -1;
}
TEST_F(MinidumpGeneratorTest, CrossArchitectureDump) {
const int kTimeoutMs = 5000;
// Create a mach port to receive the child task on.
char machPortName[128];
sprintf(machPortName,
"MinidumpGeneratorTest.CrossArchitectureDump.%d", getpid());
ReceivePort parent_recv_port(machPortName);
// Spawn a child process to dump.
string helper_path = GetHelperPath();
const char* argv[] = {
helper_path.c_str(),
machPortName,
NULL
};
pid_t pid = spawn_child_process(argv);
ASSERT_NE(-1, pid);
// Read the child's task port.
MachReceiveMessage child_message;
ASSERT_EQ(KERN_SUCCESS,
parent_recv_port.WaitForMessage(&child_message, kTimeoutMs));
mach_port_t child_task = child_message.GetTranslatedPort(0);
ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_task);
// Write a minidump of the child process.
MinidumpGenerator generator(child_task, MACH_PORT_NULL);
string dump_filename = MinidumpGenerator::UniqueNameInDirectory(tempDir.path,
NULL);
ASSERT_TRUE(generator.Write(dump_filename.c_str()));
// Ensure that minidump file exists and is > 0 bytes.
struct stat st;
ASSERT_EQ(0, stat(dump_filename.c_str(), &st));
ASSERT_LT(0, st.st_size);
// Kill child process.
kill(pid, SIGKILL);
int ret;
ASSERT_EQ(pid, waitpid(pid, &ret, 0));
const MDCPUArchitecture kExpectedArchitecture =
#if defined(__x86_64__)
MD_CPU_ARCHITECTURE_X86
#elif defined(__i386__)
MD_CPU_ARCHITECTURE_AMD64
#endif
;
const u_int32_t kExpectedContext =
#if defined(__i386__)
MD_CONTEXT_AMD64
#elif defined(__x86_64__)
MD_CONTEXT_X86
#endif
;
// Read the minidump, sanity check some data.
Minidump minidump(dump_filename.c_str());
ASSERT_TRUE(minidump.Read());
MinidumpSystemInfo* system_info = minidump.GetSystemInfo();
ASSERT_TRUE(system_info);
const MDRawSystemInfo* raw_info = system_info->system_info();
ASSERT_TRUE(raw_info);
EXPECT_EQ(kExpectedArchitecture, raw_info->processor_architecture);
MinidumpThreadList* thread_list = minidump.GetThreadList();
ASSERT_TRUE(thread_list);
ASSERT_EQ((unsigned int)1, thread_list->thread_count());
MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0);
ASSERT_TRUE(main_thread);
MinidumpContext* context = main_thread->GetContext();
ASSERT_TRUE(context);
EXPECT_EQ(kExpectedContext, context->GetContextCPU());
MinidumpModuleList* module_list = minidump.GetModuleList();
ASSERT_TRUE(module_list);
const MinidumpModule* main_module = module_list->GetMainModule();
ASSERT_TRUE(main_module);
EXPECT_EQ(helper_path, main_module->code_file());
}
#endif // 10.6 && (x86-64 || i386)
}

View file

@ -0,0 +1,63 @@
// Copyright (c) 2010, 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.
// minidump_generator_test_helper.cc: A helper program that
// minidump_generator_test.cc can launch to test certain things
// that require a separate executable.
#include "common/mac/MachIPC.h"
#include <unistd.h>
using google_breakpad::MachPortSender;
using google_breakpad::MachReceiveMessage;
using google_breakpad::MachSendMessage;
using google_breakpad::ReceivePort;
int main(int argc, char** argv) {
if (argc < 2)
return 1;
const int kTimeoutMs = 2000;
// Send parent process the task and thread ports.
MachSendMessage child_message(0);
child_message.AddDescriptor(mach_task_self());
child_message.AddDescriptor(mach_thread_self());
MachPortSender child_sender(argv[1]);
if (child_sender.SendMessage(child_message, kTimeoutMs) != KERN_SUCCESS) {
fprintf(stderr, "Error sending message from child process!\n");
exit(1);
}
// Loop forever.
while (true) {
sleep(100);
}
return 0;
}

View file

@ -839,7 +839,7 @@ bool MinidumpContext::CheckAgainstSystemInfo(u_int32_t context_cpu_type) {
BPLOG_IF(ERROR, !return_value) << "MinidumpContext CPU " <<
HexString(context_cpu_type) <<
" wrong for MinidumpSysmtemInfo CPU " <<
" wrong for MinidumpSystemInfo CPU " <<
HexString(system_info_cpu_type);
return return_value;