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:
parent
0d9bd40775
commit
0344a368de
10 changed files with 1262 additions and 653 deletions
|
@ -53,6 +53,8 @@
|
||||||
8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; };
|
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 */; };
|
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 */; };
|
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 */; };
|
D244536A12426F00009BBCE0 /* logging.cc in Sources */ = {isa = PBXBuildFile; fileRef = D244535112426EBB009BBCE0 /* logging.cc */; };
|
||||||
D244536B12426F00009BBCE0 /* minidump.cc in Sources */ = {isa = PBXBuildFile; fileRef = D244535212426EBB009BBCE0 /* minidump.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 */; };
|
D244536C12426F00009BBCE0 /* pathname_stripper.cc in Sources */ = {isa = PBXBuildFile; fileRef = D244535312426EBB009BBCE0 /* pathname_stripper.cc */; };
|
||||||
|
@ -324,6 +326,13 @@
|
||||||
remoteGlobalIDString = D2F9A41412131EF0002747C1;
|
remoteGlobalIDString = D2F9A41412131EF0002747C1;
|
||||||
remoteInfo = gtest;
|
remoteInfo = gtest;
|
||||||
};
|
};
|
||||||
|
D23F4BB912A8694C00686C8D /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = D23F4BAA12A868A500686C8D;
|
||||||
|
remoteInfo = minidump_generator_test_helper;
|
||||||
|
};
|
||||||
D2F9A44212131F80002747C1 /* PBXContainerItemProxy */ = {
|
D2F9A44212131F80002747C1 /* PBXContainerItemProxy */ = {
|
||||||
isa = PBXContainerItemProxy;
|
isa = PBXContainerItemProxy;
|
||||||
containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
|
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; };
|
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; };
|
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>"; };
|
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; };
|
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; };
|
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; };
|
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;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
D23F4BA912A868A500686C8D /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
D2F9A41312131EF0002747C1 /* Frameworks */ = {
|
D2F9A41312131EF0002747C1 /* Frameworks */ = {
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
@ -767,6 +785,7 @@
|
||||||
F93DE32C0F82C55600608B94 /* handler_test */,
|
F93DE32C0F82C55600608B94 /* handler_test */,
|
||||||
D2F9A41512131EF0002747C1 /* libgtest.a */,
|
D2F9A41512131EF0002747C1 /* libgtest.a */,
|
||||||
D2F9A546121383A1002747C1 /* crash_generation_server_test */,
|
D2F9A546121383A1002747C1 /* crash_generation_server_test */,
|
||||||
|
D23F4BAB12A868A500686C8D /* minidump_generator_test_helper */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1021,6 +1040,7 @@
|
||||||
F9C77DDF0F7DD7CF0045F7DB /* tests */ = {
|
F9C77DDF0F7DD7CF0045F7DB /* tests */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
D23F4B9A12A8688800686C8D /* minidump_generator_test_helper.cc */,
|
||||||
D23F4B2C12A7E13200686C8D /* minidump_generator_test.cc */,
|
D23F4B2C12A7E13200686C8D /* minidump_generator_test.cc */,
|
||||||
D2F9A4CE121336F7002747C1 /* crash_generation_server_test.cc */,
|
D2F9A4CE121336F7002747C1 /* crash_generation_server_test.cc */,
|
||||||
D2F9A3D41212F87C002747C1 /* exception_handler_test.cc */,
|
D2F9A3D41212F87C002747C1 /* exception_handler_test.cc */,
|
||||||
|
@ -1095,6 +1115,22 @@
|
||||||
productReference = 8DC2EF5B0486A6940098B216 /* Breakpad.framework */;
|
productReference = 8DC2EF5B0486A6940098B216 /* Breakpad.framework */;
|
||||||
productType = "com.apple.product-type.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 */ = {
|
D2F9A41412131EF0002747C1 /* gtest */ = {
|
||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = D2F9A42D12131F0E002747C1 /* Build configuration list for PBXNativeTarget "gtest" */;
|
buildConfigurationList = D2F9A42D12131F0E002747C1 /* Build configuration list for PBXNativeTarget "gtest" */;
|
||||||
|
@ -1192,6 +1228,7 @@
|
||||||
);
|
);
|
||||||
dependencies = (
|
dependencies = (
|
||||||
D23F4B3012A7E16200686C8D /* PBXTargetDependency */,
|
D23F4B3012A7E16200686C8D /* PBXTargetDependency */,
|
||||||
|
D23F4BBA12A8694C00686C8D /* PBXTargetDependency */,
|
||||||
);
|
);
|
||||||
name = generator_test;
|
name = generator_test;
|
||||||
productName = generator_test;
|
productName = generator_test;
|
||||||
|
@ -1325,6 +1362,7 @@
|
||||||
F93DE32B0F82C55600608B94 /* handler_test */,
|
F93DE32B0F82C55600608B94 /* handler_test */,
|
||||||
D2F9A41412131EF0002747C1 /* gtest */,
|
D2F9A41412131EF0002747C1 /* gtest */,
|
||||||
D2F9A52A121383A1002747C1 /* crash_generation_server_test */,
|
D2F9A52A121383A1002747C1 /* crash_generation_server_test */,
|
||||||
|
D23F4BAA12A868A500686C8D /* minidump_generator_test_helper */,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
/* End PBXProject section */
|
/* End PBXProject section */
|
||||||
|
@ -1559,6 +1597,15 @@
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
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 */ = {
|
D2F9A41212131EF0002747C1 /* Sources */ = {
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
@ -1756,6 +1803,11 @@
|
||||||
target = D2F9A41412131EF0002747C1 /* gtest */;
|
target = D2F9A41412131EF0002747C1 /* gtest */;
|
||||||
targetProxy = D23F4B2F12A7E16200686C8D /* PBXContainerItemProxy */;
|
targetProxy = D23F4B2F12A7E16200686C8D /* PBXContainerItemProxy */;
|
||||||
};
|
};
|
||||||
|
D23F4BBA12A8694C00686C8D /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = D23F4BAA12A868A500686C8D /* minidump_generator_test_helper */;
|
||||||
|
targetProxy = D23F4BB912A8694C00686C8D /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
D2F9A44312131F80002747C1 /* PBXTargetDependency */ = {
|
D2F9A44312131F80002747C1 /* PBXTargetDependency */ = {
|
||||||
isa = PBXTargetDependency;
|
isa = PBXTargetDependency;
|
||||||
target = D2F9A41412131EF0002747C1 /* gtest */;
|
target = D2F9A41412131EF0002747C1 /* gtest */;
|
||||||
|
@ -1953,6 +2005,10 @@
|
||||||
baseConfigurationReference = 8B31027711F0D3AF00FCF3E4 /* BreakpadDebug.xcconfig */;
|
baseConfigurationReference = 8B31027711F0D3AF00FCF3E4 /* BreakpadDebug.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
GCC_TREAT_WARNINGS_AS_ERRORS = NO;
|
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;
|
name = Debug;
|
||||||
};
|
};
|
||||||
|
@ -1964,6 +2020,49 @@
|
||||||
};
|
};
|
||||||
name = Release;
|
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 */ = {
|
D2F9A41612131EF0002747C1 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
@ -2387,6 +2486,16 @@
|
||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
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" */ = {
|
D2F9A42D12131F0E002747C1 /* Build configuration list for PBXNativeTarget "gtest" */ = {
|
||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
buildConfigurations = (
|
buildConfigurations = (
|
||||||
|
|
|
@ -54,7 +54,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/* nealsid:
|
/*
|
||||||
* This file was copied from libc/gen/nlist.c from Darwin's source code
|
* 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
|
* 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
|
* http://www.opensource.apple.com/darwinsource/10.5.2/Libc-498/gen/nlist.c
|
||||||
|
@ -62,24 +62,22 @@
|
||||||
* The full tarball is at:
|
* The full tarball is at:
|
||||||
* http://www.opensource.apple.com/darwinsource/tarballs/apsl/Libc-498.tar.gz
|
* http://www.opensource.apple.com/darwinsource/tarballs/apsl/Libc-498.tar.gz
|
||||||
*
|
*
|
||||||
* I've modified it to be compatible with 64-bit images. However,
|
* I've modified it to be compatible with 64-bit images.
|
||||||
* 32-bit compatibility has not been retained.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef __LP64__
|
#include "breakpad_nlist_64.h"
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
#include <mach-o/nlist.h>
|
#include <mach-o/nlist.h>
|
||||||
#include <mach-o/loader.h>
|
#include <mach-o/loader.h>
|
||||||
#include <mach-o/fat.h>
|
#include <mach-o/fat.h>
|
||||||
|
#include <mach/mach.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/uio.h>
|
#include <sys/uio.h>
|
||||||
#include <unistd.h>
|
|
||||||
#include "breakpad_nlist_64.h"
|
|
||||||
#include <TargetConditionals.h>
|
#include <TargetConditionals.h>
|
||||||
#include <stdio.h>
|
#include <unistd.h>
|
||||||
#include <mach/mach.h>
|
|
||||||
|
|
||||||
/* Stuff lifted from <a.out.h> and <sys/exec.h> since they are gone */
|
/* Stuff lifted from <a.out.h> and <sys/exec.h> since they are gone */
|
||||||
/*
|
/*
|
||||||
|
@ -108,44 +106,77 @@ struct exec {
|
||||||
#define N_SYMOFF(x) \
|
#define N_SYMOFF(x) \
|
||||||
(N_TXTOFF(x) + (x).a_text+(x).a_data + (x).a_trsize+(x).a_drsize)
|
(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
|
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)
|
* nlist - retreive attributes from name list (string table version)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int
|
template <typename nlist_type>
|
||||||
breakpad_nlist_64(const char *name,
|
int breakpad_nlist_common(const char *name,
|
||||||
breakpad_nlist *list,
|
nlist_type *list,
|
||||||
const char **symbolNames) {
|
const char **symbolNames,
|
||||||
int fd, n;
|
cpu_type_t cpu_type) {
|
||||||
|
int fd = open(name, O_RDONLY, 0);
|
||||||
fd = open(name, O_RDONLY, 0);
|
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
return (-1);
|
return -1;
|
||||||
n = __breakpad_fdnlist_64(fd, list, symbolNames);
|
int n = __breakpad_fdnlist(fd, list, symbolNames, cpu_type);
|
||||||
(void)close(fd);
|
close(fd);
|
||||||
return (n);
|
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 */
|
/* Note: __fdnlist() is called from kvm_nlist in libkvm's kvm.c */
|
||||||
|
|
||||||
int
|
template<typename nlist_type>
|
||||||
__breakpad_fdnlist_64(int fd, breakpad_nlist *list, const char **symbolNames) {
|
int __breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames,
|
||||||
register breakpad_nlist *p, *q;
|
cpu_type_t cpu_type) {
|
||||||
breakpad_nlist space[BUFSIZ/sizeof (breakpad_nlist)];
|
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;
|
const uint32_t magic = MachBits<nlist_type>::magic;
|
||||||
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;
|
|
||||||
|
|
||||||
maxlen = 500;
|
int maxlen = 500;
|
||||||
for (q = list, nreq = 0;
|
int nreq = 0;
|
||||||
|
for (nlist_type* q = list;
|
||||||
symbolNames[q-list] && symbolNames[q-list][0];
|
symbolNames[q-list] && symbolNames[q-list][0];
|
||||||
q++, nreq++) {
|
q++, nreq++) {
|
||||||
|
|
||||||
|
@ -156,61 +187,61 @@ __breakpad_fdnlist_64(int fd, breakpad_nlist *list, const char **symbolNames) {
|
||||||
q->n_un.n_strx = 0;
|
q->n_un.n_strx = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct exec buf;
|
||||||
if (read(fd, (char *)&buf, sizeof(buf)) != sizeof(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) &&
|
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) {
|
(*((long*)&buf)) != FAT_MAGIC) {
|
||||||
return (-1);
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Deal with fat file if necessary */
|
/* Deal with fat file if necessary */
|
||||||
|
unsigned arch_offset = 0;
|
||||||
if (NXSwapBigLongToHost(*((long *)&buf)) == FAT_MAGIC ||
|
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) {
|
*((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 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;
|
kern_return_t kr;
|
||||||
if ((kr=host_info(host, HOST_BASIC_INFO,
|
if ((kr = host_info(host, HOST_BASIC_INFO,
|
||||||
(host_info_t)(&hbi), &i)) != KERN_SUCCESS) {
|
(host_info_t)(&hbi), &i)) != KERN_SUCCESS) {
|
||||||
return (-1);
|
return -1;
|
||||||
}
|
}
|
||||||
mach_port_deallocate(mach_task_self(), host);
|
mach_port_deallocate(mach_task_self(), host);
|
||||||
|
|
||||||
/* Read in the fat header */
|
/* 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)) {
|
if (read(fd, (char *)&fh, sizeof(fh)) != sizeof(fh)) {
|
||||||
return (-1);
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Convert fat_narchs to host byte order */
|
/* Convert fat_narchs to host byte order */
|
||||||
fh.nfat_arch = NXSwapBigIntToHost(fh.nfat_arch);
|
fh.nfat_arch = NXSwapBigIntToHost(fh.nfat_arch);
|
||||||
|
|
||||||
/* Read in the fat archs */
|
/* Read in the fat archs */
|
||||||
fat_archs = (struct fat_arch *)malloc(fh.nfat_arch *
|
struct fat_arch *fat_archs =
|
||||||
sizeof(struct fat_arch));
|
(struct fat_arch *)malloc(fh.nfat_arch * sizeof(struct fat_arch));
|
||||||
if (fat_archs == NULL) {
|
if (fat_archs == NULL) {
|
||||||
return (-1);
|
return -1;
|
||||||
}
|
}
|
||||||
if (read(fd, (char *)fat_archs,
|
if (read(fd, (char *)fat_archs,
|
||||||
sizeof(struct fat_arch) * fh.nfat_arch) !=
|
sizeof(struct fat_arch) * fh.nfat_arch) !=
|
||||||
(ssize_t)sizeof(struct fat_arch) * fh.nfat_arch) {
|
(ssize_t)sizeof(struct fat_arch) * fh.nfat_arch) {
|
||||||
free(fat_archs);
|
free(fat_archs);
|
||||||
return (-1);
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert archs to host byte ordering (a constraint of
|
* Convert archs to host byte ordering (a constraint of
|
||||||
* cpusubtype_getbestarch()
|
* cpusubtype_getbestarch()
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < fh.nfat_arch; i++) {
|
for (unsigned i = 0; i < fh.nfat_arch; i++) {
|
||||||
fat_archs[i].cputype =
|
fat_archs[i].cputype =
|
||||||
NXSwapBigIntToHost(fat_archs[i].cputype);
|
NXSwapBigIntToHost(fat_archs[i].cputype);
|
||||||
fat_archs[i].cpusubtype =
|
fat_archs[i].cpusubtype =
|
||||||
|
@ -223,159 +254,159 @@ __breakpad_fdnlist_64(int fd, breakpad_nlist *list, const char **symbolNames) {
|
||||||
NXSwapBigIntToHost(fat_archs[i].align);
|
NXSwapBigIntToHost(fat_archs[i].align);
|
||||||
}
|
}
|
||||||
|
|
||||||
fap = NULL;
|
struct fat_arch *fap = NULL;
|
||||||
for (i = 0; i < fh.nfat_arch; i++) {
|
for (unsigned i = 0; i < fh.nfat_arch; i++) {
|
||||||
/* nealsid: Although the original Apple code uses host_info */
|
if (fat_archs[i].cputype == cpu_type) {
|
||||||
/* to retrieve the CPU type, the host_info will still return */
|
fap = &fat_archs[i];
|
||||||
/* 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)
|
|
||||||
break;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -33,11 +33,15 @@
|
||||||
|
|
||||||
#ifndef CLIENT_MAC_HANDLER_BREAKPAD_NLIST_H__
|
#ifndef CLIENT_MAC_HANDLER_BREAKPAD_NLIST_H__
|
||||||
|
|
||||||
typedef struct nlist_64 breakpad_nlist;
|
#include <mach/machine.h>
|
||||||
|
|
||||||
int
|
int breakpad_nlist(const char *name,
|
||||||
breakpad_nlist_64(const char *name,
|
struct nlist *list,
|
||||||
breakpad_nlist *list,
|
const char **symbolNames,
|
||||||
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__ */
|
#endif /* CLIENT_MAC_HANDLER_BREAKPAD_NLIST_H__ */
|
||||||
|
|
|
@ -27,6 +27,8 @@
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// 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
|
extern "C" { // needed to compile on Leopard
|
||||||
#include <mach-o/nlist.h>
|
#include <mach-o/nlist.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
@ -37,11 +39,17 @@ extern "C" { // needed to compile on Leopard
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#include <mach/mach_vm.h>
|
#include <mach/mach_vm.h>
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include "client/mac/handler/dynamic_images.h"
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
using std::vector;
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
// Returns the size of the memory region containing |address| and the
|
// Returns the size of the memory region containing |address| and the
|
||||||
// number of bytes from |address| to the end of the region.
|
// number of bytes from |address| to the end of the region.
|
||||||
|
@ -51,7 +59,7 @@ namespace google_breakpad {
|
||||||
// straddle two vm regions.
|
// straddle two vm regions.
|
||||||
//
|
//
|
||||||
static mach_vm_size_t GetMemoryRegionSize(task_port_t target_task,
|
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_size_t *size_to_end) {
|
||||||
mach_vm_address_t region_base = (mach_vm_address_t)address;
|
mach_vm_address_t region_base = (mach_vm_address_t)address;
|
||||||
mach_vm_size_t region_size;
|
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
|
// Warning! This will not read any strings longer than kMaxStringLength-1
|
||||||
//
|
//
|
||||||
static void* ReadTaskString(task_port_t target_task,
|
static string ReadTaskString(task_port_t target_task,
|
||||||
const void* address) {
|
const uint64_t address) {
|
||||||
// The problem is we don't know how much to read until we know how long
|
// 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 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
|
// 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 =
|
mach_vm_size_t size_to_read =
|
||||||
size_to_end > kMaxStringLength ? kMaxStringLength : size_to_end;
|
size_to_end > kMaxStringLength ? kMaxStringLength : size_to_end;
|
||||||
|
|
||||||
kern_return_t kr;
|
vector<uint8_t> bytes;
|
||||||
return ReadTaskMemory(target_task, address, (size_t)size_to_read, &kr);
|
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
|
// Reads an address range from another task. The bytes read will be returned
|
||||||
// and should be freed by the caller.
|
// in bytes, which will be resized as necessary.
|
||||||
void* ReadTaskMemory(task_port_t target_task,
|
kern_return_t ReadTaskMemory(task_port_t target_task,
|
||||||
const void* address,
|
const uint64_t address,
|
||||||
size_t length,
|
size_t length,
|
||||||
kern_return_t *kr) {
|
vector<uint8_t> &bytes) {
|
||||||
void* result = NULL;
|
|
||||||
int systemPageSize = getpagesize();
|
int systemPageSize = getpagesize();
|
||||||
|
|
||||||
// use the negative of the page size for the mask to find the page address
|
// use the negative of the page size for the mask to find the page address
|
||||||
mach_vm_address_t page_address =
|
mach_vm_address_t page_address = address & (-systemPageSize);
|
||||||
reinterpret_cast<mach_vm_address_t>(address) & (-systemPageSize);
|
|
||||||
|
|
||||||
mach_vm_address_t last_page_address =
|
mach_vm_address_t last_page_address =
|
||||||
(reinterpret_cast<mach_vm_address_t>(address) + length +
|
(address + length + (systemPageSize - 1)) & (-systemPageSize);
|
||||||
(systemPageSize - 1)) & (-systemPageSize);
|
|
||||||
|
|
||||||
mach_vm_size_t page_size = last_page_address - page_address;
|
mach_vm_size_t page_size = last_page_address - page_address;
|
||||||
uint8_t* local_start;
|
uint8_t* local_start;
|
||||||
uint32_t local_length;
|
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,
|
if (r != KERN_SUCCESS)
|
||||||
page_address,
|
return r;
|
||||||
page_size,
|
|
||||||
reinterpret_cast<vm_offset_t*>(&local_start),
|
|
||||||
&local_length);
|
|
||||||
|
|
||||||
|
bytes.resize(length);
|
||||||
if (kr != NULL) {
|
memcpy(&bytes[0],
|
||||||
*kr = r;
|
&local_start[(mach_vm_address_t)address - page_address],
|
||||||
}
|
length);
|
||||||
|
mach_vm_deallocate(mach_task_self(), (uintptr_t)local_start, local_length);
|
||||||
if (r == KERN_SUCCESS) {
|
return 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark -
|
#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_
|
// Initializes vmaddr_, vmsize_, and slide_
|
||||||
void DynamicImage::CalculateMemoryAndVersionInfo() {
|
void DynamicImage::CalculateMemoryAndVersionInfo() {
|
||||||
breakpad_mach_header *header = GetMachHeader();
|
|
||||||
|
|
||||||
// unless we can process the header, ensure that calls to
|
// unless we can process the header, ensure that calls to
|
||||||
// IsValid() will return false
|
// IsValid() will return false
|
||||||
vmaddr_ = 0;
|
vmaddr_ = 0;
|
||||||
|
@ -198,78 +278,29 @@ void DynamicImage::CalculateMemoryAndVersionInfo() {
|
||||||
slide_ = 0;
|
slide_ = 0;
|
||||||
version_ = 0;
|
version_ = 0;
|
||||||
|
|
||||||
bool foundTextSection = false;
|
// The function template above does all the real work.
|
||||||
bool foundDylibIDCommand = false;
|
if (Is64Bit())
|
||||||
|
FindTextSection<MachO64>(*this);
|
||||||
#if __LP64__
|
else
|
||||||
if(header->magic != MH_MAGIC_64) {
|
FindTextSection<MachO32>(*this);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DynamicImage::Print() {
|
//==============================================================================
|
||||||
const char *path = GetFilePath();
|
// The helper function template abstracts the 32/64-bit differences.
|
||||||
if (!path) {
|
template<typename MachBits>
|
||||||
path = "(unknown)";
|
uint32_t GetFileTypeFromHeader(DynamicImage& image) {
|
||||||
}
|
typedef typename MachBits::mach_header_type mach_header_type;
|
||||||
printf("%p: %s\n", GetLoadAddress(), path);
|
|
||||||
breakpad_mach_header *header = GetMachHeader();
|
const mach_header_type* header =
|
||||||
MachHeader(*header).Print();
|
reinterpret_cast<const mach_header_type*>(&image.header_[0]);
|
||||||
printf("vmaddr\t\t: %p\n", reinterpret_cast<void*>(GetVMAddr()));
|
return header->filetype;
|
||||||
printf("vmsize\t\t: %llu\n", GetVMSize());
|
}
|
||||||
printf("slide\t\t: %td\n", GetVMAddrSlide());
|
|
||||||
|
uint32_t DynamicImage::GetFileType() {
|
||||||
|
if (Is64Bit())
|
||||||
|
return GetFileTypeFromHeader<MachO64>(*this);
|
||||||
|
|
||||||
|
return GetFileTypeFromHeader<MachO32>(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark -
|
#pragma mark -
|
||||||
|
@ -277,144 +308,158 @@ void DynamicImage::Print() {
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
// Loads information about dynamically loaded code in the given task.
|
// Loads information about dynamically loaded code in the given task.
|
||||||
DynamicImages::DynamicImages(mach_port_t task)
|
DynamicImages::DynamicImages(mach_port_t task)
|
||||||
: task_(task), image_list_() {
|
: task_(task),
|
||||||
|
cpu_type_(DetermineTaskCPUType(task)),
|
||||||
|
image_list_() {
|
||||||
ReadImageInfoForTask();
|
ReadImageInfoForTask();
|
||||||
}
|
}
|
||||||
|
|
||||||
void* DynamicImages::GetDyldAllImageInfosPointer() {
|
template<typename MachBits>
|
||||||
const char *imageSymbolName = "_dyld_all_image_infos";
|
static uint64_t LookupSymbol(const char* symbol_name,
|
||||||
const char *dyldPath = "/usr/lib/dyld";
|
const char* filename,
|
||||||
#ifndef __LP64__
|
cpu_type_t cpu_type) {
|
||||||
struct nlist l[8];
|
typedef typename MachBits::nlist_type nlist_type;
|
||||||
memset(l, 0, sizeof(l) );
|
|
||||||
|
|
||||||
// First we lookup the address of the "_dyld_all_image_infos" struct
|
nlist_type symbol_info[8] = {};
|
||||||
// which lives in "dyld". This structure contains information about all
|
const char *symbolNames[2] = { symbol_name, "\0" };
|
||||||
// of the loaded dynamic images.
|
nlist_type &list = symbol_info[0];
|
||||||
struct nlist &list = l[0];
|
int invalidEntriesCount = breakpad_nlist(filename,
|
||||||
list.n_un.n_name = const_cast<char *>(imageSymbolName);
|
&list,
|
||||||
nlist(dyldPath,&list);
|
symbolNames,
|
||||||
if(list.n_value) {
|
cpu_type);
|
||||||
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);
|
|
||||||
|
|
||||||
if(invalidEntriesCount != 0) {
|
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.
|
// This code was written using dyld_debug.c (from Darwin) as a guide.
|
||||||
void DynamicImages::ReadImageInfoForTask() {
|
|
||||||
void *imageList = GetDyldAllImageInfosPointer();
|
|
||||||
|
|
||||||
if (imageList) {
|
template<typename MachBits>
|
||||||
kern_return_t kr;
|
void ReadImageInfo(DynamicImages& images,
|
||||||
// Read the structure inside of dyld that contains information about
|
uint64_t image_list_address) {
|
||||||
// loaded images. We're reading from the desired task's address space.
|
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
|
// Read the structure inside of dyld that contains information about
|
||||||
// the crashed process vs. this one. This is an assumption made in
|
// loaded images. We're reading from the desired task's address space.
|
||||||
// "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) {
|
// Here we make the assumption that dyld loaded at the same address in
|
||||||
// number of loaded images
|
// the crashed process vs. this one. This is an assumption made in
|
||||||
int count = dyldInfo->infoArrayCount;
|
// "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
|
dyld_all_image_infos *dyldInfo =
|
||||||
// information about a loaded image.
|
reinterpret_cast<dyld_all_image_infos*>(&dyld_all_info_bytes[0]);
|
||||||
dyld_image_info *infoArray = reinterpret_cast<dyld_image_info*>
|
|
||||||
(ReadTaskMemory(task_,
|
|
||||||
dyldInfo->infoArray,
|
|
||||||
count*sizeof(dyld_image_info), &kr));
|
|
||||||
|
|
||||||
image_list_.reserve(count);
|
// number of loaded images
|
||||||
|
int count = dyldInfo->infoArrayCount;
|
||||||
|
|
||||||
for (int i = 0; i < count; ++i) {
|
// Read an array of dyld_image_info structures each containing
|
||||||
dyld_image_info &info = infoArray[i];
|
// 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.
|
dyld_image_info *infoArray =
|
||||||
breakpad_mach_header *header = reinterpret_cast<breakpad_mach_header*>
|
reinterpret_cast<dyld_image_info*>(&dyld_info_array_bytes[0]);
|
||||||
(ReadTaskMemory(task_,
|
images.image_list_.reserve(count);
|
||||||
info.load_address_,
|
|
||||||
sizeof(breakpad_mach_header), &kr));
|
|
||||||
|
|
||||||
if (!header)
|
for (int i = 0; i < count; ++i) {
|
||||||
break; // bail on this dynamic image
|
dyld_image_info &info = infoArray[i];
|
||||||
|
|
||||||
// Now determine the total amount we really want to read based on the
|
// First read just the mach_header from the image in the task.
|
||||||
// size of the load commands. We need the header plus all of the
|
vector<uint8_t> mach_header_bytes;
|
||||||
// load commands.
|
if (ReadTaskMemory(images.task_,
|
||||||
size_t header_size =
|
info.load_address_,
|
||||||
sizeof(breakpad_mach_header) + header->sizeofcmds;
|
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*>
|
// Now determine the total amount necessary to read the header
|
||||||
(ReadTaskMemory(task_, info.load_address_, header_size, &kr));
|
// 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.
|
if (ReadTaskMemory(images.task_,
|
||||||
char *file_path = NULL;
|
info.load_address_,
|
||||||
if (info.file_path_) {
|
header_size,
|
||||||
// Although we're reading kMaxStringLength bytes, it's copied in the
|
mach_header_bytes) != KERN_SUCCESS)
|
||||||
// the DynamicImage constructor below with the correct string length,
|
continue;
|
||||||
// so it's not really wasting memory.
|
|
||||||
file_path = reinterpret_cast<char*>
|
|
||||||
(ReadTaskString(task_, info.file_path_));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create an object representing this image and add it to our list.
|
header = reinterpret_cast<mach_header_type*>(&mach_header_bytes[0]);
|
||||||
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()) {
|
// Read the file name from the task's memory space.
|
||||||
image_list_.push_back(DynamicImageRef(new_image));
|
string file_path;
|
||||||
} else {
|
if (info.file_path_) {
|
||||||
delete new_image;
|
// 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.
|
||||||
if (file_path) {
|
file_path = ReadTaskString(images.task_, info.file_path_);
|
||||||
free(file_path);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
free(dyldInfo);
|
// Create an object representing this image and add it to our list.
|
||||||
free(infoArray);
|
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
|
if (new_image->IsValid()) {
|
||||||
sort(image_list_.begin(), image_list_.end() );
|
images.image_list_.push_back(DynamicImageRef(new_image));
|
||||||
// remove duplicates - this happens in certain strange cases
|
} else {
|
||||||
// You can see it in DashboardClient when Google Gadgets plugin
|
delete new_image;
|
||||||
// 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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
for (int i = 0; i < image_count; ++i) {
|
||||||
DynamicImage *image = GetImage(i);
|
DynamicImage *image = GetImage(i);
|
||||||
if (image->GetMachHeader()->filetype == MH_EXECUTE) {
|
if (image->GetFileType() == MH_EXECUTE) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -444,4 +489,27 @@ int DynamicImages::GetExecutableImageIndex() {
|
||||||
return -1;
|
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
|
} // namespace google_breakpad
|
||||||
|
|
|
@ -41,32 +41,49 @@
|
||||||
#include <mach-o/dyld.h>
|
#include <mach-o/dyld.h>
|
||||||
#include <mach-o/loader.h>
|
#include <mach-o/loader.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
|
|
||||||
|
using std::string;
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
// The memory layout of this struct matches the dyld_image_info struct
|
// The memory layout of this struct matches the dyld_image_info struct
|
||||||
// defined in "dyld_gdb.h" in the darwin source.
|
// defined in "dyld_gdb.h" in the darwin source.
|
||||||
typedef struct dyld_image_info {
|
typedef struct dyld_image_info32 {
|
||||||
struct mach_header *load_address_;
|
uint32_t load_address_; // struct mach_header*
|
||||||
char *file_path_;
|
uint32_t file_path_; // char*
|
||||||
uintptr_t file_mod_date_;
|
uint32_t file_mod_date_;
|
||||||
} dyld_image_info;
|
} 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.
|
// This is as defined in "dyld_gdb.h" in the darwin source.
|
||||||
// _dyld_all_image_infos (in dyld) is a structure of this type
|
// _dyld_all_image_infos (in dyld) is a structure of this type
|
||||||
// which will be used to determine which dynamic code has been loaded.
|
// 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 version; // == 1 in Mac OS X 10.4
|
||||||
uint32_t infoArrayCount;
|
uint32_t infoArrayCount;
|
||||||
const struct dyld_image_info *infoArray;
|
uint32_t infoArray; // const struct dyld_image_info*
|
||||||
void* notification;
|
uint32_t notification;
|
||||||
bool processDetachedFromSharedRegion;
|
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
|
// some typedefs to isolate 64/32 bit differences
|
||||||
#ifdef __LP64__
|
#ifdef __LP64__
|
||||||
|
@ -77,71 +94,49 @@ typedef mach_header breakpad_mach_header;
|
||||||
typedef segment_command breakpad_mach_segment_command;
|
typedef segment_command breakpad_mach_segment_command;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//==============================================================================
|
// Helper functions to deal with 32-bit/64-bit Mach-O differences.
|
||||||
// A simple wrapper for a mach_header
|
class DynamicImage;
|
||||||
//
|
template<typename MachBits>
|
||||||
// This could be fleshed out with some more interesting methods.
|
bool FindTextSection(DynamicImage& image);
|
||||||
class MachHeader {
|
|
||||||
public:
|
|
||||||
explicit MachHeader(const breakpad_mach_header &header) : header_(header) {}
|
|
||||||
|
|
||||||
void Print() {
|
template<typename MachBits>
|
||||||
printf("magic\t\t: %4x\n", header_.magic);
|
uint32_t GetFileTypeFromHeader(DynamicImage& image);
|
||||||
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_;
|
|
||||||
};
|
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
// Represents a single dynamically loaded mach-o image
|
// Represents a single dynamically loaded mach-o image
|
||||||
class DynamicImage {
|
class DynamicImage {
|
||||||
public:
|
public:
|
||||||
DynamicImage(breakpad_mach_header *header, // we take ownership
|
DynamicImage(uint8_t *header, // data is copied
|
||||||
size_t header_size, // includes load commands
|
size_t header_size, // includes load commands
|
||||||
breakpad_mach_header *load_address,
|
uint64_t load_address,
|
||||||
char *inFilePath,
|
string file_path,
|
||||||
uintptr_t image_mod_date,
|
uintptr_t image_mod_date,
|
||||||
mach_port_t task)
|
mach_port_t task,
|
||||||
: header_(header),
|
cpu_type_t cpu_type)
|
||||||
|
: header_(header, header + header_size),
|
||||||
header_size_(header_size),
|
header_size_(header_size),
|
||||||
load_address_(load_address),
|
load_address_(load_address),
|
||||||
vmaddr_(0),
|
vmaddr_(0),
|
||||||
vmsize_(0),
|
vmsize_(0),
|
||||||
slide_(0),
|
slide_(0),
|
||||||
version_(0),
|
version_(0),
|
||||||
file_path_(NULL),
|
file_path_(file_path),
|
||||||
file_mod_date_(image_mod_date),
|
file_mod_date_(image_mod_date),
|
||||||
task_(task) {
|
task_(task),
|
||||||
InitializeFilePath(inFilePath);
|
cpu_type_(cpu_type) {
|
||||||
CalculateMemoryAndVersionInfo();
|
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 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
|
// 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
|
// 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
|
// Address where the image should be loaded
|
||||||
mach_vm_address_t GetVMAddr() const {return vmaddr_;}
|
mach_vm_address_t GetVMAddr() const {return vmaddr_;}
|
||||||
|
@ -155,49 +150,49 @@ class DynamicImage {
|
||||||
// Task owning this loaded image
|
// Task owning this loaded image
|
||||||
mach_port_t GetTask() {return task_;}
|
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_;}
|
uint32_t GetVersion() {return version_;}
|
||||||
// For sorting
|
// For sorting
|
||||||
bool operator<(const DynamicImage &inInfo) {
|
bool operator<(const DynamicImage &inInfo) {
|
||||||
return GetLoadAddress() < inInfo.GetLoadAddress();
|
return GetLoadAddress() < inInfo.GetLoadAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debugging
|
// Sanity checking
|
||||||
void Print();
|
bool IsValid() {return GetVMSize() != 0;}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DynamicImage(const DynamicImage &);
|
DynamicImage(const DynamicImage &);
|
||||||
DynamicImage &operator=(const DynamicImage &);
|
DynamicImage &operator=(const DynamicImage &);
|
||||||
|
|
||||||
friend class DynamicImages;
|
friend class DynamicImages;
|
||||||
|
template<typename MachBits>
|
||||||
// Sanity checking
|
friend bool FindTextSection(DynamicImage& image);
|
||||||
bool IsValid() {return GetVMSize() != 0;}
|
template<typename MachBits>
|
||||||
|
friend uint32_t GetFileTypeFromHeader(DynamicImage& image);
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initializes vmaddr_, vmsize_, and slide_
|
// Initializes vmaddr_, vmsize_, and slide_
|
||||||
void CalculateMemoryAndVersionInfo();
|
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
|
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_address_t vmaddr_;
|
||||||
mach_vm_size_t vmsize_;
|
mach_vm_size_t vmsize_;
|
||||||
ptrdiff_t slide_;
|
ptrdiff_t slide_;
|
||||||
uint32_t version_; // Dylib version
|
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
|
uintptr_t file_mod_date_; // time_t of image file
|
||||||
|
|
||||||
mach_port_t task_;
|
mach_port_t task_;
|
||||||
|
cpu_type_t cpu_type_; // CPU type of task_
|
||||||
};
|
};
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
@ -230,6 +225,11 @@ class DynamicImageRef {
|
||||||
DynamicImage *p;
|
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 object of type DynamicImages may be created to allow introspection of
|
||||||
// an arbitrary task's dynamically loaded mach-o binaries. This makes the
|
// 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.
|
// Returns the task which we're looking at.
|
||||||
mach_port_t GetTask() const {return task_;}
|
mach_port_t GetTask() const {return task_;}
|
||||||
|
|
||||||
// Debugging
|
// CPU type of the task
|
||||||
void Print() {
|
cpu_type_t GetCPUType() {return cpu_type_;}
|
||||||
for (int i = 0; i < GetImageCount(); ++i) {
|
|
||||||
image_list_[i]->Print();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestPrint() {
|
// Return true if the task is a 64-bit architecture.
|
||||||
const breakpad_mach_header *header;
|
bool Is64Bit() { return (GetCPUType() & CPU_ARCH_ABI64) == CPU_ARCH_ABI64; }
|
||||||
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);
|
// Determine the CPU type of the task being dumped.
|
||||||
header = reinterpret_cast<const breakpad_mach_header*>(imageHeader);
|
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:
|
private:
|
||||||
|
template<typename MachBits>
|
||||||
|
friend void ReadImageInfo(DynamicImages& images, uint64_t image_list_address);
|
||||||
|
|
||||||
bool IsOurTask() {return task_ == mach_task_self();}
|
bool IsOurTask() {return task_ == mach_task_self();}
|
||||||
|
|
||||||
// Initialization
|
// Initialization
|
||||||
void ReadImageInfoForTask();
|
void ReadImageInfoForTask();
|
||||||
void* GetDyldAllImageInfosPointer();
|
uint64_t GetDyldAllImageInfosPointer();
|
||||||
|
|
||||||
mach_port_t task_;
|
mach_port_t task_;
|
||||||
|
cpu_type_t cpu_type_; // CPU type of task_
|
||||||
vector<DynamicImageRef> image_list_;
|
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.
|
// location in another task.
|
||||||
void* ReadTaskMemory(task_port_t target_task,
|
kern_return_t ReadTaskMemory(task_port_t target_task,
|
||||||
const void* address,
|
const uint64_t address,
|
||||||
size_t len,
|
size_t length,
|
||||||
kern_return_t *kr);
|
vector<uint8_t> &bytes);
|
||||||
|
|
||||||
} // namespace google_breakpad
|
} // namespace google_breakpad
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,9 @@
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
#include <mach/host_info.h>
|
#include <mach/host_info.h>
|
||||||
|
#include <mach/i386/thread_status.h>
|
||||||
#include <mach/mach_vm.h>
|
#include <mach/mach_vm.h>
|
||||||
|
#include <mach/ppc/thread_status.h>
|
||||||
#include <mach/vm_statistics.h>
|
#include <mach/vm_statistics.h>
|
||||||
#include <mach-o/dyld.h>
|
#include <mach-o/dyld.h>
|
||||||
#include <mach-o/loader.h>
|
#include <mach-o/loader.h>
|
||||||
|
@ -65,6 +67,7 @@ MinidumpGenerator::MinidumpGenerator()
|
||||||
exception_thread_(0),
|
exception_thread_(0),
|
||||||
crashing_task_(mach_task_self()),
|
crashing_task_(mach_task_self()),
|
||||||
handler_thread_(mach_thread_self()),
|
handler_thread_(mach_thread_self()),
|
||||||
|
cpu_type_(DynamicImages::GetNativeCPUType()),
|
||||||
dynamic_images_(NULL),
|
dynamic_images_(NULL),
|
||||||
memory_blocks_(&allocator_) {
|
memory_blocks_(&allocator_) {
|
||||||
GatherSystemInformation();
|
GatherSystemInformation();
|
||||||
|
@ -81,12 +84,15 @@ MinidumpGenerator::MinidumpGenerator(mach_port_t crashing_task,
|
||||||
exception_thread_(0),
|
exception_thread_(0),
|
||||||
crashing_task_(crashing_task),
|
crashing_task_(crashing_task),
|
||||||
handler_thread_(handler_thread),
|
handler_thread_(handler_thread),
|
||||||
|
cpu_type_(DynamicImages::GetNativeCPUType()),
|
||||||
dynamic_images_(NULL),
|
dynamic_images_(NULL),
|
||||||
memory_blocks_(&allocator_) {
|
memory_blocks_(&allocator_) {
|
||||||
if (crashing_task != mach_task_self()) {
|
if (crashing_task != mach_task_self()) {
|
||||||
dynamic_images_ = new DynamicImages(crashing_task_);
|
dynamic_images_ = new DynamicImages(crashing_task_);
|
||||||
|
cpu_type_ = dynamic_images_->GetCPUType();
|
||||||
} else {
|
} else {
|
||||||
dynamic_images_ = NULL;
|
dynamic_images_ = NULL;
|
||||||
|
cpu_type_ = DynamicImages::GetNativeCPUType();
|
||||||
}
|
}
|
||||||
|
|
||||||
GatherSystemInformation();
|
GatherSystemInformation();
|
||||||
|
@ -254,8 +260,10 @@ size_t MinidumpGenerator::CalculateStackSize(mach_vm_address_t start_addr) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (((cpu_type_ & CPU_ARCH_ABI64) &&
|
||||||
if ((stack_region_base + stack_region_size) == TOP_OF_THREAD0_STACK) {
|
(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
|
// The stack for thread 0 needs to extend all the way to
|
||||||
// 0xc0000000 on 32 bit and 00007fff5fc00000 on 64bit. HOWEVER,
|
// 0xc0000000 on 32 bit and 00007fff5fc00000 on 64bit. HOWEVER,
|
||||||
// for many processes, the stack is first created in one page
|
// for many processes, the stack is first created in one page
|
||||||
|
@ -305,20 +313,15 @@ bool MinidumpGenerator::WriteStackFromStartAddress(
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (dynamic_images_) {
|
if (dynamic_images_) {
|
||||||
|
vector<uint8_t> stack_memory;
|
||||||
kern_return_t kr;
|
if (ReadTaskMemory(crashing_task_,
|
||||||
|
start_addr,
|
||||||
void *stack_memory = ReadTaskMemory(crashing_task_,
|
size,
|
||||||
(void*)start_addr,
|
stack_memory) != KERN_SUCCESS) {
|
||||||
size,
|
|
||||||
&kr);
|
|
||||||
|
|
||||||
if (stack_memory == NULL) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = memory.Copy(stack_memory, size);
|
result = memory.Copy(&stack_memory[0], size);
|
||||||
free(stack_memory);
|
|
||||||
} else {
|
} else {
|
||||||
result = memory.Copy(reinterpret_cast<const void *>(start_addr), size);
|
result = memory.Copy(reinterpret_cast<const void *>(start_addr), size);
|
||||||
}
|
}
|
||||||
|
@ -330,34 +333,99 @@ bool MinidumpGenerator::WriteStackFromStartAddress(
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if TARGET_CPU_PPC || TARGET_CPU_PPC64
|
|
||||||
bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state,
|
bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state,
|
||||||
MDMemoryDescriptor *stack_location) {
|
MDMemoryDescriptor *stack_location) {
|
||||||
breakpad_thread_state_t *machine_state =
|
switch (cpu_type_) {
|
||||||
reinterpret_cast<breakpad_thread_state_t *>(state);
|
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);
|
mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, r1);
|
||||||
return WriteStackFromStartAddress(start_addr, stack_location);
|
return WriteStackFromStartAddress(start_addr, stack_location);
|
||||||
}
|
}
|
||||||
|
|
||||||
u_int64_t
|
u_int64_t
|
||||||
MinidumpGenerator::CurrentPCForStack(breakpad_thread_state_data_t state) {
|
MinidumpGenerator::CurrentPCForStackPPC(breakpad_thread_state_data_t state) {
|
||||||
breakpad_thread_state_t *machine_state =
|
ppc_thread_state_t *machine_state =
|
||||||
reinterpret_cast<breakpad_thread_state_t *>(state);
|
reinterpret_cast<ppc_thread_state_t *>(state);
|
||||||
|
|
||||||
return REGISTER_FROM_THREADSTATE(machine_state, srr0);
|
return REGISTER_FROM_THREADSTATE(machine_state, srr0);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
|
u_int64_t
|
||||||
MDLocationDescriptor *register_location) {
|
MinidumpGenerator::CurrentPCForStackPPC64(breakpad_thread_state_data_t state) {
|
||||||
TypedMDRVA<MinidumpContext> context(&writer_);
|
ppc_thread_state64_t *machine_state =
|
||||||
breakpad_thread_state_t *machine_state =
|
reinterpret_cast<ppc_thread_state64_t *>(state);
|
||||||
reinterpret_cast<breakpad_thread_state_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())
|
if (!context.Allocate())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
*register_location = context.location();
|
*register_location = context.location();
|
||||||
MinidumpContext *context_ptr = context.get();
|
MDRawContextPPC *context_ptr = context.get();
|
||||||
context_ptr->context_flags = MD_CONTEXT_PPC_BASE;
|
context_ptr->context_flags = MD_CONTEXT_PPC_BASE;
|
||||||
|
|
||||||
#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a)
|
#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(29);
|
||||||
AddGPR(30);
|
AddGPR(30);
|
||||||
AddGPR(31);
|
AddGPR(31);
|
||||||
|
|
||||||
#if TARGET_CPU_PPC
|
|
||||||
/* The mq register is only for PPC */
|
|
||||||
AddReg(mq);
|
AddReg(mq);
|
||||||
#endif
|
#undef AddReg
|
||||||
|
#undef AddGPR
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif TARGET_CPU_X86 || TARGET_CPU_X86_64
|
bool MinidumpGenerator::WriteContextPPC64(
|
||||||
|
breakpad_thread_state_data_t state,
|
||||||
bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state,
|
MDLocationDescriptor *register_location) {
|
||||||
MDMemoryDescriptor *stack_location) {
|
TypedMDRVA<MDRawContextPPC64> context(&writer_);
|
||||||
breakpad_thread_state_t *machine_state =
|
ppc_thread_state64_t *machine_state =
|
||||||
reinterpret_cast<breakpad_thread_state_t *>(state);
|
reinterpret_cast<ppc_thread_state64_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);
|
|
||||||
|
|
||||||
if (!context.Allocate())
|
if (!context.Allocate())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
*register_location = context.location();
|
*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)
|
#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;
|
context_ptr->context_flags = MD_CONTEXT_X86;
|
||||||
AddReg(eax);
|
AddReg(eax);
|
||||||
AddReg(ebx);
|
AddReg(ebx);
|
||||||
|
@ -472,7 +607,26 @@ bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
|
||||||
AddReg(eflags);
|
AddReg(eflags);
|
||||||
|
|
||||||
AddReg(eip);
|
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;
|
context_ptr->context_flags = MD_CONTEXT_AMD64;
|
||||||
AddReg(rax);
|
AddReg(rax);
|
||||||
AddReg(rbx);
|
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
|
// not used in the flags register. Since the minidump format
|
||||||
// specifies 32 bits for the flags register, we can truncate safely
|
// specifies 32 bits for the flags register, we can truncate safely
|
||||||
// with no loss.
|
// 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(cs);
|
||||||
AddReg(fs);
|
AddReg(fs);
|
||||||
AddReg(gs);
|
AddReg(gs);
|
||||||
#endif
|
|
||||||
#undef AddReg(a)
|
#undef AddReg(a)
|
||||||
|
|
||||||
return true;
|
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,
|
bool MinidumpGenerator::WriteThreadStream(mach_port_t thread_id,
|
||||||
MDRawThread *thread) {
|
MDRawThread *thread) {
|
||||||
|
@ -512,9 +688,7 @@ bool MinidumpGenerator::WriteThreadStream(mach_port_t thread_id,
|
||||||
mach_msg_type_number_t state_count
|
mach_msg_type_number_t state_count
|
||||||
= static_cast<mach_msg_type_number_t>(sizeof(state));
|
= static_cast<mach_msg_type_number_t>(sizeof(state));
|
||||||
|
|
||||||
if (thread_get_state(thread_id, BREAKPAD_MACHINE_THREAD_STATE,
|
if (GetThreadState(thread_id, state, &state_count)) {
|
||||||
state, &state_count) ==
|
|
||||||
KERN_SUCCESS) {
|
|
||||||
if (!WriteStack(state, &thread->stack))
|
if (!WriteStack(state, &thread->stack))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -653,21 +827,15 @@ bool MinidumpGenerator::WriteMemoryListStream(
|
||||||
|
|
||||||
if (dynamic_images_) {
|
if (dynamic_images_) {
|
||||||
// Out-of-process.
|
// Out-of-process.
|
||||||
kern_return_t kr;
|
vector<uint8_t> memory;
|
||||||
|
if (ReadTaskMemory(crashing_task_,
|
||||||
void *memory =
|
ip_memory_d.start_of_memory_range,
|
||||||
ReadTaskMemory(
|
ip_memory_d.memory.data_size,
|
||||||
crashing_task_,
|
memory) != KERN_SUCCESS) {
|
||||||
reinterpret_cast<const void *>(ip_memory_d.start_of_memory_range),
|
|
||||||
ip_memory_d.memory.data_size,
|
|
||||||
&kr);
|
|
||||||
|
|
||||||
if (memory == NULL) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ip_memory.Copy(memory, ip_memory_d.memory.data_size);
|
ip_memory.Copy(&memory[0], ip_memory_d.memory.data_size);
|
||||||
free(memory);
|
|
||||||
} else {
|
} else {
|
||||||
// In-process, just copy from local memory.
|
// In-process, just copy from local memory.
|
||||||
ip_memory.Copy(
|
ip_memory.Copy(
|
||||||
|
@ -733,23 +901,23 @@ bool MinidumpGenerator::WriteSystemInfoStream(
|
||||||
system_info_stream->location = info.location();
|
system_info_stream->location = info.location();
|
||||||
|
|
||||||
// CPU Information
|
// 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;
|
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);
|
sysctlbyname("hw.ncpu", &number_of_processors, &len, NULL, 0);
|
||||||
MDRawSystemInfo *info_ptr = info.get();
|
MDRawSystemInfo *info_ptr = info.get();
|
||||||
|
|
||||||
switch (cpu_type) {
|
switch (cpu_type_) {
|
||||||
case CPU_TYPE_POWERPC:
|
case CPU_TYPE_POWERPC:
|
||||||
|
case CPU_TYPE_POWERPC64:
|
||||||
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_PPC;
|
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_PPC;
|
||||||
break;
|
break;
|
||||||
case CPU_TYPE_I386:
|
case CPU_TYPE_I386:
|
||||||
case CPU_TYPE_X86_64:
|
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__
|
#ifdef __i386__
|
||||||
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_X86;
|
|
||||||
// ebx is used for PIC code, so we need
|
// ebx is used for PIC code, so we need
|
||||||
// to preserve it.
|
// to preserve it.
|
||||||
#define cpuid(op,eax,ebx,ecx,edx) \
|
#define cpuid(op,eax,ebx,ecx,edx) \
|
||||||
|
@ -763,7 +931,7 @@ bool MinidumpGenerator::WriteSystemInfoStream(
|
||||||
"=d" (edx) \
|
"=d" (edx) \
|
||||||
: "0" (op))
|
: "0" (op))
|
||||||
#elif defined(__x86_64__)
|
#elif defined(__x86_64__)
|
||||||
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_AMD64;
|
|
||||||
#define cpuid(op,eax,ebx,ecx,edx) \
|
#define cpuid(op,eax,ebx,ecx,edx) \
|
||||||
asm ("cpuid \n\t" \
|
asm ("cpuid \n\t" \
|
||||||
: "=a" (eax), \
|
: "=a" (eax), \
|
||||||
|
@ -837,19 +1005,12 @@ bool MinidumpGenerator::WriteModuleStream(unsigned int index,
|
||||||
if (!image)
|
if (!image)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const breakpad_mach_header *header = image->GetMachHeader();
|
|
||||||
|
|
||||||
if (!header)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
int cpu_type = header->cputype;
|
|
||||||
|
|
||||||
memset(module, 0, sizeof(MDRawModule));
|
memset(module, 0, sizeof(MDRawModule));
|
||||||
|
|
||||||
MDLocationDescriptor string_location;
|
MDLocationDescriptor string_location;
|
||||||
|
|
||||||
const char* name = image->GetFilePath();
|
string name = image->GetFilePath();
|
||||||
if (!writer_.WriteString(name, 0, &string_location))
|
if (!writer_.WriteString(name.c_str(), 0, &string_location))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
module->base_of_image = image->GetVMAddr() + image->GetVMAddrSlide();
|
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);
|
module->version_info.file_version_lo |= (modVersion & 0xff);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!WriteCVRecord(module, cpu_type, name)) {
|
if (!WriteCVRecord(module, image->GetCPUType(), name.c_str())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// we're getting module info in the crashed process
|
// Getting module info in the crashed process
|
||||||
|
|
||||||
const breakpad_mach_header *header;
|
const breakpad_mach_header *header;
|
||||||
header = (breakpad_mach_header*)_dyld_get_image_header(index);
|
header = (breakpad_mach_header*)_dyld_get_image_header(index);
|
||||||
if (!header)
|
if (!header)
|
||||||
|
@ -903,7 +1063,7 @@ bool MinidumpGenerator::WriteModuleStream(unsigned int index,
|
||||||
unsigned long slide = _dyld_get_image_vmaddr_slide(index);
|
unsigned long slide = _dyld_get_image_vmaddr_slide(index);
|
||||||
const char* name = _dyld_get_image_name(index);
|
const char* name = _dyld_get_image_name(index);
|
||||||
const struct load_command *cmd =
|
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));
|
memset(module, 0, sizeof(MDRawModule));
|
||||||
|
|
||||||
|
@ -911,7 +1071,7 @@ bool MinidumpGenerator::WriteModuleStream(unsigned int index,
|
||||||
if (cmd->cmd == LC_SEGMENT_ARCH) {
|
if (cmd->cmd == LC_SEGMENT_ARCH) {
|
||||||
|
|
||||||
const breakpad_mach_segment_command *seg =
|
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")) {
|
if (!strcmp(seg->segname, "__TEXT")) {
|
||||||
MDLocationDescriptor string_location;
|
MDLocationDescriptor string_location;
|
||||||
|
|
|
@ -47,25 +47,8 @@ namespace google_breakpad {
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
|
|
||||||
#if TARGET_CPU_X86_64 || TARGET_CPU_PPC64
|
const u_int64_t TOP_OF_THREAD0_STACK_64BIT = 0x00007fff5fbff000LL;
|
||||||
#define TOP_OF_THREAD0_STACK 0x00007fff5fbff000
|
const u_int32_t TOP_OF_THREAD0_STACK_32BIT = 0xbffff000;
|
||||||
#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
|
|
||||||
|
|
||||||
// Use the REGISTER_FROM_THREADSTATE to access a register name from the
|
// Use the REGISTER_FROM_THREADSTATE to access a register name from the
|
||||||
// breakpad_thread_state_t structure.
|
// breakpad_thread_state_t structure.
|
||||||
|
@ -129,6 +112,8 @@ class MinidumpGenerator {
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
u_int64_t CurrentPCForStack(breakpad_thread_state_data_t state);
|
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,
|
bool WriteStackFromStartAddress(mach_vm_address_t start_addr,
|
||||||
MDMemoryDescriptor *stack_location);
|
MDMemoryDescriptor *stack_location);
|
||||||
bool WriteStack(breakpad_thread_state_data_t state,
|
bool WriteStack(breakpad_thread_state_data_t state,
|
||||||
|
@ -139,11 +124,31 @@ class MinidumpGenerator {
|
||||||
bool WriteCVRecord(MDRawModule *module, int cpu_type,
|
bool WriteCVRecord(MDRawModule *module, int cpu_type,
|
||||||
const char *module_path);
|
const char *module_path);
|
||||||
bool WriteModuleStream(unsigned int index, MDRawModule *module);
|
bool WriteModuleStream(unsigned int index, MDRawModule *module);
|
||||||
|
|
||||||
size_t CalculateStackSize(mach_vm_address_t start_addr);
|
size_t CalculateStackSize(mach_vm_address_t start_addr);
|
||||||
|
|
||||||
int FindExecutableModule();
|
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=
|
// disallow copy ctor and operator=
|
||||||
explicit MinidumpGenerator(const MinidumpGenerator &);
|
explicit MinidumpGenerator(const MinidumpGenerator &);
|
||||||
void operator=(const MinidumpGenerator &);
|
void operator=(const MinidumpGenerator &);
|
||||||
|
@ -158,6 +163,9 @@ class MinidumpGenerator {
|
||||||
mach_port_t exception_thread_;
|
mach_port_t exception_thread_;
|
||||||
mach_port_t crashing_task_;
|
mach_port_t crashing_task_;
|
||||||
mach_port_t handler_thread_;
|
mach_port_t handler_thread_;
|
||||||
|
|
||||||
|
// CPU type of the task being dumped.
|
||||||
|
cpu_type_t cpu_type_;
|
||||||
|
|
||||||
// System information
|
// System information
|
||||||
static char build_string_[16];
|
static char build_string_[16];
|
||||||
|
|
|
@ -29,10 +29,21 @@
|
||||||
|
|
||||||
// minidump_generator_test.cc: Unit tests for google_breakpad::MinidumpGenerator
|
// 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 <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 <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "breakpad_googletest_includes.h"
|
#include "breakpad_googletest_includes.h"
|
||||||
#include "client/mac/handler/minidump_generator.h"
|
#include "client/mac/handler/minidump_generator.h"
|
||||||
#include "client/mac/tests/auto_tempdir.h"
|
#include "client/mac/tests/auto_tempdir.h"
|
||||||
|
@ -48,6 +59,7 @@ std::ostringstream info_log;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
using std::string;
|
using std::string;
|
||||||
|
using std::vector;
|
||||||
using google_breakpad::AutoTempDir;
|
using google_breakpad::AutoTempDir;
|
||||||
using google_breakpad::MinidumpGenerator;
|
using google_breakpad::MinidumpGenerator;
|
||||||
using google_breakpad::MachPortSender;
|
using google_breakpad::MachPortSender;
|
||||||
|
@ -161,7 +173,7 @@ TEST_F(MinidumpGeneratorTest, OutOfProcess) {
|
||||||
const int kTimeoutMs = 2000;
|
const int kTimeoutMs = 2000;
|
||||||
// Create a mach port to receive the child task on.
|
// Create a mach port to receive the child task on.
|
||||||
char machPortName[128];
|
char machPortName[128];
|
||||||
sprintf(machPortName, "MinidumpGeneratorTest.%d", getpid());
|
sprintf(machPortName, "MinidumpGeneratorTest.OutOfProcess.%d", getpid());
|
||||||
ReceivePort parent_recv_port(machPortName);
|
ReceivePort parent_recv_port(machPortName);
|
||||||
|
|
||||||
// Give the child process a pipe to block on.
|
// Give the child process a pipe to block on.
|
||||||
|
@ -248,4 +260,150 @@ TEST_F(MinidumpGeneratorTest, OutOfProcess) {
|
||||||
EXPECT_EQ(GetExecutablePath(), main_module->code_file());
|
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)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
63
src/client/mac/tests/minidump_generator_test_helper.cc
Normal file
63
src/client/mac/tests/minidump_generator_test_helper.cc
Normal 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;
|
||||||
|
}
|
|
@ -839,7 +839,7 @@ bool MinidumpContext::CheckAgainstSystemInfo(u_int32_t context_cpu_type) {
|
||||||
|
|
||||||
BPLOG_IF(ERROR, !return_value) << "MinidumpContext CPU " <<
|
BPLOG_IF(ERROR, !return_value) << "MinidumpContext CPU " <<
|
||||||
HexString(context_cpu_type) <<
|
HexString(context_cpu_type) <<
|
||||||
" wrong for MinidumpSysmtemInfo CPU " <<
|
" wrong for MinidumpSystemInfo CPU " <<
|
||||||
HexString(system_info_cpu_type);
|
HexString(system_info_cpu_type);
|
||||||
|
|
||||||
return return_value;
|
return return_value;
|
||||||
|
|
Loading…
Reference in a new issue