aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorted.mielczarek@gmail.com <ted.mielczarek@gmail.com@4c0a9323-5329-0410-9bdc-e9ce6186880e>2010-12-15 21:55:56 +0000
committerted.mielczarek@gmail.com <ted.mielczarek@gmail.com@4c0a9323-5329-0410-9bdc-e9ce6186880e>2010-12-15 21:55:56 +0000
commit0344a368deac6abaa280a298bcea9bb00a90df3f (patch)
tree48245f419549c51163ea1cffdc9ba35989001485
parentAllow writing on-request minidumps with an exception stream (diff)
downloadbreakpad-0344a368deac6abaa280a298bcea9bb00a90df3f.tar.xz
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
-rw-r--r--src/client/mac/Breakpad.xcodeproj/project.pbxproj109
-rw-r--r--src/client/mac/handler/breakpad_nlist_64.cc411
-rw-r--r--src/client/mac/handler/breakpad_nlist_64.h14
-rw-r--r--src/client/mac/handler/dynamic_images.cc502
-rw-r--r--src/client/mac/handler/dynamic_images.h196
-rw-r--r--src/client/mac/handler/minidump_generator.cc346
-rw-r--r--src/client/mac/handler/minidump_generator.h50
-rw-r--r--src/client/mac/tests/minidump_generator_test.cc160
-rw-r--r--src/client/mac/tests/minidump_generator_test_helper.cc63
-rw-r--r--src/processor/minidump.cc2
10 files changed, 1231 insertions, 622 deletions
diff --git a/src/client/mac/Breakpad.xcodeproj/project.pbxproj b/src/client/mac/Breakpad.xcodeproj/project.pbxproj
index 6f106aa1..7e143406 100644
--- a/src/client/mac/Breakpad.xcodeproj/project.pbxproj
+++ b/src/client/mac/Breakpad.xcodeproj/project.pbxproj
@@ -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 = (
diff --git a/src/client/mac/handler/breakpad_nlist_64.cc b/src/client/mac/handler/breakpad_nlist_64.cc
index 4b655c79..709e8546 100644
--- a/src/client/mac/handler/breakpad_nlist_64.cc
+++ b/src/client/mac/handler/breakpad_nlist_64.cc
@@ -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)];
-
- 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;
+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;
- maxlen = 500;
- for (q = list, nreq = 0;
+ const uint32_t magic = MachBits<nlist_type>::magic;
+
+ 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;
- }
+ 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;
}
+ }
- if (!fap) {
- free(fat_archs);
- return (-1);
- }
- arch_offset = fap->offset;
+ 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);
- }
+ /* 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;
+ }
+ }
- if (*((unsigned int *)&buf) == MH_MAGIC_64) {
- struct mach_header_64 mh;
- struct load_command *load_commands, *lcp;
- struct symtab_command *stp;
- long i;
+ 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;
+ }
- 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) {
+ 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);
+ 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) {
+ if (lcp->cmd == LC_SYMTAB) {
+ if (lcp->cmdsize !=
+ sizeof(struct symtab_command)) {
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;
+ return -1;
}
- lcp = (struct load_command *)
- ((char *)lcp + lcp->cmdsize);
- }
- if (stp == NULL) {
- free(load_commands);
- return (-1);
+ stp = (struct symtab_command *)lcp;
+ break;
}
- // 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);
+ lcp = (struct load_command *)
+ ((char *)lcp + lcp->cmdsize);
}
- else {
- sa = N_SYMOFF(buf) + arch_offset;
- ss = sa + buf.a_syms + arch_offset;
- n = buf.a_syms;
+ 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;
+ }
- lseek(fd, sa, SEEK_SET);
+ 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];
- // 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;
+ if (q->n_un.n_strx == 0 || q->n_type & N_STAB)
+ continue;
- m = sizeof (space);
- if (n < m)
- m = n;
- if (read(fd, (char *)space, m) != m)
- 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)
+ // 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);
+ 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: ;
- }
+ break;
+ cont: ;
}
- lseek(fd, savpos, SEEK_SET);
}
- return (nreq);
+ if (lseek(fd, savpos, SEEK_SET) == -1) {
+ return -1;
+ }
}
-
-#endif /* __LP64__ */
+ return nreq;
+}
diff --git a/src/client/mac/handler/breakpad_nlist_64.h b/src/client/mac/handler/breakpad_nlist_64.h
index ee10afb9..1d2c6391 100644
--- a/src/client/mac/handler/breakpad_nlist_64.h
+++ b/src/client/mac/handler/breakpad_nlist_64.h
@@ -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__ */
diff --git a/src/client/mac/handler/dynamic_images.cc b/src/client/mac/handler/dynamic_images.cc
index 1d5f1f9b..035afe7e 100644
--- a/src/client/mac/handler/dynamic_images.cc
+++ b/src/client/mac/handler/dynamic_images.cc
@@ -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,147 +137,170 @@ 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;
-
- r = mach_vm_read(target_task,
- page_address,
- page_size,
- reinterpret_cast<vm_offset_t*>(&local_start),
- &local_length);
-
-
- 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;
+ kern_return_t 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;
+
+ 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 -
//==============================================================================
-// 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;
- vmsize_ = 0;
- slide_ = 0;
- version_ = 0;
-
- bool foundTextSection = false;
- bool foundDylibIDCommand = false;
+// 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;
-#if __LP64__
- if(header->magic != MH_MAGIC_64) {
- return;
- }
-#else
- if(header->magic != MH_MAGIC) {
- return;
- }
-#endif
+ const mach_header_type* header =
+ reinterpret_cast<const mach_header_type*>(&image.header_[0]);
-#ifdef __LP64__
- const uint32_t segmentLoadCommand = LC_SEGMENT_64;
-#else
- const uint32_t segmentLoadCommand = LC_SEGMENT;
-#endif
+ if(header->magic != MachBits::magic) {
+ return false;
+ }
const struct load_command *cmd =
- reinterpret_cast<const struct load_command *>(header + 1);
+ 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 (!foundTextSection) {
- if (cmd->cmd == segmentLoadCommand) {
- const breakpad_mach_segment_command *seg =
- reinterpret_cast<const breakpad_mach_segment_command *>(cmd);
+ 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")) {
- vmaddr_ = seg->vmaddr;
- vmsize_ = seg->vmsize;
- slide_ = 0;
+ image.vmaddr_ = seg->vmaddr;
+ image.vmsize_ = seg->vmsize;
+ image.slide_ = 0;
- if (seg->fileoff == 0 && seg->filesize != 0) {
- slide_ = (uintptr_t)GetLoadAddress() - (uintptr_t)seg->vmaddr;
+ if (seg->fileoff == 0 && seg->filesize != 0) {
+ image.slide_ =
+ (uintptr_t)image.GetLoadAddress() - (uintptr_t)seg->vmaddr;
}
- foundTextSection = true;
+ found_text_section = true;
}
}
}
- if (!foundDylibIDCommand) {
+ if (!found_dylib_id_command) {
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;
+ image.version_ = dc->dylib.current_version;
+ found_dylib_id_command = true;
}
}
- if (foundDylibIDCommand && foundTextSection) {
- return;
+ if (found_dylib_id_command && found_text_section) {
+ return true;
}
cmd = reinterpret_cast<const struct load_command *>
- (reinterpret_cast<const char *>(cmd) + cmd->cmdsize);
+ (reinterpret_cast<const char *>(cmd) + cmd->cmdsize);
}
+ return false;
}
-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());
+//==============================================================================
+// Initializes vmaddr_, vmsize_, and slide_
+void DynamicImage::CalculateMemoryAndVersionInfo() {
+ // unless we can process the header, ensure that calls to
+ // IsValid() will return false
+ vmaddr_ = 0;
+ vmsize_ = 0;
+ slide_ = 0;
+ version_ = 0;
+
+ // The function template above does all the real work.
+ if (Is64Bit())
+ FindTextSection<MachO64>(*this);
+ else
+ FindTextSection<MachO32>(*this);
+}
+
+//==============================================================================
+// 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) );
-
- // 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];
+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;
- 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
+ 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.
-
- // 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));
-
- 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), &kr));
-
- 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.
- breakpad_mach_header *header = reinterpret_cast<breakpad_mach_header*>
- (ReadTaskMemory(task_,
- info.load_address_,
- sizeof(breakpad_mach_header), &kr));
-
- 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.
- size_t header_size =
- sizeof(breakpad_mach_header) + header->sizeofcmds;
-
- free(header);
-
- header = reinterpret_cast<breakpad_mach_header*>
- (ReadTaskMemory(task_, info.load_address_, header_size, &kr));
-
- // 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_));
- }
+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;
+
+ // 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.
+ 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;
- // 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_);
-
- if (new_image->IsValid()) {
- image_list_.push_back(DynamicImageRef(new_image));
- } else {
- delete new_image;
- }
+ dyld_all_image_infos *dyldInfo =
+ reinterpret_cast<dyld_all_image_infos*>(&dyld_all_info_bytes[0]);
- if (file_path) {
- free(file_path);
- }
- }
+ // number of loaded images
+ int count = dyldInfo->infoArrayCount;
- free(dyldInfo);
- free(infoArray);
+ // 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;
- // 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
+ dyld_image_info *infoArray =
+ reinterpret_cast<dyld_image_info*>(&dyld_info_array_bytes[0]);
+ images.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.
+ 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
+
+ mach_header_type *header =
+ reinterpret_cast<mach_header_type*>(&mach_header_bytes[0]);
+
+ // 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;
+
+ if (ReadTaskMemory(images.task_,
+ info.load_address_,
+ header_size,
+ mach_header_bytes) != KERN_SUCCESS)
+ continue;
+
+ header = reinterpret_cast<mach_header_type*>(&mach_header_bytes[0]);
+
+ // 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_);
+ }
- vector<DynamicImageRef>::iterator it = unique(image_list_.begin(),
- image_list_.end() );
- image_list_.erase(it, image_list_.end());
+ // 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_);
+
+ 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
diff --git a/src/client/mac/handler/dynamic_images.h b/src/client/mac/handler/dynamic_images.h
index 72eb221b..63816bf3 100644
--- a/src/client/mac/handler/dynamic_images.h
+++ b/src/client/mac/handler/dynamic_images.h
@@ -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;
+ uint32_t infoArray; // const struct dyld_image_info*
+ uint32_t notification;
+ bool processDetachedFromSharedRegion;
+} dyld_all_image_infos32;
+
+typedef struct dyld_all_image_infos64 {
uint32_t version; // == 1 in Mac OS X 10.4
uint32_t infoArrayCount;
- const struct dyld_image_info *infoArray;
- void* notification;
+ uint64_t infoArray; // const struct dyld_image_info*
+ uint64_t notification;
bool processDetachedFromSharedRegion;
-} dyld_all_image_infos;
+} 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) {}
-
- 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);
- }
+// Helper functions to deal with 32-bit/64-bit Mach-O differences.
+class DynamicImage;
+template<typename MachBits>
+bool FindTextSection(DynamicImage& image);
- 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();
- }
- }
-
- 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) );
-
- const void *imageHeader = _dyld_get_image_header(i);
- header = reinterpret_cast<const breakpad_mach_header*>(imageHeader);
-
- MachHeader(*header).Print();
- }
+ // CPU type of the task
+ cpu_type_t GetCPUType() {return cpu_type_;}
+
+ // Return true if the task is a 64-bit architecture.
+ bool Is64Bit() { return (GetCPUType() & CPU_ARCH_ABI64) == CPU_ARCH_ABI64; }
+
+ // Determine the CPU type of the task being dumped.
+ static cpu_type_t DetermineTaskCPUType(task_t task);
+
+ // 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
diff --git a/src/client/mac/handler/minidump_generator.cc b/src/client/mac/handler/minidump_generator.cc
index 8e3faf27..2e9765ad 100644
--- a/src/client/mac/handler/minidump_generator.cc
+++ b/src/client/mac/handler/minidump_generator.cc
@@ -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::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);
-bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state,
+ if (!context.Allocate())
+ return false;
+
+ *register_location = context.location();
+ 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)
+#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) {
- breakpad_thread_state_t *machine_state =
- reinterpret_cast<breakpad_thread_state_t *>(state);
+ i386_thread_state_t *machine_state =
+ reinterpret_cast<i386_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);
+}
+
+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::CurrentPCForStack(breakpad_thread_state_data_t state) {
- breakpad_thread_state_t *machine_state =
- reinterpret_cast<breakpad_thread_state_t *>(state);
+MinidumpGenerator::CurrentPCForStackX86(breakpad_thread_state_data_t state) {
+ i386_thread_state_t *machine_state =
+ reinterpret_cast<i386_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);
+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();
- MinidumpContext *context_ptr = context.get();
+ MDRawContextX86 *context_ptr = context.get();
#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a)
-#if TARGET_CPU_X86
+
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;
diff --git a/src/client/mac/handler/minidump_generator.h b/src/client/mac/handler/minidump_generator.h
index 39f28402..224ad1c2 100644
--- a/src/client/mac/handler/minidump_generator.h
+++ b/src/client/mac/handler/minidump_generator.h
@@ -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];
diff --git a/src/client/mac/tests/minidump_generator_test.cc b/src/client/mac/tests/minidump_generator_test.cc
index 5d0d5c28..68bc1de6 100644
--- a/src/client/mac/tests/minidump_generator_test.cc
+++ b/src/client/mac/tests/minidump_generator_test.cc
@@ -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)
+
}
diff --git a/src/client/mac/tests/minidump_generator_test_helper.cc b/src/client/mac/tests/minidump_generator_test_helper.cc
new file mode 100644
index 00000000..41ef449d
--- /dev/null
+++ b/src/client/mac/tests/minidump_generator_test_helper.cc
@@ -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;
+}
diff --git a/src/processor/minidump.cc b/src/processor/minidump.cc
index f57001a2..41e7cea6 100644
--- a/src/processor/minidump.cc
+++ b/src/processor/minidump.cc
@@ -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;