summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThanos Doukoudakis <thanos.doukoudakis@isode.com>2017-10-16 11:53:42 (GMT)
committerKevin Smith <kevin.smith@isode.com>2017-10-19 09:22:57 (GMT)
commiteb07238e9c1a09a640dae06e8a433d7dba77f490 (patch)
tree51b618bbdf933bcca2b8deb915e131c48d80d5f7 /3rdParty/Breakpad/src/common/mac
parent80ef26c165a08d5251d7ee56e0bd07b86fc82f55 (diff)
downloadswift-eb07238e9c1a09a640dae06e8a433d7dba77f490.zip
swift-eb07238e9c1a09a640dae06e8a433d7dba77f490.tar.bz2
Upgrade Breakpad to latest
This commit will upgrade breakpad to version 1.0.86 (I9957f27cd134f862b9831e4b1d90f8a014eb37b6) from https://chromium.googlesource.com/breakpad/breakpad Added a script(BreakpadSwiftCleanup.sh) that remove files from Breakpad's repository that are not used by Swift. This commit also re-applies the changes that were introduced in commit 7f0fe603be200c09c74cf9cc295a972f3c3dbdfd, that change the minidump filename format to include version and date Test-Information: https://travis-ci.org/google/breakpad/builds/283789304 https://ci.appveyor.com/project/vapier/breakpad/build/job/1bu73ysmcfpwg9e4 Tested by adding some code that forces a crash to the client on Windows 10 with VS2015. Verified the name and contents of the generated crash dump. Change-Id: Ied9e74088e43137845edc09d070c2c27494aeade
Diffstat (limited to '3rdParty/Breakpad/src/common/mac')
-rw-r--r--3rdParty/Breakpad/src/common/mac/Breakpad.xcconfig52
-rw-r--r--3rdParty/Breakpad/src/common/mac/BreakpadDebug.xcconfig32
-rw-r--r--3rdParty/Breakpad/src/common/mac/BreakpadRelease.xcconfig34
-rw-r--r--3rdParty/Breakpad/src/common/mac/GTMDefines.h456
-rw-r--r--3rdParty/Breakpad/src/common/mac/GTMLogger.h504
-rw-r--r--3rdParty/Breakpad/src/common/mac/GTMLogger.m611
-rw-r--r--3rdParty/Breakpad/src/common/mac/HTTPMultipartUpload.h61
-rw-r--r--3rdParty/Breakpad/src/common/mac/HTTPMultipartUpload.m267
-rw-r--r--3rdParty/Breakpad/src/common/mac/MachIPC.h8
-rw-r--r--3rdParty/Breakpad/src/common/mac/arch_utilities.cc231
-rw-r--r--3rdParty/Breakpad/src/common/mac/arch_utilities.h47
-rw-r--r--3rdParty/Breakpad/src/common/mac/byteswap.h25
-rw-r--r--3rdParty/Breakpad/src/common/mac/dump_syms.cc (renamed from 3rdParty/Breakpad/src/common/mac/dump_syms.mm)476
-rw-r--r--3rdParty/Breakpad/src/common/mac/dump_syms.h76
-rw-r--r--3rdParty/Breakpad/src/common/mac/file_id.cc19
-rw-r--r--3rdParty/Breakpad/src/common/mac/file_id.h17
-rw-r--r--3rdParty/Breakpad/src/common/mac/launch_reporter.cc84
-rw-r--r--3rdParty/Breakpad/src/common/mac/launch_reporter.h43
-rw-r--r--3rdParty/Breakpad/src/common/mac/macho_id.cc65
-rw-r--r--3rdParty/Breakpad/src/common/mac/macho_id.h33
-rw-r--r--3rdParty/Breakpad/src/common/mac/macho_reader.cc47
-rw-r--r--3rdParty/Breakpad/src/common/mac/macho_reader.h21
-rw-r--r--3rdParty/Breakpad/src/common/mac/macho_reader_unittest.cc1902
-rw-r--r--3rdParty/Breakpad/src/common/mac/macho_utilities.cc89
-rw-r--r--3rdParty/Breakpad/src/common/mac/macho_utilities.h35
-rw-r--r--3rdParty/Breakpad/src/common/mac/macho_walker.cc69
-rw-r--r--3rdParty/Breakpad/src/common/mac/macho_walker.h26
-rw-r--r--3rdParty/Breakpad/src/common/mac/string_utilities.cc2
-rw-r--r--3rdParty/Breakpad/src/common/mac/super_fat_arch.h88
29 files changed, 5067 insertions, 353 deletions
diff --git a/3rdParty/Breakpad/src/common/mac/Breakpad.xcconfig b/3rdParty/Breakpad/src/common/mac/Breakpad.xcconfig
new file mode 100644
index 0000000..f091369
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/mac/Breakpad.xcconfig
@@ -0,0 +1,52 @@
+// 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.
+
+GCC_C_LANGUAGE_STANDARD = c99
+
+GCC_WARN_CHECK_SWITCH_STATEMENTS = YES
+// TODO(nealsid): Get the code so we can turn on the 64_TO_32 warning.
+GCC_WARN_64_TO_32_BIT_CONVERSION = NO
+GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES
+GCC_WARN_ABOUT_RETURN_TYPE = YES
+GCC_WARN_MISSING_PARENTHESES = YES
+
+// Once https://bugs.chromium.org/p/google-breakpad/issues/detail?id=697
+// is fixed this should be reenabled.
+//GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES
+GCC_WARN_ABOUT_MISSING_NEWLINE = YES
+GCC_WARN_SIGN_COMPARE = YES
+GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES
+GCC_WARN_UNDECLARED_SELECTOR = YES
+GCC_WARN_UNKNOWN_PRAGMAS = YES
+GCC_WARN_UNUSED_VARIABLE = YES
+GCC_TREAT_WARNINGS_AS_ERRORS = YES
+
+DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
+
+ALWAYS_SEARCH_USER_PATHS = NO
diff --git a/3rdParty/Breakpad/src/common/mac/BreakpadDebug.xcconfig b/3rdParty/Breakpad/src/common/mac/BreakpadDebug.xcconfig
new file mode 100644
index 0000000..94cdd8c
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/mac/BreakpadDebug.xcconfig
@@ -0,0 +1,32 @@
+// 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.
+
+#include "Breakpad.xcconfig"
+
+GCC_OPTIMIZATION_LEVEL = 0
diff --git a/3rdParty/Breakpad/src/common/mac/BreakpadRelease.xcconfig b/3rdParty/Breakpad/src/common/mac/BreakpadRelease.xcconfig
new file mode 100644
index 0000000..920f277
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/mac/BreakpadRelease.xcconfig
@@ -0,0 +1,34 @@
+// 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.
+
+#include "Breakpad.xcconfig"
+
+GCC_OPTIMIZATION_LEVEL = s
+GCC_WARN_UNINITIALIZED_AUTOS = YES
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) NDEBUG
diff --git a/3rdParty/Breakpad/src/common/mac/GTMDefines.h b/3rdParty/Breakpad/src/common/mac/GTMDefines.h
new file mode 100644
index 0000000..04fcf6d
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/mac/GTMDefines.h
@@ -0,0 +1,456 @@
+//
+// GTMDefines.h
+//
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+// ============================================================================
+
+#include <AvailabilityMacros.h>
+#include <TargetConditionals.h>
+
+#ifdef __OBJC__
+#include <Foundation/NSObjCRuntime.h>
+#endif // __OBJC__
+
+#if TARGET_OS_IPHONE
+#include <Availability.h>
+#endif // TARGET_OS_IPHONE
+
+// Not all MAC_OS_X_VERSION_10_X macros defined in past SDKs
+#ifndef MAC_OS_X_VERSION_10_5
+ #define MAC_OS_X_VERSION_10_5 1050
+#endif
+#ifndef MAC_OS_X_VERSION_10_6
+ #define MAC_OS_X_VERSION_10_6 1060
+#endif
+#ifndef MAC_OS_X_VERSION_10_7
+ #define MAC_OS_X_VERSION_10_7 1070
+#endif
+
+// Not all __IPHONE_X macros defined in past SDKs
+#ifndef __IPHONE_3_0
+ #define __IPHONE_3_0 30000
+#endif
+#ifndef __IPHONE_3_1
+ #define __IPHONE_3_1 30100
+#endif
+#ifndef __IPHONE_3_2
+ #define __IPHONE_3_2 30200
+#endif
+#ifndef __IPHONE_4_0
+ #define __IPHONE_4_0 40000
+#endif
+#ifndef __IPHONE_4_3
+ #define __IPHONE_4_3 40300
+#endif
+#ifndef __IPHONE_5_0
+ #define __IPHONE_5_0 50000
+#endif
+
+// ----------------------------------------------------------------------------
+// CPP symbols that can be overridden in a prefix to control how the toolbox
+// is compiled.
+// ----------------------------------------------------------------------------
+
+
+// By setting the GTM_CONTAINERS_VALIDATION_FAILED_LOG and
+// GTM_CONTAINERS_VALIDATION_FAILED_ASSERT macros you can control what happens
+// when a validation fails. If you implement your own validators, you may want
+// to control their internals using the same macros for consistency.
+#ifndef GTM_CONTAINERS_VALIDATION_FAILED_ASSERT
+ #define GTM_CONTAINERS_VALIDATION_FAILED_ASSERT 0
+#endif
+
+// Give ourselves a consistent way to do inlines. Apple's macros even use
+// a few different actual definitions, so we're based off of the foundation
+// one.
+#if !defined(GTM_INLINE)
+ #if (defined (__GNUC__) && (__GNUC__ == 4)) || defined (__clang__)
+ #define GTM_INLINE static __inline__ __attribute__((always_inline))
+ #else
+ #define GTM_INLINE static __inline__
+ #endif
+#endif
+
+// Give ourselves a consistent way of doing externs that links up nicely
+// when mixing objc and objc++
+#if !defined (GTM_EXTERN)
+ #if defined __cplusplus
+ #define GTM_EXTERN extern "C"
+ #define GTM_EXTERN_C_BEGIN extern "C" {
+ #define GTM_EXTERN_C_END }
+ #else
+ #define GTM_EXTERN extern
+ #define GTM_EXTERN_C_BEGIN
+ #define GTM_EXTERN_C_END
+ #endif
+#endif
+
+// Give ourselves a consistent way of exporting things if we have visibility
+// set to hidden.
+#if !defined (GTM_EXPORT)
+ #define GTM_EXPORT __attribute__((visibility("default")))
+#endif
+
+// Give ourselves a consistent way of declaring something as unused. This
+// doesn't use __unused because that is only supported in gcc 4.2 and greater.
+#if !defined (GTM_UNUSED)
+#define GTM_UNUSED(x) ((void)(x))
+#endif
+
+// _GTMDevLog & _GTMDevAssert
+//
+// _GTMDevLog & _GTMDevAssert are meant to be a very lightweight shell for
+// developer level errors. This implementation simply macros to NSLog/NSAssert.
+// It is not intended to be a general logging/reporting system.
+//
+// Please see https://github.com/google/google-toolbox-for-mac/wiki/DevLogNAssert
+// for a little more background on the usage of these macros.
+//
+// _GTMDevLog log some error/problem in debug builds
+// _GTMDevAssert assert if conditon isn't met w/in a method/function
+// in all builds.
+//
+// To replace this system, just provide different macro definitions in your
+// prefix header. Remember, any implementation you provide *must* be thread
+// safe since this could be called by anything in what ever situtation it has
+// been placed in.
+//
+
+// We only define the simple macros if nothing else has defined this.
+#ifndef _GTMDevLog
+
+#ifdef DEBUG
+ #define _GTMDevLog(...) NSLog(__VA_ARGS__)
+#else
+ #define _GTMDevLog(...) do { } while (0)
+#endif
+
+#endif // _GTMDevLog
+
+#ifndef _GTMDevAssert
+// we directly invoke the NSAssert handler so we can pass on the varargs
+// (NSAssert doesn't have a macro we can use that takes varargs)
+#if !defined(NS_BLOCK_ASSERTIONS)
+ #define _GTMDevAssert(condition, ...) \
+ do { \
+ if (!(condition)) { \
+ [[NSAssertionHandler currentHandler] \
+ handleFailureInFunction:[NSString stringWithUTF8String:__PRETTY_FUNCTION__] \
+ file:[NSString stringWithUTF8String:__FILE__] \
+ lineNumber:__LINE__ \
+ description:__VA_ARGS__]; \
+ } \
+ } while(0)
+#else // !defined(NS_BLOCK_ASSERTIONS)
+ #define _GTMDevAssert(condition, ...) do { } while (0)
+#endif // !defined(NS_BLOCK_ASSERTIONS)
+
+#endif // _GTMDevAssert
+
+// _GTMCompileAssert
+// _GTMCompileAssert is an assert that is meant to fire at compile time if you
+// want to check things at compile instead of runtime. For example if you
+// want to check that a wchar is 4 bytes instead of 2 you would use
+// _GTMCompileAssert(sizeof(wchar_t) == 4, wchar_t_is_4_bytes_on_OS_X)
+// Note that the second "arg" is not in quotes, and must be a valid processor
+// symbol in it's own right (no spaces, punctuation etc).
+
+// Wrapping this in an #ifndef allows external groups to define their own
+// compile time assert scheme.
+#ifndef _GTMCompileAssert
+ // We got this technique from here:
+ // http://unixjunkie.blogspot.com/2007/10/better-compile-time-asserts_29.html
+
+ #define _GTMCompileAssertSymbolInner(line, msg) _GTMCOMPILEASSERT ## line ## __ ## msg
+ #define _GTMCompileAssertSymbol(line, msg) _GTMCompileAssertSymbolInner(line, msg)
+ #define _GTMCompileAssert(test, msg) \
+ typedef char _GTMCompileAssertSymbol(__LINE__, msg) [ ((test) ? 1 : -1) ]
+#endif // _GTMCompileAssert
+
+// ----------------------------------------------------------------------------
+// CPP symbols defined based on the project settings so the GTM code has
+// simple things to test against w/o scattering the knowledge of project
+// setting through all the code.
+// ----------------------------------------------------------------------------
+
+// Provide a single constant CPP symbol that all of GTM uses for ifdefing
+// iPhone code.
+#if TARGET_OS_IPHONE // iPhone SDK
+ // For iPhone specific stuff
+ #define GTM_IPHONE_SDK 1
+ #if TARGET_IPHONE_SIMULATOR
+ #define GTM_IPHONE_DEVICE 0
+ #define GTM_IPHONE_SIMULATOR 1
+ #else
+ #define GTM_IPHONE_DEVICE 1
+ #define GTM_IPHONE_SIMULATOR 0
+ #endif // TARGET_IPHONE_SIMULATOR
+ // By default, GTM has provided it's own unittesting support, define this
+ // to use the support provided by Xcode, especially for the Xcode4 support
+ // for unittesting.
+ #ifndef GTM_IPHONE_USE_SENTEST
+ #define GTM_IPHONE_USE_SENTEST 0
+ #endif
+ #define GTM_MACOS_SDK 0
+#else
+ // For MacOS specific stuff
+ #define GTM_MACOS_SDK 1
+ #define GTM_IPHONE_SDK 0
+ #define GTM_IPHONE_SIMULATOR 0
+ #define GTM_IPHONE_DEVICE 0
+ #define GTM_IPHONE_USE_SENTEST 0
+#endif
+
+// Some of our own availability macros
+#if GTM_MACOS_SDK
+#define GTM_AVAILABLE_ONLY_ON_IPHONE UNAVAILABLE_ATTRIBUTE
+#define GTM_AVAILABLE_ONLY_ON_MACOS
+#else
+#define GTM_AVAILABLE_ONLY_ON_IPHONE
+#define GTM_AVAILABLE_ONLY_ON_MACOS UNAVAILABLE_ATTRIBUTE
+#endif
+
+// GC was dropped by Apple, define the old constant incase anyone still keys
+// off of it.
+#ifndef GTM_SUPPORT_GC
+ #define GTM_SUPPORT_GC 0
+#endif
+
+// To simplify support for 64bit (and Leopard in general), we provide the type
+// defines for non Leopard SDKs
+#if !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ // NSInteger/NSUInteger and Max/Mins
+ #ifndef NSINTEGER_DEFINED
+ #if (defined(__LP64__) && __LP64__) || NS_BUILD_32_LIKE_64
+ typedef long NSInteger;
+ typedef unsigned long NSUInteger;
+ #else
+ typedef int NSInteger;
+ typedef unsigned int NSUInteger;
+ #endif
+ #define NSIntegerMax LONG_MAX
+ #define NSIntegerMin LONG_MIN
+ #define NSUIntegerMax ULONG_MAX
+ #define NSINTEGER_DEFINED 1
+ #endif // NSINTEGER_DEFINED
+ // CGFloat
+ #ifndef CGFLOAT_DEFINED
+ #if defined(__LP64__) && __LP64__
+ // This really is an untested path (64bit on Tiger?)
+ typedef double CGFloat;
+ #define CGFLOAT_MIN DBL_MIN
+ #define CGFLOAT_MAX DBL_MAX
+ #define CGFLOAT_IS_DOUBLE 1
+ #else /* !defined(__LP64__) || !__LP64__ */
+ typedef float CGFloat;
+ #define CGFLOAT_MIN FLT_MIN
+ #define CGFLOAT_MAX FLT_MAX
+ #define CGFLOAT_IS_DOUBLE 0
+ #endif /* !defined(__LP64__) || !__LP64__ */
+ #define CGFLOAT_DEFINED 1
+ #endif // CGFLOAT_DEFINED
+#endif // MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
+
+// Some support for advanced clang static analysis functionality
+// See http://clang-analyzer.llvm.org/annotations.html
+#ifndef __has_feature // Optional.
+ #define __has_feature(x) 0 // Compatibility with non-clang compilers.
+#endif
+
+#ifndef NS_RETURNS_RETAINED
+ #if __has_feature(attribute_ns_returns_retained)
+ #define NS_RETURNS_RETAINED __attribute__((ns_returns_retained))
+ #else
+ #define NS_RETURNS_RETAINED
+ #endif
+#endif
+
+#ifndef NS_RETURNS_NOT_RETAINED
+ #if __has_feature(attribute_ns_returns_not_retained)
+ #define NS_RETURNS_NOT_RETAINED __attribute__((ns_returns_not_retained))
+ #else
+ #define NS_RETURNS_NOT_RETAINED
+ #endif
+#endif
+
+#ifndef CF_RETURNS_RETAINED
+ #if __has_feature(attribute_cf_returns_retained)
+ #define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
+ #else
+ #define CF_RETURNS_RETAINED
+ #endif
+#endif
+
+#ifndef CF_RETURNS_NOT_RETAINED
+ #if __has_feature(attribute_cf_returns_not_retained)
+ #define CF_RETURNS_NOT_RETAINED __attribute__((cf_returns_not_retained))
+ #else
+ #define CF_RETURNS_NOT_RETAINED
+ #endif
+#endif
+
+#ifndef NS_CONSUMED
+ #if __has_feature(attribute_ns_consumed)
+ #define NS_CONSUMED __attribute__((ns_consumed))
+ #else
+ #define NS_CONSUMED
+ #endif
+#endif
+
+#ifndef CF_CONSUMED
+ #if __has_feature(attribute_cf_consumed)
+ #define CF_CONSUMED __attribute__((cf_consumed))
+ #else
+ #define CF_CONSUMED
+ #endif
+#endif
+
+#ifndef NS_CONSUMES_SELF
+ #if __has_feature(attribute_ns_consumes_self)
+ #define NS_CONSUMES_SELF __attribute__((ns_consumes_self))
+ #else
+ #define NS_CONSUMES_SELF
+ #endif
+#endif
+
+// Defined on 10.6 and above.
+#ifndef NS_FORMAT_ARGUMENT
+ #define NS_FORMAT_ARGUMENT(A)
+#endif
+
+// Defined on 10.6 and above.
+#ifndef NS_FORMAT_FUNCTION
+ #define NS_FORMAT_FUNCTION(F,A)
+#endif
+
+// Defined on 10.6 and above.
+#ifndef CF_FORMAT_ARGUMENT
+ #define CF_FORMAT_ARGUMENT(A)
+#endif
+
+// Defined on 10.6 and above.
+#ifndef CF_FORMAT_FUNCTION
+ #define CF_FORMAT_FUNCTION(F,A)
+#endif
+
+#ifndef GTM_NONNULL
+ #if defined(__has_attribute)
+ #if __has_attribute(nonnull)
+ #define GTM_NONNULL(x) __attribute__((nonnull x))
+ #else
+ #define GTM_NONNULL(x)
+ #endif
+ #else
+ #define GTM_NONNULL(x)
+ #endif
+#endif
+
+// Invalidates the initializer from which it's called.
+#ifndef GTMInvalidateInitializer
+ #if __has_feature(objc_arc)
+ #define GTMInvalidateInitializer() \
+ do { \
+ [self class]; /* Avoid warning of dead store to |self|. */ \
+ _GTMDevAssert(NO, @"Invalid initializer."); \
+ return nil; \
+ } while (0)
+ #else
+ #define GTMInvalidateInitializer() \
+ do { \
+ [self release]; \
+ _GTMDevAssert(NO, @"Invalid initializer."); \
+ return nil; \
+ } while (0)
+ #endif
+#endif
+
+#ifndef GTMCFAutorelease
+ #if __has_feature(objc_arc)
+ #define GTMCFAutorelease(x) CFBridgingRelease(x)
+ #else
+ #define GTMCFAutorelease(x) ([(id)x autorelease])
+ #endif
+#endif
+
+#ifdef __OBJC__
+
+// Declared here so that it can easily be used for logging tracking if
+// necessary. See GTMUnitTestDevLog.h for details.
+@class NSString;
+GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...) NS_FORMAT_FUNCTION(1, 2);
+
+// Macro to allow you to create NSStrings out of other macros.
+// #define FOO foo
+// NSString *fooString = GTM_NSSTRINGIFY(FOO);
+#if !defined (GTM_NSSTRINGIFY)
+ #define GTM_NSSTRINGIFY_INNER(x) @#x
+ #define GTM_NSSTRINGIFY(x) GTM_NSSTRINGIFY_INNER(x)
+#endif
+
+// Macro to allow fast enumeration when building for 10.5 or later, and
+// reliance on NSEnumerator for 10.4. Remember, NSDictionary w/ FastEnumeration
+// does keys, so pick the right thing, nothing is done on the FastEnumeration
+// side to be sure you're getting what you wanted.
+#ifndef GTM_FOREACH_OBJECT
+ #if TARGET_OS_IPHONE || !(MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5)
+ #define GTM_FOREACH_ENUMEREE(element, enumeration) \
+ for (element in enumeration)
+ #define GTM_FOREACH_OBJECT(element, collection) \
+ for (element in collection)
+ #define GTM_FOREACH_KEY(element, collection) \
+ for (element in collection)
+ #else
+ #define GTM_FOREACH_ENUMEREE(element, enumeration) \
+ for (NSEnumerator *_ ## element ## _enum = enumeration; \
+ (element = [_ ## element ## _enum nextObject]) != nil; )
+ #define GTM_FOREACH_OBJECT(element, collection) \
+ GTM_FOREACH_ENUMEREE(element, [collection objectEnumerator])
+ #define GTM_FOREACH_KEY(element, collection) \
+ GTM_FOREACH_ENUMEREE(element, [collection keyEnumerator])
+ #endif
+#endif
+
+// ============================================================================
+
+// To simplify support for both Leopard and Snow Leopard we declare
+// the Snow Leopard protocols that we need here.
+#if !defined(GTM_10_6_PROTOCOLS_DEFINED) && !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
+#define GTM_10_6_PROTOCOLS_DEFINED 1
+@protocol NSConnectionDelegate
+@end
+@protocol NSAnimationDelegate
+@end
+@protocol NSImageDelegate
+@end
+@protocol NSTabViewDelegate
+@end
+#endif // !defined(GTM_10_6_PROTOCOLS_DEFINED) && !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
+
+// GTM_SEL_STRING is for specifying selector (usually property) names to KVC
+// or KVO methods.
+// In debug it will generate warnings for undeclared selectors if
+// -Wunknown-selector is turned on.
+// In release it will have no runtime overhead.
+#ifndef GTM_SEL_STRING
+ #ifdef DEBUG
+ #define GTM_SEL_STRING(selName) NSStringFromSelector(@selector(selName))
+ #else
+ #define GTM_SEL_STRING(selName) @#selName
+ #endif // DEBUG
+#endif // GTM_SEL_STRING
+
+#endif // __OBJC__
diff --git a/3rdParty/Breakpad/src/common/mac/GTMLogger.h b/3rdParty/Breakpad/src/common/mac/GTMLogger.h
new file mode 100644
index 0000000..c4fd140
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/mac/GTMLogger.h
@@ -0,0 +1,504 @@
+//
+// GTMLogger.h
+//
+// Copyright 2007-2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+// Key Abstractions
+// ----------------
+//
+// This file declares multiple classes and protocols that are used by the
+// GTMLogger logging system. The 4 main abstractions used in this file are the
+// following:
+//
+// * logger (GTMLogger) - The main logging class that users interact with. It
+// has methods for logging at different levels and uses a log writer, a log
+// formatter, and a log filter to get the job done.
+//
+// * log writer (GTMLogWriter) - Writes a given string to some log file, where
+// a "log file" can be a physical file on disk, a POST over HTTP to some URL,
+// or even some in-memory structure (e.g., a ring buffer).
+//
+// * log formatter (GTMLogFormatter) - Given a format string and arguments as
+// a va_list, returns a single formatted NSString. A "formatted string" could
+// be a string with the date prepended, a string with values in a CSV format,
+// or even a string of XML.
+//
+// * log filter (GTMLogFilter) - Given a formatted log message as an NSString
+// and the level at which the message is to be logged, this class will decide
+// whether the given message should be logged or not. This is a flexible way
+// to filter out messages logged at a certain level, messages that contain
+// certain text, or filter nothing out at all. This gives the caller the
+// flexibility to dynamically enable debug logging in Release builds.
+//
+// This file also declares some classes to handle the common log writer, log
+// formatter, and log filter cases. Callers can also create their own writers,
+// formatters, and filters and they can even build them on top of the ones
+// declared here. Keep in mind that your custom writer/formatter/filter may be
+// called from multiple threads, so it must be thread-safe.
+
+#import <Foundation/Foundation.h>
+#import "GTMDefines.h"
+
+// Predeclaration of used protocols that are declared later in this file.
+@protocol GTMLogWriter, GTMLogFormatter, GTMLogFilter;
+
+// GTMLogger
+//
+// GTMLogger is the primary user-facing class for an object-oriented logging
+// system. It is built on the concept of log formatters (GTMLogFormatter), log
+// writers (GTMLogWriter), and log filters (GTMLogFilter). When a message is
+// sent to a GTMLogger to log a message, the message is formatted using the log
+// formatter, then the log filter is consulted to see if the message should be
+// logged, and if so, the message is sent to the log writer to be written out.
+//
+// GTMLogger is intended to be a flexible and thread-safe logging solution. Its
+// flexibility comes from the fact that GTMLogger instances can be customized
+// with user defined formatters, filters, and writers. And these writers,
+// filters, and formatters can be combined, stacked, and customized in arbitrary
+// ways to suit the needs at hand. For example, multiple writers can be used at
+// the same time, and a GTMLogger instance can even be used as another
+// GTMLogger's writer. This allows for arbitrarily deep logging trees.
+//
+// A standard GTMLogger uses a writer that sends messages to standard out, a
+// formatter that smacks a timestamp and a few other bits of interesting
+// information on the message, and a filter that filters out debug messages from
+// release builds. Using the standard log settings, a log message will look like
+// the following:
+//
+// 2007-12-30 10:29:24.177 myapp[4588/0xa07d0f60] [lvl=1] foo=<Foo: 0x123>
+//
+// The output contains the date and time of the log message, the name of the
+// process followed by its process ID/thread ID, the log level at which the
+// message was logged (in the previous example the level was 1:
+// kGTMLoggerLevelDebug), and finally, the user-specified log message itself (in
+// this case, the log message was @"foo=%@", foo).
+//
+// Multiple instances of GTMLogger can be created, each configured their own
+// way. Though GTMLogger is not a singleton (in the GoF sense), it does provide
+// access to a shared (i.e., globally accessible) GTMLogger instance. This makes
+// it convenient for all code in a process to use the same GTMLogger instance.
+// The shared GTMLogger instance can also be configured in an arbitrary, and
+// these configuration changes will affect all code that logs through the shared
+// instance.
+
+//
+// Log Levels
+// ----------
+// GTMLogger has 3 different log levels: Debug, Info, and Error. GTMLogger
+// doesn't take any special action based on the log level; it simply forwards
+// this information on to formatters, filters, and writers, each of which may
+// optionally take action based on the level. Since log level filtering is
+// performed at runtime, log messages are typically not filtered out at compile
+// time. The exception to this rule is that calls to the GTMLoggerDebug() macro
+// *ARE* filtered out of non-DEBUG builds. This is to be backwards compatible
+// with behavior that many developers are currently used to. Note that this
+// means that GTMLoggerDebug(@"hi") will be compiled out of Release builds, but
+// [[GTMLogger sharedLogger] logDebug:@"hi"] will NOT be compiled out.
+//
+// Standard loggers are created with the GTMLogLevelFilter log filter, which
+// filters out certain log messages based on log level, and some other settings.
+//
+// In addition to the -logDebug:, -logInfo:, and -logError: methods defined on
+// GTMLogger itself, there are also C macros that make usage of the shared
+// GTMLogger instance very convenient. These macros are:
+//
+// GTMLoggerDebug(...)
+// GTMLoggerInfo(...)
+// GTMLoggerError(...)
+//
+// Again, a notable feature of these macros is that GTMLogDebug() calls *will be
+// compiled out of non-DEBUG builds*.
+//
+// Standard Loggers
+// ----------------
+// GTMLogger has the concept of "standard loggers". A standard logger is simply
+// a logger that is pre-configured with some standard/common writer, formatter,
+// and filter combination. Standard loggers are created using the creation
+// methods beginning with "standard". The alternative to a standard logger is a
+// regular logger, which will send messages to stdout, with no special
+// formatting, and no filtering.
+//
+// How do I use GTMLogger?
+// ----------------------
+// The typical way you will want to use GTMLogger is to simply use the
+// GTMLogger*() macros for logging from code. That way we can easily make
+// changes to the GTMLogger class and simply update the macros accordingly. Only
+// your application startup code (perhaps, somewhere in main()) should use the
+// GTMLogger class directly in order to configure the shared logger, which all
+// of the code using the macros will be using. Again, this is just the typical
+// situation.
+//
+// To be complete, there are cases where you may want to use GTMLogger directly,
+// or even create separate GTMLogger instances for some reason. That's fine,
+// too.
+//
+// Examples
+// --------
+// The following show some common GTMLogger use cases.
+//
+// 1. You want to log something as simply as possible. Also, this call will only
+// appear in debug builds. In non-DEBUG builds it will be completely removed.
+//
+// GTMLoggerDebug(@"foo = %@", foo);
+//
+// 2. The previous example is similar to the following. The major difference is
+// that the previous call (example 1) will be compiled out of Release builds
+// but this statement will not be compiled out.
+//
+// [[GTMLogger sharedLogger] logDebug:@"foo = %@", foo];
+//
+// 3. Send all logging output from the shared logger to a file. We do this by
+// creating an NSFileHandle for writing associated with a file, and setting
+// that file handle as the logger's writer.
+//
+// NSFileHandle *f = [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log"
+// create:YES];
+// [[GTMLogger sharedLogger] setWriter:f];
+// GTMLoggerError(@"hi"); // This will be sent to /tmp/f.log
+//
+// 4. Create a new GTMLogger that will log to a file. This example differs from
+// the previous one because here we create a new GTMLogger that is different
+// from the shared logger.
+//
+// GTMLogger *logger = [GTMLogger standardLoggerWithPath:@"/tmp/temp.log"];
+// [logger logInfo:@"hi temp log file"];
+//
+// 5. Create a logger that writes to stdout and does NOT do any formatting to
+// the log message. This might be useful, for example, when writing a help
+// screen for a command-line tool to standard output.
+//
+// GTMLogger *logger = [GTMLogger logger];
+// [logger logInfo:@"%@ version 0.1 usage", progName];
+//
+// 6. Send log output to stdout AND to a log file. The trick here is that
+// NSArrays function as composite log writers, which means when an array is
+// set as the log writer, it forwards all logging messages to all of its
+// contained GTMLogWriters.
+//
+// // Create array of GTMLogWriters
+// NSArray *writers = [NSArray arrayWithObjects:
+// [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log" create:YES],
+// [NSFileHandle fileHandleWithStandardOutput], nil];
+//
+// GTMLogger *logger = [GTMLogger standardLogger];
+// [logger setWriter:writers];
+// [logger logInfo:@"hi"]; // Output goes to stdout and /tmp/f.log
+//
+// For futher details on log writers, formatters, and filters, see the
+// documentation below.
+//
+// NOTE: GTMLogger is application level logging. By default it does nothing
+// with _GTMDevLog/_GTMDevAssert (see GTMDefines.h). An application can choose
+// to bridge _GTMDevLog/_GTMDevAssert to GTMLogger by providing macro
+// definitions in its prefix header (see GTMDefines.h for how one would do
+// that).
+//
+@interface GTMLogger : NSObject {
+ @private
+ id<GTMLogWriter> writer_;
+ id<GTMLogFormatter> formatter_;
+ id<GTMLogFilter> filter_;
+}
+
+//
+// Accessors for the shared logger instance
+//
+
+// Returns a shared/global standard GTMLogger instance. Callers should typically
+// use this method to get a GTMLogger instance, unless they explicitly want
+// their own instance to configure for their own needs. This is the only method
+// that returns a shared instance; all the rest return new GTMLogger instances.
++ (id)sharedLogger;
+
+// Sets the shared logger instance to |logger|. Future calls to +sharedLogger
+// will return |logger| instead.
++ (void)setSharedLogger:(GTMLogger *)logger;
+
+//
+// Creation methods
+//
+
+// Returns a new autoreleased GTMLogger instance that will log to stdout, using
+// the GTMLogStandardFormatter, and the GTMLogLevelFilter filter.
++ (id)standardLogger;
+
+// Same as +standardLogger, but logs to stderr.
++ (id)standardLoggerWithStderr;
+
+// Same as +standardLogger but levels >= kGTMLoggerLevelError are routed to
+// stderr, everything else goes to stdout.
++ (id)standardLoggerWithStdoutAndStderr;
+
+// Returns a new standard GTMLogger instance with a log writer that will
+// write to the file at |path|, and will use the GTMLogStandardFormatter and
+// GTMLogLevelFilter classes. If |path| does not exist, it will be created.
++ (id)standardLoggerWithPath:(NSString *)path;
+
+// Returns an autoreleased GTMLogger instance that will use the specified
+// |writer|, |formatter|, and |filter|.
++ (id)loggerWithWriter:(id<GTMLogWriter>)writer
+ formatter:(id<GTMLogFormatter>)formatter
+ filter:(id<GTMLogFilter>)filter;
+
+// Returns an autoreleased GTMLogger instance that logs to stdout, with the
+// basic formatter, and no filter. The returned logger differs from the logger
+// returned by +standardLogger because this one does not do any filtering and
+// does not do any special log formatting; this is the difference between a
+// "regular" logger and a "standard" logger.
++ (id)logger;
+
+// Designated initializer. This method returns a GTMLogger initialized with the
+// specified |writer|, |formatter|, and |filter|. See the setter methods below
+// for what values will be used if nil is passed for a parameter.
+- (id)initWithWriter:(id<GTMLogWriter>)writer
+ formatter:(id<GTMLogFormatter>)formatter
+ filter:(id<GTMLogFilter>)filter;
+
+//
+// Logging methods
+//
+
+// Logs a message at the debug level (kGTMLoggerLevelDebug).
+- (void)logDebug:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
+// Logs a message at the info level (kGTMLoggerLevelInfo).
+- (void)logInfo:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
+// Logs a message at the error level (kGTMLoggerLevelError).
+- (void)logError:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
+// Logs a message at the assert level (kGTMLoggerLevelAssert).
+- (void)logAssert:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
+
+
+//
+// Accessors
+//
+
+// Accessor methods for the log writer. If the log writer is set to nil,
+// [NSFileHandle fileHandleWithStandardOutput] is used.
+- (id<GTMLogWriter>)writer;
+- (void)setWriter:(id<GTMLogWriter>)writer;
+
+// Accessor methods for the log formatter. If the log formatter is set to nil,
+// GTMLogBasicFormatter is used. This formatter will format log messages in a
+// plain printf style.
+- (id<GTMLogFormatter>)formatter;
+- (void)setFormatter:(id<GTMLogFormatter>)formatter;
+
+// Accessor methods for the log filter. If the log filter is set to nil,
+// GTMLogNoFilter is used, which allows all log messages through.
+- (id<GTMLogFilter>)filter;
+- (void)setFilter:(id<GTMLogFilter>)filter;
+
+@end // GTMLogger
+
+
+// Helper functions that are used by the convenience GTMLogger*() macros that
+// enable the logging of function names.
+@interface GTMLogger (GTMLoggerMacroHelpers)
+- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ...
+ NS_FORMAT_FUNCTION(2, 3);
+- (void)logFuncInfo:(const char *)func msg:(NSString *)fmt, ...
+ NS_FORMAT_FUNCTION(2, 3);
+- (void)logFuncError:(const char *)func msg:(NSString *)fmt, ...
+ NS_FORMAT_FUNCTION(2, 3);
+- (void)logFuncAssert:(const char *)func msg:(NSString *)fmt, ...
+ NS_FORMAT_FUNCTION(2, 3);
+@end // GTMLoggerMacroHelpers
+
+
+// The convenience macros are only defined if they haven't already been defined.
+#ifndef GTMLoggerInfo
+
+// Convenience macros that log to the shared GTMLogger instance. These macros
+// are how users should typically log to GTMLogger. Notice that GTMLoggerDebug()
+// calls will be compiled out of non-Debug builds.
+#define GTMLoggerDebug(...) \
+ [[GTMLogger sharedLogger] logFuncDebug:__func__ msg:__VA_ARGS__]
+#define GTMLoggerInfo(...) \
+ [[GTMLogger sharedLogger] logFuncInfo:__func__ msg:__VA_ARGS__]
+#define GTMLoggerError(...) \
+ [[GTMLogger sharedLogger] logFuncError:__func__ msg:__VA_ARGS__]
+#define GTMLoggerAssert(...) \
+ [[GTMLogger sharedLogger] logFuncAssert:__func__ msg:__VA_ARGS__]
+
+// If we're not in a debug build, remove the GTMLoggerDebug statements. This
+// makes calls to GTMLoggerDebug "compile out" of Release builds
+#ifndef DEBUG
+#undef GTMLoggerDebug
+#define GTMLoggerDebug(...) do {} while(0)
+#endif
+
+#endif // !defined(GTMLoggerInfo)
+
+// Log levels.
+typedef enum {
+ kGTMLoggerLevelUnknown,
+ kGTMLoggerLevelDebug,
+ kGTMLoggerLevelInfo,
+ kGTMLoggerLevelError,
+ kGTMLoggerLevelAssert,
+} GTMLoggerLevel;
+
+
+//
+// Log Writers
+//
+
+// Protocol to be implemented by a GTMLogWriter instance.
+@protocol GTMLogWriter <NSObject>
+// Writes the given log message to where the log writer is configured to write.
+- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level;
+@end // GTMLogWriter
+
+
+// Simple category on NSFileHandle that makes NSFileHandles valid log writers.
+// This is convenient because something like, say, +fileHandleWithStandardError
+// now becomes a valid log writer. Log messages are written to the file handle
+// with a newline appended.
+@interface NSFileHandle (GTMFileHandleLogWriter) <GTMLogWriter>
+// Opens the file at |path| in append mode, and creates the file with |mode|
+// if it didn't previously exist.
++ (id)fileHandleForLoggingAtPath:(NSString *)path mode:(mode_t)mode;
+@end // NSFileHandle
+
+
+// This category makes NSArray a GTMLogWriter that can be composed of other
+// GTMLogWriters. This is the classic Composite GoF design pattern. When the
+// GTMLogWriter -logMessage:level: message is sent to the array, the array
+// forwards the message to all of its elements that implement the GTMLogWriter
+// protocol.
+//
+// This is useful in situations where you would like to send log output to
+// multiple log writers at the same time. Simply create an NSArray of the log
+// writers you wish to use, then set the array as the "writer" for your
+// GTMLogger instance.
+@interface NSArray (GTMArrayCompositeLogWriter) <GTMLogWriter>
+@end // GTMArrayCompositeLogWriter
+
+
+// This category adapts the GTMLogger interface so that it can be used as a log
+// writer; it's an "adapter" in the GoF Adapter pattern sense.
+//
+// This is useful when you want to configure a logger to log to a specific
+// writer with a specific formatter and/or filter. But you want to also compose
+// that with a different log writer that may have its own formatter and/or
+// filter.
+@interface GTMLogger (GTMLoggerLogWriter) <GTMLogWriter>
+@end // GTMLoggerLogWriter
+
+
+//
+// Log Formatters
+//
+
+// Protocol to be implemented by a GTMLogFormatter instance.
+@protocol GTMLogFormatter <NSObject>
+// Returns a formatted string using the format specified in |fmt| and the va
+// args specified in |args|.
+- (NSString *)stringForFunc:(NSString *)func
+ withFormat:(NSString *)fmt
+ valist:(va_list)args
+ level:(GTMLoggerLevel)level NS_FORMAT_FUNCTION(2, 0);
+@end // GTMLogFormatter
+
+
+// A basic log formatter that formats a string the same way that NSLog (or
+// printf) would. It does not do anything fancy, nor does it add any data of its
+// own.
+@interface GTMLogBasicFormatter : NSObject <GTMLogFormatter>
+
+// Helper method for prettying C99 __func__ and GCC __PRETTY_FUNCTION__
+- (NSString *)prettyNameForFunc:(NSString *)func;
+
+@end // GTMLogBasicFormatter
+
+
+// A log formatter that formats the log string like the basic formatter, but
+// also prepends a timestamp and some basic process info to the message, as
+// shown in the following sample output.
+// 2007-12-30 10:29:24.177 myapp[4588/0xa07d0f60] [lvl=1] log mesage here
+@interface GTMLogStandardFormatter : GTMLogBasicFormatter {
+ @private
+ NSDateFormatter *dateFormatter_; // yyyy-MM-dd HH:mm:ss.SSS
+ NSString *pname_;
+ pid_t pid_;
+}
+@end // GTMLogStandardFormatter
+
+
+//
+// Log Filters
+//
+
+// Protocol to be imlemented by a GTMLogFilter instance.
+@protocol GTMLogFilter <NSObject>
+// Returns YES if |msg| at |level| should be filtered out; NO otherwise.
+- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level;
+@end // GTMLogFilter
+
+
+// A log filter that filters messages at the kGTMLoggerLevelDebug level out of
+// non-debug builds. Messages at the kGTMLoggerLevelInfo level are also filtered
+// out of non-debug builds unless GTMVerboseLogging is set in the environment or
+// the processes's defaults. Messages at the kGTMLoggerLevelError level are
+// never filtered.
+@interface GTMLogLevelFilter : NSObject <GTMLogFilter>
+@end // GTMLogLevelFilter
+
+// A simple log filter that does NOT filter anything out;
+// -filterAllowsMessage:level will always return YES. This can be a convenient
+// way to enable debug-level logging in release builds (if you so desire).
+@interface GTMLogNoFilter : NSObject <GTMLogFilter>
+@end // GTMLogNoFilter
+
+
+// Base class for custom level filters. Not for direct use, use the minimum
+// or maximum level subclasses below.
+@interface GTMLogAllowedLevelFilter : NSObject <GTMLogFilter> {
+ @private
+ NSIndexSet *allowedLevels_;
+}
+@end
+
+// A log filter that allows you to set a minimum log level. Messages below this
+// level will be filtered.
+@interface GTMLogMininumLevelFilter : GTMLogAllowedLevelFilter
+
+// Designated initializer, logs at levels < |level| will be filtered.
+- (id)initWithMinimumLevel:(GTMLoggerLevel)level;
+
+@end
+
+// A log filter that allows you to set a maximum log level. Messages whose level
+// exceeds this level will be filtered. This is really only useful if you have
+// a composite GTMLogger that is sending the other messages elsewhere.
+@interface GTMLogMaximumLevelFilter : GTMLogAllowedLevelFilter
+
+// Designated initializer, logs at levels > |level| will be filtered.
+- (id)initWithMaximumLevel:(GTMLoggerLevel)level;
+
+@end
+
+
+// For subclasses only
+@interface GTMLogger (PrivateMethods)
+
+- (void)logInternalFunc:(const char *)func
+ format:(NSString *)fmt
+ valist:(va_list)args
+ level:(GTMLoggerLevel)level NS_FORMAT_FUNCTION(2, 0);
+
+@end
+
diff --git a/3rdParty/Breakpad/src/common/mac/GTMLogger.m b/3rdParty/Breakpad/src/common/mac/GTMLogger.m
new file mode 100644
index 0000000..ebc5836
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/mac/GTMLogger.m
@@ -0,0 +1,611 @@
+//
+// GTMLogger.m
+//
+// Copyright 2007-2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import "GTMLogger.h"
+#import <fcntl.h>
+#import <unistd.h>
+#import <stdlib.h>
+#import <pthread.h>
+
+
+#if !defined(__clang__) && (__GNUC__*10+__GNUC_MINOR__ >= 42)
+// Some versions of GCC (4.2 and below AFAIK) aren't great about supporting
+// -Wmissing-format-attribute
+// when the function is anything more complex than foo(NSString *fmt, ...).
+// You see the error inside the function when you turn ... into va_args and
+// attempt to call another function (like vsprintf for example).
+// So we just shut off the warning for this file. We reenable it at the end.
+#pragma GCC diagnostic ignored "-Wmissing-format-attribute"
+#endif // !__clang__
+
+// Reference to the shared GTMLogger instance. This is not a singleton, it's
+// just an easy reference to one shared instance.
+static GTMLogger *gSharedLogger = nil;
+
+
+@implementation GTMLogger
+
+// Returns a pointer to the shared logger instance. If none exists, a standard
+// logger is created and returned.
++ (id)sharedLogger {
+ @synchronized(self) {
+ if (gSharedLogger == nil) {
+ gSharedLogger = [[self standardLogger] retain];
+ }
+ }
+ return [[gSharedLogger retain] autorelease];
+}
+
++ (void)setSharedLogger:(GTMLogger *)logger {
+ @synchronized(self) {
+ [gSharedLogger autorelease];
+ gSharedLogger = [logger retain];
+ }
+}
+
++ (id)standardLogger {
+ // Don't trust NSFileHandle not to throw
+ @try {
+ id<GTMLogWriter> writer = [NSFileHandle fileHandleWithStandardOutput];
+ id<GTMLogFormatter> fr = [[[GTMLogStandardFormatter alloc] init]
+ autorelease];
+ id<GTMLogFilter> filter = [[[GTMLogLevelFilter alloc] init] autorelease];
+ return [[[self alloc] initWithWriter:writer
+ formatter:fr
+ filter:filter] autorelease];
+ }
+ @catch (id e) {
+ // Ignored
+ }
+ return nil;
+}
+
++ (id)standardLoggerWithStderr {
+ // Don't trust NSFileHandle not to throw
+ @try {
+ id me = [self standardLogger];
+ [me setWriter:[NSFileHandle fileHandleWithStandardError]];
+ return me;
+ }
+ @catch (id e) {
+ // Ignored
+ }
+ return nil;
+}
+
++ (id)standardLoggerWithStdoutAndStderr {
+ // We're going to take advantage of the GTMLogger to GTMLogWriter adaptor
+ // and create a composite logger that an outer "standard" logger can use
+ // as a writer. Our inner loggers should apply no formatting since the main
+ // logger does that and we want the caller to be able to change formatters
+ // or add writers without knowing the inner structure of our composite.
+
+ // Don't trust NSFileHandle not to throw
+ @try {
+ GTMLogBasicFormatter *formatter = [[[GTMLogBasicFormatter alloc] init]
+ autorelease];
+ GTMLogger *stdoutLogger =
+ [self loggerWithWriter:[NSFileHandle fileHandleWithStandardOutput]
+ formatter:formatter
+ filter:[[[GTMLogMaximumLevelFilter alloc]
+ initWithMaximumLevel:kGTMLoggerLevelInfo]
+ autorelease]];
+ GTMLogger *stderrLogger =
+ [self loggerWithWriter:[NSFileHandle fileHandleWithStandardError]
+ formatter:formatter
+ filter:[[[GTMLogMininumLevelFilter alloc]
+ initWithMinimumLevel:kGTMLoggerLevelError]
+ autorelease]];
+ GTMLogger *compositeWriter =
+ [self loggerWithWriter:[NSArray arrayWithObjects:
+ stdoutLogger, stderrLogger, nil]
+ formatter:formatter
+ filter:[[[GTMLogNoFilter alloc] init] autorelease]];
+ GTMLogger *outerLogger = [self standardLogger];
+ [outerLogger setWriter:compositeWriter];
+ return outerLogger;
+ }
+ @catch (id e) {
+ // Ignored
+ }
+ return nil;
+}
+
++ (id)standardLoggerWithPath:(NSString *)path {
+ @try {
+ NSFileHandle *fh = [NSFileHandle fileHandleForLoggingAtPath:path mode:0644];
+ if (fh == nil) return nil;
+ id me = [self standardLogger];
+ [me setWriter:fh];
+ return me;
+ }
+ @catch (id e) {
+ // Ignored
+ }
+ return nil;
+}
+
++ (id)loggerWithWriter:(id<GTMLogWriter>)writer
+ formatter:(id<GTMLogFormatter>)formatter
+ filter:(id<GTMLogFilter>)filter {
+ return [[[self alloc] initWithWriter:writer
+ formatter:formatter
+ filter:filter] autorelease];
+}
+
++ (id)logger {
+ return [[[self alloc] init] autorelease];
+}
+
+- (id)init {
+ return [self initWithWriter:nil formatter:nil filter:nil];
+}
+
+- (id)initWithWriter:(id<GTMLogWriter>)writer
+ formatter:(id<GTMLogFormatter>)formatter
+ filter:(id<GTMLogFilter>)filter {
+ if ((self = [super init])) {
+ [self setWriter:writer];
+ [self setFormatter:formatter];
+ [self setFilter:filter];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ // Unlikely, but |writer_| may be an NSFileHandle, which can throw
+ @try {
+ [formatter_ release];
+ [filter_ release];
+ [writer_ release];
+ }
+ @catch (id e) {
+ // Ignored
+ }
+ [super dealloc];
+}
+
+- (id<GTMLogWriter>)writer {
+ return [[writer_ retain] autorelease];
+}
+
+- (void)setWriter:(id<GTMLogWriter>)writer {
+ @synchronized(self) {
+ [writer_ autorelease];
+ writer_ = nil;
+ if (writer == nil) {
+ // Try to use stdout, but don't trust NSFileHandle
+ @try {
+ writer_ = [[NSFileHandle fileHandleWithStandardOutput] retain];
+ }
+ @catch (id e) {
+ // Leave |writer_| nil
+ }
+ } else {
+ writer_ = [writer retain];
+ }
+ }
+}
+
+- (id<GTMLogFormatter>)formatter {
+ return [[formatter_ retain] autorelease];
+}
+
+- (void)setFormatter:(id<GTMLogFormatter>)formatter {
+ @synchronized(self) {
+ [formatter_ autorelease];
+ formatter_ = nil;
+ if (formatter == nil) {
+ @try {
+ formatter_ = [[GTMLogBasicFormatter alloc] init];
+ }
+ @catch (id e) {
+ // Leave |formatter_| nil
+ }
+ } else {
+ formatter_ = [formatter retain];
+ }
+ }
+}
+
+- (id<GTMLogFilter>)filter {
+ return [[filter_ retain] autorelease];
+}
+
+- (void)setFilter:(id<GTMLogFilter>)filter {
+ @synchronized(self) {
+ [filter_ autorelease];
+ filter_ = nil;
+ if (filter == nil) {
+ @try {
+ filter_ = [[GTMLogNoFilter alloc] init];
+ }
+ @catch (id e) {
+ // Leave |filter_| nil
+ }
+ } else {
+ filter_ = [filter retain];
+ }
+ }
+}
+
+- (void)logDebug:(NSString *)fmt, ... {
+ va_list args;
+ va_start(args, fmt);
+ [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelDebug];
+ va_end(args);
+}
+
+- (void)logInfo:(NSString *)fmt, ... {
+ va_list args;
+ va_start(args, fmt);
+ [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelInfo];
+ va_end(args);
+}
+
+- (void)logError:(NSString *)fmt, ... {
+ va_list args;
+ va_start(args, fmt);
+ [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelError];
+ va_end(args);
+}
+
+- (void)logAssert:(NSString *)fmt, ... {
+ va_list args;
+ va_start(args, fmt);
+ [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelAssert];
+ va_end(args);
+}
+
+@end // GTMLogger
+
+@implementation GTMLogger (GTMLoggerMacroHelpers)
+
+- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ... {
+ va_list args;
+ va_start(args, fmt);
+ [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelDebug];
+ va_end(args);
+}
+
+- (void)logFuncInfo:(const char *)func msg:(NSString *)fmt, ... {
+ va_list args;
+ va_start(args, fmt);
+ [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelInfo];
+ va_end(args);
+}
+
+- (void)logFuncError:(const char *)func msg:(NSString *)fmt, ... {
+ va_list args;
+ va_start(args, fmt);
+ [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelError];
+ va_end(args);
+}
+
+- (void)logFuncAssert:(const char *)func msg:(NSString *)fmt, ... {
+ va_list args;
+ va_start(args, fmt);
+ [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelAssert];
+ va_end(args);
+}
+
+@end // GTMLoggerMacroHelpers
+
+@implementation GTMLogger (PrivateMethods)
+
+- (void)logInternalFunc:(const char *)func
+ format:(NSString *)fmt
+ valist:(va_list)args
+ level:(GTMLoggerLevel)level {
+ // Primary point where logging happens, logging should never throw, catch
+ // everything.
+ @try {
+ NSString *fname = func ? [NSString stringWithUTF8String:func] : nil;
+ NSString *msg = [formatter_ stringForFunc:fname
+ withFormat:fmt
+ valist:args
+ level:level];
+ if (msg && [filter_ filterAllowsMessage:msg level:level])
+ [writer_ logMessage:msg level:level];
+ }
+ @catch (id e) {
+ // Ignored
+ }
+}
+
+@end // PrivateMethods
+
+
+@implementation NSFileHandle (GTMFileHandleLogWriter)
+
++ (id)fileHandleForLoggingAtPath:(NSString *)path mode:(mode_t)mode {
+ int fd = -1;
+ if (path) {
+ int flags = O_WRONLY | O_APPEND | O_CREAT;
+ fd = open([path fileSystemRepresentation], flags, mode);
+ }
+ if (fd == -1) return nil;
+ return [[[self alloc] initWithFileDescriptor:fd
+ closeOnDealloc:YES] autorelease];
+}
+
+- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level {
+ @synchronized(self) {
+ // Closed pipes should not generate exceptions in our caller. Catch here
+ // as well [GTMLogger logInternalFunc:...] so that an exception in this
+ // writer does not prevent other writers from having a chance.
+ @try {
+ NSString *line = [NSString stringWithFormat:@"%@\n", msg];
+ [self writeData:[line dataUsingEncoding:NSUTF8StringEncoding]];
+ }
+ @catch (id e) {
+ // Ignored
+ }
+ }
+}
+
+@end // GTMFileHandleLogWriter
+
+
+@implementation NSArray (GTMArrayCompositeLogWriter)
+
+- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level {
+ @synchronized(self) {
+ id<GTMLogWriter> child = nil;
+ GTM_FOREACH_OBJECT(child, self) {
+ if ([child conformsToProtocol:@protocol(GTMLogWriter)])
+ [child logMessage:msg level:level];
+ }
+ }
+}
+
+@end // GTMArrayCompositeLogWriter
+
+
+@implementation GTMLogger (GTMLoggerLogWriter)
+
+- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level {
+ switch (level) {
+ case kGTMLoggerLevelDebug:
+ [self logDebug:@"%@", msg];
+ break;
+ case kGTMLoggerLevelInfo:
+ [self logInfo:@"%@", msg];
+ break;
+ case kGTMLoggerLevelError:
+ [self logError:@"%@", msg];
+ break;
+ case kGTMLoggerLevelAssert:
+ [self logAssert:@"%@", msg];
+ break;
+ default:
+ // Ignore the message.
+ break;
+ }
+}
+
+@end // GTMLoggerLogWriter
+
+
+@implementation GTMLogBasicFormatter
+
+- (NSString *)prettyNameForFunc:(NSString *)func {
+ NSString *name = [func stringByTrimmingCharactersInSet:
+ [NSCharacterSet whitespaceAndNewlineCharacterSet]];
+ NSString *function = @"(unknown)";
+ if ([name length]) {
+ if (// Objective C __func__ and __PRETTY_FUNCTION__
+ [name hasPrefix:@"-["] || [name hasPrefix:@"+["] ||
+ // C++ __PRETTY_FUNCTION__ and other preadorned formats
+ [name hasSuffix:@")"]) {
+ function = name;
+ } else {
+ // Assume C99 __func__
+ function = [NSString stringWithFormat:@"%@()", name];
+ }
+ }
+ return function;
+}
+
+- (NSString *)stringForFunc:(NSString *)func
+ withFormat:(NSString *)fmt
+ valist:(va_list)args
+ level:(GTMLoggerLevel)level {
+ // Performance note: We may want to do a quick check here to see if |fmt|
+ // contains a '%', and if not, simply return 'fmt'.
+ if (!(fmt && args)) return nil;
+ return [[[NSString alloc] initWithFormat:fmt arguments:args] autorelease];
+}
+
+@end // GTMLogBasicFormatter
+
+
+@implementation GTMLogStandardFormatter
+
+- (id)init {
+ if ((self = [super init])) {
+ dateFormatter_ = [[NSDateFormatter alloc] init];
+ [dateFormatter_ setFormatterBehavior:NSDateFormatterBehavior10_4];
+ [dateFormatter_ setDateFormat:@"yyyy-MM-dd HH:mm:ss.SSS"];
+ pname_ = [[[NSProcessInfo processInfo] processName] copy];
+ pid_ = [[NSProcessInfo processInfo] processIdentifier];
+ if (!(dateFormatter_ && pname_)) {
+ [self release];
+ return nil;
+ }
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [dateFormatter_ release];
+ [pname_ release];
+ [super dealloc];
+}
+
+- (NSString *)stringForFunc:(NSString *)func
+ withFormat:(NSString *)fmt
+ valist:(va_list)args
+ level:(GTMLoggerLevel)level {
+ NSString *tstamp = nil;
+ @synchronized (dateFormatter_) {
+ tstamp = [dateFormatter_ stringFromDate:[NSDate date]];
+ }
+ return [NSString stringWithFormat:@"%@ %@[%d/%p] [lvl=%d] %@ %@",
+ tstamp, pname_, pid_, pthread_self(),
+ level, [self prettyNameForFunc:func],
+ // |super| has guard for nil |fmt| and |args|
+ [super stringForFunc:func withFormat:fmt valist:args level:level]];
+}
+
+@end // GTMLogStandardFormatter
+
+
+@implementation GTMLogLevelFilter
+
+// Check the environment and the user preferences for the GTMVerboseLogging key
+// to see if verbose logging has been enabled. The environment variable will
+// override the defaults setting, so check the environment first.
+// COV_NF_START
+static BOOL IsVerboseLoggingEnabled(void) {
+ static NSString *const kVerboseLoggingKey = @"GTMVerboseLogging";
+ NSString *value = [[[NSProcessInfo processInfo] environment]
+ objectForKey:kVerboseLoggingKey];
+ if (value) {
+ // Emulate [NSString boolValue] for pre-10.5
+ value = [value stringByTrimmingCharactersInSet:
+ [NSCharacterSet whitespaceAndNewlineCharacterSet]];
+ if ([[value uppercaseString] hasPrefix:@"Y"] ||
+ [[value uppercaseString] hasPrefix:@"T"] ||
+ [value intValue]) {
+ return YES;
+ } else {
+ return NO;
+ }
+ }
+ return [[NSUserDefaults standardUserDefaults] boolForKey:kVerboseLoggingKey];
+}
+// COV_NF_END
+
+// In DEBUG builds, log everything. If we're not in a debug build we'll assume
+// that we're in a Release build.
+- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level {
+#if defined(DEBUG) && DEBUG
+ return YES;
+#endif
+
+ BOOL allow = YES;
+
+ switch (level) {
+ case kGTMLoggerLevelDebug:
+ allow = NO;
+ break;
+ case kGTMLoggerLevelInfo:
+ allow = IsVerboseLoggingEnabled();
+ break;
+ case kGTMLoggerLevelError:
+ allow = YES;
+ break;
+ case kGTMLoggerLevelAssert:
+ allow = YES;
+ break;
+ default:
+ allow = YES;
+ break;
+ }
+
+ return allow;
+}
+
+@end // GTMLogLevelFilter
+
+
+@implementation GTMLogNoFilter
+
+- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level {
+ return YES; // Allow everything through
+}
+
+@end // GTMLogNoFilter
+
+
+@implementation GTMLogAllowedLevelFilter
+
+// Private designated initializer
+- (id)initWithAllowedLevels:(NSIndexSet *)levels {
+ self = [super init];
+ if (self != nil) {
+ allowedLevels_ = [levels retain];
+ // Cap min/max level
+ if (!allowedLevels_ ||
+ // NSIndexSet is unsigned so only check the high bound, but need to
+ // check both first and last index because NSIndexSet appears to allow
+ // wraparound.
+ ([allowedLevels_ firstIndex] > kGTMLoggerLevelAssert) ||
+ ([allowedLevels_ lastIndex] > kGTMLoggerLevelAssert)) {
+ [self release];
+ return nil;
+ }
+ }
+ return self;
+}
+
+- (id)init {
+ // Allow all levels in default init
+ return [self initWithAllowedLevels:[NSIndexSet indexSetWithIndexesInRange:
+ NSMakeRange(kGTMLoggerLevelUnknown,
+ (kGTMLoggerLevelAssert - kGTMLoggerLevelUnknown + 1))]];
+}
+
+- (void)dealloc {
+ [allowedLevels_ release];
+ [super dealloc];
+}
+
+- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level {
+ return [allowedLevels_ containsIndex:level];
+}
+
+@end // GTMLogAllowedLevelFilter
+
+
+@implementation GTMLogMininumLevelFilter
+
+- (id)initWithMinimumLevel:(GTMLoggerLevel)level {
+ return [super initWithAllowedLevels:[NSIndexSet indexSetWithIndexesInRange:
+ NSMakeRange(level,
+ (kGTMLoggerLevelAssert - level + 1))]];
+}
+
+@end // GTMLogMininumLevelFilter
+
+
+@implementation GTMLogMaximumLevelFilter
+
+- (id)initWithMaximumLevel:(GTMLoggerLevel)level {
+ return [super initWithAllowedLevels:[NSIndexSet indexSetWithIndexesInRange:
+ NSMakeRange(kGTMLoggerLevelUnknown, level + 1)]];
+}
+
+@end // GTMLogMaximumLevelFilter
+
+#if !defined(__clang__) && (__GNUC__*10+__GNUC_MINOR__ >= 42)
+// See comment at top of file.
+#pragma GCC diagnostic error "-Wmissing-format-attribute"
+#endif // !__clang__
+
diff --git a/3rdParty/Breakpad/src/common/mac/HTTPMultipartUpload.h b/3rdParty/Breakpad/src/common/mac/HTTPMultipartUpload.h
new file mode 100644
index 0000000..42e8fed
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/mac/HTTPMultipartUpload.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// HTTPMultipartUpload: A multipart/form-data HTTP uploader.
+// Each parameter pair is sent as a boundary
+// Each file is sent with a name field in addition to the filename and data
+// The data will be sent synchronously.
+
+#import <Foundation/Foundation.h>
+
+@interface HTTPMultipartUpload : NSObject {
+ @protected
+ NSURL *url_; // The destination URL (STRONG)
+ NSDictionary *parameters_; // The key/value pairs for sending data (STRONG)
+ NSMutableDictionary *files_; // Dictionary of name/file-path (STRONG)
+ NSString *boundary_; // The boundary string (STRONG)
+ NSHTTPURLResponse *response_; // The response from the send (STRONG)
+}
+
+- (id)initWithURL:(NSURL *)url;
+
+- (NSURL *)URL;
+
+- (void)setParameters:(NSDictionary *)parameters;
+- (NSDictionary *)parameters;
+
+- (void)addFileAtPath:(NSString *)path name:(NSString *)name;
+- (void)addFileContents:(NSData *)data name:(NSString *)name;
+- (NSDictionary *)files;
+
+// Set the data and return the response
+- (NSData *)send:(NSError **)error;
+- (NSHTTPURLResponse *)response;
+
+@end
diff --git a/3rdParty/Breakpad/src/common/mac/HTTPMultipartUpload.m b/3rdParty/Breakpad/src/common/mac/HTTPMultipartUpload.m
new file mode 100644
index 0000000..f6d081e
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/mac/HTTPMultipartUpload.m
@@ -0,0 +1,267 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import "HTTPMultipartUpload.h"
+#import "GTMDefines.h"
+
+// As -[NSString stringByAddingPercentEscapesUsingEncoding:] has been
+// deprecated with iOS 9.0 / OS X 10.11 SDKs, this function re-implements it
+// using -[NSString stringByAddingPercentEncodingWithAllowedCharacters:] when
+// using those SDKs.
+static NSString *PercentEncodeNSString(NSString *key) {
+#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && defined(__IPHONE_9_0) && \
+ __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_9_0) || \
+ (defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \
+ defined(MAC_OS_X_VERSION_10_11) && \
+ MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11)
+ return [key stringByAddingPercentEncodingWithAllowedCharacters:
+ [NSCharacterSet URLQueryAllowedCharacterSet]];
+#else
+ return [key stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+#endif
+}
+
+// As -[NSURLConnection sendSynchronousRequest:returningResponse:error:] has
+// been deprecated with iOS 9.0 / OS X 10.11 SDKs, this function re-implements
+// it using -[NSURLSession dataTaskWithRequest:completionHandler:] which is
+// available on iOS 7+.
+static NSData *SendSynchronousNSURLRequest(NSURLRequest *req,
+ NSURLResponse **out_response,
+ NSError **out_error) {
+#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && defined(__IPHONE_7_0) && \
+ __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_7_0) || \
+ (defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \
+ defined(MAC_OS_X_VERSION_10_11) && \
+ MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11)
+ __block NSData* result = nil;
+ __block NSError* error = nil;
+ __block NSURLResponse* response = nil;
+ dispatch_semaphore_t wait_semaphone = dispatch_semaphore_create(0);
+ [[[NSURLSession sharedSession]
+ dataTaskWithRequest:req
+ completionHandler:^(NSData *data,
+ NSURLResponse *resp,
+ NSError *err) {
+ if (out_error)
+ error = [err retain];
+ if (out_response)
+ response = [resp retain];
+ if (err == nil)
+ result = [data retain];
+ dispatch_semaphore_signal(wait_semaphone);
+ }] resume];
+ dispatch_semaphore_wait(wait_semaphone, DISPATCH_TIME_FOREVER);
+ dispatch_release(wait_semaphone);
+ if (out_error)
+ *out_error = [error autorelease];
+ if (out_response)
+ *out_response = [response autorelease];
+ return [result autorelease];
+#else
+ return [NSURLConnection sendSynchronousRequest:req
+ returningResponse:out_response
+ error:out_error];
+#endif
+}
+@interface HTTPMultipartUpload(PrivateMethods)
+- (NSString *)multipartBoundary;
+// Each of the following methods will append the starting multipart boundary,
+// but not the ending one.
+- (NSData *)formDataForKey:(NSString *)key value:(NSString *)value;
+- (NSData *)formDataForFileContents:(NSData *)contents name:(NSString *)name;
+- (NSData *)formDataForFile:(NSString *)file name:(NSString *)name;
+@end
+
+@implementation HTTPMultipartUpload
+//=============================================================================
+#pragma mark -
+#pragma mark || Private ||
+//=============================================================================
+- (NSString *)multipartBoundary {
+ // The boundary has 27 '-' characters followed by 16 hex digits
+ return [NSString stringWithFormat:@"---------------------------%08X%08X",
+ rand(), rand()];
+}
+
+//=============================================================================
+- (NSData *)formDataForKey:(NSString *)key value:(NSString *)value {
+ NSString *escaped = PercentEncodeNSString(key);
+ NSString *fmt =
+ @"--%@\r\nContent-Disposition: form-data; name=\"%@\"\r\n\r\n%@\r\n";
+ NSString *form = [NSString stringWithFormat:fmt, boundary_, escaped, value];
+
+ return [form dataUsingEncoding:NSUTF8StringEncoding];
+}
+
+//=============================================================================
+- (NSData *)formDataForFileContents:(NSData *)contents name:(NSString *)name {
+ NSMutableData *data = [NSMutableData data];
+ NSString *escaped = PercentEncodeNSString(name);
+ NSString *fmt = @"--%@\r\nContent-Disposition: form-data; name=\"%@\"; "
+ "filename=\"minidump.dmp\"\r\nContent-Type: application/octet-stream\r\n\r\n";
+ NSString *pre = [NSString stringWithFormat:fmt, boundary_, escaped];
+
+ [data appendData:[pre dataUsingEncoding:NSUTF8StringEncoding]];
+ [data appendData:contents];
+
+ return data;
+}
+
+//=============================================================================
+- (NSData *)formDataForFile:(NSString *)file name:(NSString *)name {
+ NSData *contents = [NSData dataWithContentsOfFile:file];
+
+ return [self formDataForFileContents:contents name:name];
+}
+
+//=============================================================================
+#pragma mark -
+#pragma mark || Public ||
+//=============================================================================
+- (id)initWithURL:(NSURL *)url {
+ if ((self = [super init])) {
+ url_ = [url copy];
+ boundary_ = [[self multipartBoundary] retain];
+ files_ = [[NSMutableDictionary alloc] init];
+ }
+
+ return self;
+}
+
+//=============================================================================
+- (void)dealloc {
+ [url_ release];
+ [parameters_ release];
+ [files_ release];
+ [boundary_ release];
+ [response_ release];
+
+ [super dealloc];
+}
+
+//=============================================================================
+- (NSURL *)URL {
+ return url_;
+}
+
+//=============================================================================
+- (void)setParameters:(NSDictionary *)parameters {
+ if (parameters != parameters_) {
+ [parameters_ release];
+ parameters_ = [parameters copy];
+ }
+}
+
+//=============================================================================
+- (NSDictionary *)parameters {
+ return parameters_;
+}
+
+//=============================================================================
+- (void)addFileAtPath:(NSString *)path name:(NSString *)name {
+ [files_ setObject:path forKey:name];
+}
+
+//=============================================================================
+- (void)addFileContents:(NSData *)data name:(NSString *)name {
+ [files_ setObject:data forKey:name];
+}
+
+//=============================================================================
+- (NSDictionary *)files {
+ return files_;
+}
+
+//=============================================================================
+- (NSData *)send:(NSError **)error {
+ NSMutableURLRequest *req =
+ [[NSMutableURLRequest alloc]
+ initWithURL:url_ cachePolicy:NSURLRequestUseProtocolCachePolicy
+ timeoutInterval:10.0 ];
+
+ NSMutableData *postBody = [NSMutableData data];
+
+ [req setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@",
+ boundary_] forHTTPHeaderField:@"Content-type"];
+
+ // Add any parameters to the message
+ NSArray *parameterKeys = [parameters_ allKeys];
+ NSString *key;
+
+ NSInteger count = [parameterKeys count];
+ for (NSInteger i = 0; i < count; ++i) {
+ key = [parameterKeys objectAtIndex:i];
+ [postBody appendData:[self formDataForKey:key
+ value:[parameters_ objectForKey:key]]];
+ }
+
+ // Add any files to the message
+ NSArray *fileNames = [files_ allKeys];
+ for (NSString *name in fileNames) {
+ id fileOrData = [files_ objectForKey:name];
+ NSData *fileData;
+
+ // The object can be either the path to a file (NSString) or the contents
+ // of the file (NSData).
+ if ([fileOrData isKindOfClass:[NSData class]])
+ fileData = [self formDataForFileContents:fileOrData name:name];
+ else
+ fileData = [self formDataForFile:fileOrData name:name];
+
+ [postBody appendData:fileData];
+ }
+
+ NSString *epilogue = [NSString stringWithFormat:@"\r\n--%@--\r\n", boundary_];
+ [postBody appendData:[epilogue dataUsingEncoding:NSUTF8StringEncoding]];
+
+ [req setHTTPBody:postBody];
+ [req setHTTPMethod:@"POST"];
+
+ [response_ release];
+ response_ = nil;
+
+ NSData *data = nil;
+ if ([[req URL] isFileURL]) {
+ [[req HTTPBody] writeToURL:[req URL] options:0 error:error];
+ } else {
+ NSURLResponse *response = nil;
+ data = SendSynchronousNSURLRequest(req, &response, error);
+ response_ = (NSHTTPURLResponse *)[response retain];
+ }
+ [req release];
+
+ return data;
+}
+
+//=============================================================================
+- (NSHTTPURLResponse *)response {
+ return response_;
+}
+
+@end
diff --git a/3rdParty/Breakpad/src/common/mac/MachIPC.h b/3rdParty/Breakpad/src/common/mac/MachIPC.h
index 52bed59..8df9165 100644
--- a/3rdParty/Breakpad/src/common/mac/MachIPC.h
+++ b/3rdParty/Breakpad/src/common/mac/MachIPC.h
@@ -164,11 +164,11 @@ class MachMessage {
public:
// The receiver of the message can retrieve the raw data this way
- u_int8_t *GetData() {
+ uint8_t *GetData() {
return GetDataLength() > 0 ? GetDataPacket()->data : NULL;
}
- u_int32_t GetDataLength() {
+ uint32_t GetDataLength() {
return EndianU32_LtoN(GetDataPacket()->data_length);
}
@@ -210,7 +210,7 @@ class MachMessage {
struct MessageDataPacket {
int32_t id; // little-endian
int32_t data_length; // little-endian
- u_int8_t data[1]; // actual size limited by sizeof(MachMessage)
+ uint8_t data[1]; // actual size limited by sizeof(MachMessage)
};
MessageDataPacket* GetDataPacket();
@@ -223,7 +223,7 @@ class MachMessage {
mach_msg_header_t head;
mach_msg_body_t body;
- u_int8_t padding[1024]; // descriptors and data may be embedded here
+ uint8_t padding[1024]; // descriptors and data may be embedded here
};
//==============================================================================
diff --git a/3rdParty/Breakpad/src/common/mac/arch_utilities.cc b/3rdParty/Breakpad/src/common/mac/arch_utilities.cc
new file mode 100644
index 0000000..c9225e0
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/mac/arch_utilities.cc
@@ -0,0 +1,231 @@
+// Copyright (c) 2012, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "common/mac/arch_utilities.h"
+
+#include <mach-o/arch.h>
+#include <mach-o/fat.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifndef CPU_SUBTYPE_ARM_V7S
+#define CPU_SUBTYPE_ARM_V7S (static_cast<cpu_subtype_t>(11))
+#endif // CPU_SUBTYPE_ARM_V7S
+
+#ifndef CPU_TYPE_ARM64
+#define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64)
+#endif // CPU_TYPE_ARM64
+
+#ifndef CPU_SUBTYPE_ARM64_ALL
+#define CPU_SUBTYPE_ARM64_ALL (static_cast<cpu_subtype_t>(0))
+#endif // CPU_SUBTYPE_ARM64_ALL
+
+namespace {
+
+const NXArchInfo* ArchInfo_arm64() {
+ NXArchInfo* arm64 = new NXArchInfo;
+ *arm64 = *NXGetArchInfoFromCpuType(CPU_TYPE_ARM,
+ CPU_SUBTYPE_ARM_V7);
+ arm64->name = "arm64";
+ arm64->cputype = CPU_TYPE_ARM64;
+ arm64->cpusubtype = CPU_SUBTYPE_ARM64_ALL;
+ arm64->description = "arm 64";
+ return arm64;
+}
+
+const NXArchInfo* ArchInfo_armv7s() {
+ NXArchInfo* armv7s = new NXArchInfo;
+ *armv7s = *NXGetArchInfoFromCpuType(CPU_TYPE_ARM,
+ CPU_SUBTYPE_ARM_V7);
+ armv7s->name = "armv7s";
+ armv7s->cpusubtype = CPU_SUBTYPE_ARM_V7S;
+ armv7s->description = "arm v7s";
+ return armv7s;
+}
+
+} // namespace
+
+namespace google_breakpad {
+
+const NXArchInfo* BreakpadGetArchInfoFromName(const char* arch_name) {
+ // TODO: Remove this when the OS knows about arm64.
+ if (!strcmp("arm64", arch_name))
+ return BreakpadGetArchInfoFromCpuType(CPU_TYPE_ARM64,
+ CPU_SUBTYPE_ARM64_ALL);
+
+ // TODO: Remove this when the OS knows about armv7s.
+ if (!strcmp("armv7s", arch_name))
+ return BreakpadGetArchInfoFromCpuType(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S);
+
+ return NXGetArchInfoFromName(arch_name);
+}
+
+const NXArchInfo* BreakpadGetArchInfoFromCpuType(cpu_type_t cpu_type,
+ cpu_subtype_t cpu_subtype) {
+ // TODO: Remove this when the OS knows about arm64.
+ if (cpu_type == CPU_TYPE_ARM64 && cpu_subtype == CPU_SUBTYPE_ARM64_ALL) {
+ static const NXArchInfo* arm64 = ArchInfo_arm64();
+ return arm64;
+ }
+
+ // TODO: Remove this when the OS knows about armv7s.
+ if (cpu_type == CPU_TYPE_ARM && cpu_subtype == CPU_SUBTYPE_ARM_V7S) {
+ static const NXArchInfo* armv7s = ArchInfo_armv7s();
+ return armv7s;
+ }
+
+ return NXGetArchInfoFromCpuType(cpu_type, cpu_subtype);
+}
+
+} // namespace google_breakpad
+
+#ifndef __APPLE__
+namespace {
+
+enum Architecture {
+ kArch_i386 = 0,
+ kArch_x86_64,
+ kArch_x86_64h,
+ kArch_arm,
+ kArch_arm64,
+ kArch_ppc,
+ // This must be last.
+ kNumArchitectures
+};
+
+// enum Architecture above and kKnownArchitectures below
+// must be kept in sync.
+const NXArchInfo kKnownArchitectures[] = {
+ {
+ "i386",
+ CPU_TYPE_I386,
+ CPU_SUBTYPE_I386_ALL,
+ NX_LittleEndian,
+ "Intel 80x86"
+ },
+ {
+ "x86_64",
+ CPU_TYPE_X86_64,
+ CPU_SUBTYPE_X86_64_ALL,
+ NX_LittleEndian,
+ "Intel x86-64"
+ },
+ {
+ "x86_64h",
+ CPU_TYPE_X86_64,
+ CPU_SUBTYPE_X86_64_H,
+ NX_LittleEndian,
+ "Intel x86-64h Haswell"
+ },
+ {
+ "arm",
+ CPU_TYPE_ARM,
+ CPU_SUBTYPE_ARM_ALL,
+ NX_LittleEndian,
+ "ARM"
+ },
+ {
+ "arm64",
+ CPU_TYPE_ARM64,
+ CPU_SUBTYPE_ARM64_ALL,
+ NX_LittleEndian,
+ "ARM64"
+ },
+ {
+ "ppc",
+ CPU_TYPE_POWERPC,
+ CPU_SUBTYPE_POWERPC_ALL,
+ NX_BigEndian,
+ "PowerPC"
+ }
+};
+
+} // namespace
+
+const NXArchInfo *NXGetLocalArchInfo(void) {
+ Architecture arch;
+#if defined(__i386__)
+ arch = kArch_i386;
+#elif defined(__x86_64__)
+ arch = kArch_x86_64;
+#elif defined(__arm64)
+ arch = kArch_arm64;
+#elif defined(__arm__)
+ arch = kArch_arm;
+#elif defined(__powerpc__)
+ arch = kArch_ppc;
+#else
+ #error "Unsupported CPU architecture"
+#endif
+ return &kKnownArchitectures[arch];
+}
+
+const NXArchInfo *NXGetArchInfoFromName(const char *name) {
+ for (int arch = 0; arch < kNumArchitectures; ++arch) {
+ if (!strcmp(name, kKnownArchitectures[arch].name)) {
+ return &kKnownArchitectures[arch];
+ }
+ }
+ return NULL;
+}
+
+const NXArchInfo *NXGetArchInfoFromCpuType(cpu_type_t cputype,
+ cpu_subtype_t cpusubtype) {
+ const NXArchInfo *candidate = NULL;
+ for (int arch = 0; arch < kNumArchitectures; ++arch) {
+ if (kKnownArchitectures[arch].cputype == cputype) {
+ if (kKnownArchitectures[arch].cpusubtype == cpusubtype) {
+ return &kKnownArchitectures[arch];
+ }
+ if (!candidate) {
+ candidate = &kKnownArchitectures[arch];
+ }
+ }
+ }
+ return candidate;
+}
+
+struct fat_arch *NXFindBestFatArch(cpu_type_t cputype,
+ cpu_subtype_t cpusubtype,
+ struct fat_arch *fat_archs,
+ uint32_t nfat_archs) {
+ struct fat_arch *candidate = NULL;
+ for (uint32_t f = 0; f < nfat_archs; ++f) {
+ if (fat_archs[f].cputype == cputype) {
+ if (fat_archs[f].cpusubtype == cpusubtype) {
+ return &fat_archs[f];
+ }
+ if (!candidate) {
+ candidate = &fat_archs[f];
+ }
+ }
+ }
+ return candidate;
+}
+#endif // !__APPLE__
diff --git a/3rdParty/Breakpad/src/common/mac/arch_utilities.h b/3rdParty/Breakpad/src/common/mac/arch_utilities.h
new file mode 100644
index 0000000..397c1f5
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/mac/arch_utilities.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2012, 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.
+
+// arch_utilities.h: Utilities for architecture introspection for Mac platform.
+
+#ifndef COMMON_MAC_ARCH_UTILITIES_H__
+#define COMMON_MAC_ARCH_UTILITIES_H__
+
+#include <mach-o/arch.h>
+
+namespace google_breakpad {
+
+// Custom implementation of |NXGetArchInfoFromName| and
+// |NXGetArchInfoFromCpuType| that handle newer CPU on older OSes.
+const NXArchInfo* BreakpadGetArchInfoFromName(const char* arch_name);
+const NXArchInfo* BreakpadGetArchInfoFromCpuType(cpu_type_t cpu_type,
+ cpu_subtype_t cpu_subtype);
+
+} // namespace google_breakpad
+
+#endif // COMMON_MAC_ARCH_UTILITIES_H__
diff --git a/3rdParty/Breakpad/src/common/mac/byteswap.h b/3rdParty/Breakpad/src/common/mac/byteswap.h
index a5d745b..b7bbc0b 100644
--- a/3rdParty/Breakpad/src/common/mac/byteswap.h
+++ b/3rdParty/Breakpad/src/common/mac/byteswap.h
@@ -36,6 +36,7 @@
#ifndef COMMON_MAC_BYTESWAP_H_
#define COMMON_MAC_BYTESWAP_H_
+#ifdef __APPLE__
#include <libkern/OSByteOrder.h>
static inline uint16_t ByteSwap(uint16_t v) { return OSSwapInt16(v); }
@@ -45,4 +46,28 @@ static inline int16_t ByteSwap(int16_t v) { return OSSwapInt16(v); }
static inline int32_t ByteSwap(int32_t v) { return OSSwapInt32(v); }
static inline int64_t ByteSwap(int64_t v) { return OSSwapInt64(v); }
+#elif defined(__linux__)
+// For NXByteOrder
+#include <architecture/byte_order.h>
+#include <stdint.h>
+#include <endian.h>
+#include_next <byteswap.h>
+
+static inline uint16_t ByteSwap(uint16_t v) { return bswap_16(v); }
+static inline uint32_t ByteSwap(uint32_t v) { return bswap_32(v); }
+static inline uint64_t ByteSwap(uint64_t v) { return bswap_64(v); }
+static inline int16_t ByteSwap(int16_t v) { return bswap_16(v); }
+static inline int32_t ByteSwap(int32_t v) { return bswap_32(v); }
+static inline int64_t ByteSwap(int64_t v) { return bswap_64(v); }
+
+static inline NXByteOrder NXHostByteOrder() {
+#ifdef __LITTLE_ENDIAN
+ return NX_LittleEndian;
+#else
+ return NX_BigEndian;
+#endif
+}
+
+#endif // __APPLE__
+
#endif // COMMON_MAC_BYTESWAP_H_
diff --git a/3rdParty/Breakpad/src/common/mac/dump_syms.mm b/3rdParty/Breakpad/src/common/mac/dump_syms.cc
index 9783514..eb92729 100644
--- a/3rdParty/Breakpad/src/common/mac/dump_syms.mm
+++ b/3rdParty/Breakpad/src/common/mac/dump_syms.cc
@@ -31,14 +31,21 @@
// Author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
-// dump_syms.mm: Create a symbol file for use with minidumps
+// dump_syms.cc: Create a symbol file for use with minidumps
#include "common/mac/dump_syms.h"
-#include <Foundation/Foundation.h>
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <libgen.h>
#include <mach-o/arch.h>
#include <mach-o/fat.h>
+#include <stdint.h>
#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
#include <ostream>
#include <string>
@@ -50,15 +57,22 @@
#include "common/dwarf_cu_to_module.h"
#include "common/dwarf_line_to_module.h"
#include "common/mac/file_id.h"
+#include "common/mac/arch_utilities.h"
#include "common/mac/macho_reader.h"
#include "common/module.h"
+#include "common/scoped_ptr.h"
#include "common/stabs_reader.h"
#include "common/stabs_to_module.h"
+#include "common/symbol_data.h"
#ifndef CPU_TYPE_ARM
#define CPU_TYPE_ARM (static_cast<cpu_type_t>(12))
#endif // CPU_TYPE_ARM
+#ifndef CPU_TYPE_ARM64
+#define CPU_TYPE_ARM64 (static_cast<cpu_type_t>(16777228))
+#endif // CPU_TYPE_ARM64
+
using dwarf2reader::ByteReader;
using google_breakpad::DwarfCUToModule;
using google_breakpad::DwarfLineToModule;
@@ -69,109 +83,126 @@ using google_breakpad::mach_o::Segment;
using google_breakpad::Module;
using google_breakpad::StabsReader;
using google_breakpad::StabsToModule;
+using google_breakpad::scoped_ptr;
using std::make_pair;
using std::pair;
using std::string;
using std::vector;
+namespace {
+// Return a vector<string> with absolute paths to all the entries
+// in directory (excluding . and ..).
+vector<string> list_directory(const string& directory) {
+ vector<string> entries;
+ DIR* dir = opendir(directory.c_str());
+ if (!dir) {
+ return entries;
+ }
+
+ string path = directory;
+ if (path[path.length() - 1] != '/') {
+ path += '/';
+ }
+
+ struct dirent* entry = NULL;
+ while ((entry = readdir(dir))) {
+ if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
+ entries.push_back(path + entry->d_name);
+ }
+ }
+
+ closedir(dir);
+ return entries;
+}
+}
+
namespace google_breakpad {
-bool DumpSymbols::Read(NSString *filename) {
- if (![[NSFileManager defaultManager] fileExistsAtPath:filename]) {
- fprintf(stderr, "Object file does not exist: %s\n",
- [filename fileSystemRepresentation]);
+bool DumpSymbols::Read(const string &filename) {
+ struct stat st;
+ if (stat(filename.c_str(), &st) == -1) {
+ fprintf(stderr, "Could not access object file %s: %s\n",
+ filename.c_str(), strerror(errno));
return false;
}
- input_pathname_ = [filename retain];
+ input_pathname_ = filename;
// Does this filename refer to a dSYM bundle?
- NSBundle *bundle = [NSBundle bundleWithPath:input_pathname_];
-
- if (bundle) {
- // Filenames referring to bundles usually have names of the form
- // "<basename>.dSYM"; however, if the user has specified a wrapper
- // suffix (the WRAPPER_SUFFIX and WRAPPER_EXTENSION build settings),
- // then the name may have the form "<basename>.<extension>.dSYM". In
- // either case, the resource name for the file containing the DWARF
- // info within the bundle is <basename>.
- //
- // Since there's no way to tell how much to strip off, remove one
- // extension at a time, and use the first one that
- // pathForResource:ofType:inDirectory likes.
- NSString *base_name = [input_pathname_ lastPathComponent];
- NSString *dwarf_resource;
-
- do {
- NSString *new_base_name = [base_name stringByDeletingPathExtension];
-
- // If stringByDeletingPathExtension returned the name unchanged, then
- // there's nothing more for us to strip off --- lose.
- if ([new_base_name isEqualToString:base_name]) {
- fprintf(stderr, "Unable to find DWARF-bearing file in bundle: %s\n",
- [input_pathname_ fileSystemRepresentation]);
- return false;
- }
-
- // Take the shortened result as our new base_name.
- base_name = new_base_name;
-
- // Try to find a DWARF resource in the bundle under the new base_name.
- dwarf_resource = [bundle pathForResource:base_name
- ofType:nil inDirectory:@"DWARF"];
- } while (!dwarf_resource);
+ string contents_path = input_pathname_ + "/Contents/Resources/DWARF";
+ if (S_ISDIR(st.st_mode) &&
+ access(contents_path.c_str(), F_OK) == 0) {
+ // If there's one file under Contents/Resources/DWARF then use that,
+ // otherwise bail out.
+ const vector<string> entries = list_directory(contents_path);
+ if (entries.size() == 0) {
+ fprintf(stderr, "Unable to find DWARF-bearing file in bundle: %s\n",
+ input_pathname_.c_str());
+ return false;
+ }
+ if (entries.size() > 1) {
+ fprintf(stderr, "Too many DWARF files in bundle: %s\n",
+ input_pathname_.c_str());
+ return false;
+ }
- object_filename_ = [dwarf_resource retain];
+ object_filename_ = entries[0];
} else {
- object_filename_ = [input_pathname_ retain];
+ object_filename_ = input_pathname_;
}
// Read the file's contents into memory.
- //
- // The documentation for dataWithContentsOfMappedFile says:
- //
- // Because of file mapping restrictions, this method should only be
- // used if the file is guaranteed to exist for the duration of the
- // data object’s existence. It is generally safer to use the
- // dataWithContentsOfFile: method.
- //
- // I gather this means that OS X doesn't have (or at least, that method
- // doesn't use) a form of mapping like Linux's MAP_PRIVATE, where the
- // process appears to get its own copy of the data, and changes to the
- // file don't affect memory and vice versa).
- NSError *error;
- contents_ = [NSData dataWithContentsOfFile:object_filename_
- options:0
- error:&error];
- if (!contents_) {
+ bool read_ok = true;
+ string error;
+ if (stat(object_filename_.c_str(), &st) != -1) {
+ FILE* f = fopen(object_filename_.c_str(), "rb");
+ if (f) {
+ contents_.reset(new uint8_t[st.st_size]);
+ off_t total = 0;
+ while (total < st.st_size && !feof(f)) {
+ size_t read = fread(&contents_[0] + total, 1, st.st_size - total, f);
+ if (read == 0) {
+ if (ferror(f)) {
+ read_ok = false;
+ error = strerror(errno);
+ }
+ break;
+ }
+ total += read;
+ }
+ fclose(f);
+ } else {
+ error = strerror(errno);
+ }
+ }
+
+ if (!read_ok) {
fprintf(stderr, "Error reading object file: %s: %s\n",
- [object_filename_ fileSystemRepresentation],
- [[error localizedDescription] UTF8String]);
+ object_filename_.c_str(),
+ error.c_str());
return false;
}
- [contents_ retain];
// Get the list of object files present in the file.
- FatReader::Reporter fat_reporter([object_filename_
- fileSystemRepresentation]);
+ FatReader::Reporter fat_reporter(object_filename_);
FatReader fat_reader(&fat_reporter);
- if (!fat_reader.Read(reinterpret_cast<const uint8_t *>([contents_ bytes]),
- [contents_ length])) {
+ if (!fat_reader.Read(&contents_[0],
+ st.st_size)) {
return false;
}
// Get our own copy of fat_reader's object file list.
size_t object_files_count;
- const struct fat_arch *object_files =
+ const SuperFatArch *object_files =
fat_reader.object_files(&object_files_count);
if (object_files_count == 0) {
fprintf(stderr, "Fat binary file contains *no* architectures: %s\n",
- [object_filename_ fileSystemRepresentation]);
+ object_filename_.c_str());
return false;
}
object_files_.resize(object_files_count);
memcpy(&object_files_[0], object_files,
- sizeof(struct fat_arch) * object_files_count);
+ sizeof(SuperFatArch) * object_files_count);
return true;
}
@@ -179,9 +210,8 @@ bool DumpSymbols::Read(NSString *filename) {
bool DumpSymbols::SetArchitecture(cpu_type_t cpu_type,
cpu_subtype_t cpu_subtype) {
// Find the best match for the architecture the user requested.
- const struct fat_arch *best_match
- = NXFindBestFatArch(cpu_type, cpu_subtype, &object_files_[0],
- static_cast<uint32_t>(object_files_.size()));
+ const SuperFatArch *best_match = FindBestMatchForArchitecture(
+ cpu_type, cpu_subtype);
if (!best_match) return false;
// Record the selected object file.
@@ -191,20 +221,73 @@ bool DumpSymbols::SetArchitecture(cpu_type_t cpu_type,
bool DumpSymbols::SetArchitecture(const std::string &arch_name) {
bool arch_set = false;
- const NXArchInfo *arch_info = NXGetArchInfoFromName(arch_name.c_str());
+ const NXArchInfo *arch_info =
+ google_breakpad::BreakpadGetArchInfoFromName(arch_name.c_str());
if (arch_info) {
arch_set = SetArchitecture(arch_info->cputype, arch_info->cpusubtype);
}
return arch_set;
}
+SuperFatArch* DumpSymbols::FindBestMatchForArchitecture(
+ cpu_type_t cpu_type, cpu_subtype_t cpu_subtype) {
+ // Check if all the object files can be converted to struct fat_arch.
+ bool can_convert_to_fat_arch = true;
+ vector<struct fat_arch> fat_arch_vector;
+ for (vector<SuperFatArch>::const_iterator it = object_files_.begin();
+ it != object_files_.end();
+ ++it) {
+ struct fat_arch arch;
+ bool success = it->ConvertToFatArch(&arch);
+ if (!success) {
+ can_convert_to_fat_arch = false;
+ break;
+ }
+ fat_arch_vector.push_back(arch);
+ }
+
+ // If all the object files can be converted to struct fat_arch, use
+ // NXFindBestFatArch.
+ if (can_convert_to_fat_arch) {
+ const struct fat_arch *best_match
+ = NXFindBestFatArch(cpu_type, cpu_subtype, &fat_arch_vector[0],
+ static_cast<uint32_t>(fat_arch_vector.size()));
+
+ for (size_t i = 0; i < fat_arch_vector.size(); ++i) {
+ if (best_match == &fat_arch_vector[i])
+ return &object_files_[i];
+ }
+ assert(best_match == NULL);
+ return NULL;
+ }
+
+ // Check for an exact match with cpu_type and cpu_subtype.
+ for (vector<SuperFatArch>::iterator it = object_files_.begin();
+ it != object_files_.end();
+ ++it) {
+ if (static_cast<cpu_type_t>(it->cputype) == cpu_type &&
+ static_cast<cpu_subtype_t>(it->cpusubtype) == cpu_subtype)
+ return &*it;
+ }
+
+ // No exact match found.
+ // TODO(erikchen): If it becomes necessary, we can copy the implementation of
+ // NXFindBestFatArch, located at
+ // http://web.mit.edu/darwin/src/modules/cctools/libmacho/arch.c.
+ fprintf(stderr, "Failed to find an exact match for an object file with cpu "
+ "type: %d and cpu subtype: %d. Furthermore, at least one object file is "
+ "larger than 2**32.\n", cpu_type, cpu_subtype);
+ return NULL;
+}
+
string DumpSymbols::Identifier() {
- FileID file_id([object_filename_ fileSystemRepresentation]);
+ FileID file_id(object_filename_.c_str());
unsigned char identifier_bytes[16];
cpu_type_t cpu_type = selected_object_file_->cputype;
- if (!file_id.MachoIdentifier(cpu_type, identifier_bytes)) {
+ cpu_subtype_t cpu_subtype = selected_object_file_->cpusubtype;
+ if (!file_id.MachoIdentifier(cpu_type, cpu_subtype, identifier_bytes)) {
fprintf(stderr, "Unable to calculate UUID of mach-o binary %s!\n",
- [object_filename_ fileSystemRepresentation]);
+ object_filename_.c_str());
return "";
}
@@ -224,24 +307,90 @@ string DumpSymbols::Identifier() {
// dwarf2reader::LineInfo and populates a Module and a line vector
// with the results.
class DumpSymbols::DumperLineToModule:
- public DwarfCUToModule::LineToModuleFunctor {
+ public DwarfCUToModule::LineToModuleHandler {
public:
// Create a line-to-module converter using BYTE_READER.
DumperLineToModule(dwarf2reader::ByteReader *byte_reader)
: byte_reader_(byte_reader) { }
- void operator()(const char *program, uint64 length,
- Module *module, vector<Module::Line> *lines) {
- DwarfLineToModule handler(module, lines);
+
+ void StartCompilationUnit(const string& compilation_dir) {
+ compilation_dir_ = compilation_dir;
+ }
+
+ void ReadProgram(const uint8_t *program, uint64 length,
+ Module *module, vector<Module::Line> *lines) {
+ DwarfLineToModule handler(module, compilation_dir_, lines);
dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler);
parser.Start();
}
private:
+ string compilation_dir_;
dwarf2reader::ByteReader *byte_reader_; // WEAK
};
+bool DumpSymbols::CreateEmptyModule(scoped_ptr<Module>& module) {
+ // Select an object file, if SetArchitecture hasn't been called to set one
+ // explicitly.
+ if (!selected_object_file_) {
+ // If there's only one architecture, that's the one.
+ if (object_files_.size() == 1)
+ selected_object_file_ = &object_files_[0];
+ else {
+ // Look for an object file whose architecture matches our own.
+ const NXArchInfo *local_arch = NXGetLocalArchInfo();
+ if (!SetArchitecture(local_arch->cputype, local_arch->cpusubtype)) {
+ fprintf(stderr, "%s: object file contains more than one"
+ " architecture, none of which match the current"
+ " architecture; specify an architecture explicitly"
+ " with '-a ARCH' to resolve the ambiguity\n",
+ object_filename_.c_str());
+ return false;
+ }
+ }
+ }
+
+ assert(selected_object_file_);
+
+ // Find the name of the selected file's architecture, to appear in
+ // the MODULE record and in error messages.
+ const NXArchInfo *selected_arch_info =
+ google_breakpad::BreakpadGetArchInfoFromCpuType(
+ selected_object_file_->cputype, selected_object_file_->cpusubtype);
+
+ const char *selected_arch_name = selected_arch_info->name;
+ if (strcmp(selected_arch_name, "i386") == 0)
+ selected_arch_name = "x86";
+
+ // Produce a name to use in error messages that includes the
+ // filename, and the architecture, if there is more than one.
+ selected_object_name_ = object_filename_;
+ if (object_files_.size() > 1) {
+ selected_object_name_ += ", architecture ";
+ selected_object_name_ + selected_arch_name;
+ }
+
+ // Compute a module name, to appear in the MODULE record.
+ string module_name = object_filename_;
+ module_name = basename(&module_name[0]);
+
+ // Choose an identifier string, to appear in the MODULE record.
+ string identifier = Identifier();
+ if (identifier.empty())
+ return false;
+ identifier += "0";
+
+ // Create a module to hold the debugging information.
+ module.reset(new Module(module_name,
+ "mac",
+ selected_arch_name,
+ identifier));
+ return true;
+}
+
bool DumpSymbols::ReadDwarf(google_breakpad::Module *module,
const mach_o::Reader &macho_reader,
- const mach_o::SectionMap &dwarf_sections) const {
+ const mach_o::SectionMap &dwarf_sections,
+ bool handle_inter_cu_refs) const {
// Build a byte reader of the appropriate endianness.
ByteReader byte_reader(macho_reader.big_endian()
? dwarf2reader::ENDIANNESS_BIG
@@ -249,19 +398,24 @@ bool DumpSymbols::ReadDwarf(google_breakpad::Module *module,
// Construct a context for this file.
DwarfCUToModule::FileContext file_context(selected_object_name_,
- module);
+ module,
+ handle_inter_cu_refs);
// Build a dwarf2reader::SectionMap from our mach_o::SectionMap.
for (mach_o::SectionMap::const_iterator it = dwarf_sections.begin();
- it != dwarf_sections.end(); it++) {
- file_context.section_map[it->first] =
- make_pair(reinterpret_cast<const char *>(it->second.contents.start),
- it->second.contents.Size());
+ it != dwarf_sections.end(); ++it) {
+ file_context.AddSectionToSectionMap(
+ it->first,
+ it->second.contents.start,
+ it->second.contents.Size());
}
// Find the __debug_info section.
- std::pair<const char *, uint64> debug_info_section
- = file_context.section_map["__debug_info"];
+ dwarf2reader::SectionMap::const_iterator debug_info_entry =
+ file_context.section_map().find("__debug_info");
+ assert(debug_info_entry != file_context.section_map().end());
+ const std::pair<const uint8_t *, uint64>& debug_info_section =
+ debug_info_entry->second;
// There had better be a __debug_info section!
if (!debug_info_section.first) {
fprintf(stderr, "%s: __DWARF segment of file has no __debug_info section\n",
@@ -283,7 +437,8 @@ bool DumpSymbols::ReadDwarf(google_breakpad::Module *module,
// Make a Dwarf2Handler that drives our DIEHandler.
dwarf2reader::DIEDispatcher die_dispatcher(&root_handler);
// Make a DWARF parser for the compilation unit at OFFSET.
- dwarf2reader::CompilationUnit dwarf_reader(file_context.section_map,
+ dwarf2reader::CompilationUnit dwarf_reader(selected_object_name_,
+ file_context.section_map(),
offset,
&byte_reader,
&die_dispatcher);
@@ -311,10 +466,12 @@ bool DumpSymbols::ReadCFI(google_breakpad::Module *module,
case CPU_TYPE_ARM:
register_names = DwarfCFIToModule::RegisterNames::ARM();
break;
+ case CPU_TYPE_ARM64:
+ register_names = DwarfCFIToModule::RegisterNames::ARM64();
+ break;
default: {
- const NXArchInfo *arch =
- NXGetArchInfoFromCpuType(macho_reader.cpu_type(),
- macho_reader.cpu_subtype());
+ const NXArchInfo *arch = google_breakpad::BreakpadGetArchInfoFromCpuType(
+ macho_reader.cpu_type(), macho_reader.cpu_subtype());
fprintf(stderr, "%s: cannot convert DWARF call frame information for ",
selected_object_name_.c_str());
if (arch)
@@ -328,7 +485,7 @@ bool DumpSymbols::ReadCFI(google_breakpad::Module *module,
}
// Find the call frame information and its size.
- const char *cfi = reinterpret_cast<const char *>(section.contents.start);
+ const uint8_t *cfi = section.contents.start;
size_t cfi_size = section.contents.Size();
// Plug together the parser, handler, and their entourages.
@@ -362,8 +519,14 @@ class DumpSymbols::LoadCommandDumper:
// file, and adding data to MODULE.
LoadCommandDumper(const DumpSymbols &dumper,
google_breakpad::Module *module,
- const mach_o::Reader &reader)
- : dumper_(dumper), module_(module), reader_(reader) { }
+ const mach_o::Reader &reader,
+ SymbolData symbol_data,
+ bool handle_inter_cu_refs)
+ : dumper_(dumper),
+ module_(module),
+ reader_(reader),
+ symbol_data_(symbol_data),
+ handle_inter_cu_refs_(handle_inter_cu_refs) { }
bool SegmentCommand(const mach_o::Segment &segment);
bool SymtabCommand(const ByteBuffer &entries, const ByteBuffer &strings);
@@ -372,6 +535,8 @@ class DumpSymbols::LoadCommandDumper:
const DumpSymbols &dumper_;
google_breakpad::Module *module_; // WEAK
const mach_o::Reader &reader_;
+ const SymbolData symbol_data_;
+ const bool handle_inter_cu_refs_;
};
bool DumpSymbols::LoadCommandDumper::SegmentCommand(const Segment &segment) {
@@ -381,23 +546,31 @@ bool DumpSymbols::LoadCommandDumper::SegmentCommand(const Segment &segment) {
if (segment.name == "__TEXT") {
module_->SetLoadAddress(segment.vmaddr);
- mach_o::SectionMap::const_iterator eh_frame =
- section_map.find("__eh_frame");
- if (eh_frame != section_map.end()) {
- // If there is a problem reading this, don't treat it as a fatal error.
- dumper_.ReadCFI(module_, reader_, eh_frame->second, true);
+ if (symbol_data_ != NO_CFI) {
+ mach_o::SectionMap::const_iterator eh_frame =
+ section_map.find("__eh_frame");
+ if (eh_frame != section_map.end()) {
+ // If there is a problem reading this, don't treat it as a fatal error.
+ dumper_.ReadCFI(module_, reader_, eh_frame->second, true);
+ }
}
return true;
}
if (segment.name == "__DWARF") {
- if (!dumper_.ReadDwarf(module_, reader_, section_map))
- return false;
- mach_o::SectionMap::const_iterator debug_frame
- = section_map.find("__debug_frame");
- if (debug_frame != section_map.end()) {
- // If there is a problem reading this, don't treat it as a fatal error.
- dumper_.ReadCFI(module_, reader_, debug_frame->second, false);
+ if (symbol_data_ != ONLY_CFI) {
+ if (!dumper_.ReadDwarf(module_, reader_, section_map,
+ handle_inter_cu_refs_)) {
+ return false;
+ }
+ }
+ if (symbol_data_ != NO_CFI) {
+ mach_o::SectionMap::const_iterator debug_frame
+ = section_map.find("__debug_frame");
+ if (debug_frame != section_map.end()) {
+ // If there is a problem reading this, don't treat it as a fatal error.
+ dumper_.ReadCFI(module_, reader_, debug_frame->second, false);
+ }
}
}
@@ -421,64 +594,15 @@ bool DumpSymbols::LoadCommandDumper::SymtabCommand(const ByteBuffer &entries,
return true;
}
-bool DumpSymbols::WriteSymbolFile(std::ostream &stream, bool cfi) {
- // Select an object file, if SetArchitecture hasn't been called to set one
- // explicitly.
- if (!selected_object_file_) {
- // If there's only one architecture, that's the one.
- if (object_files_.size() == 1)
- selected_object_file_ = &object_files_[0];
- else {
- // Look for an object file whose architecture matches our own.
- const NXArchInfo *local_arch = NXGetLocalArchInfo();
- if (!SetArchitecture(local_arch->cputype, local_arch->cpusubtype)) {
- fprintf(stderr, "%s: object file contains more than one"
- " architecture, none of which match the current"
- " architecture; specify an architecture explicitly"
- " with '-a ARCH' to resolve the ambiguity\n",
- [object_filename_ fileSystemRepresentation]);
- return false;
- }
- }
- }
-
- assert(selected_object_file_);
-
- // Find the name of the selected file's architecture, to appear in
- // the MODULE record and in error messages.
- const NXArchInfo *selected_arch_info
- = NXGetArchInfoFromCpuType(selected_object_file_->cputype,
- selected_object_file_->cpusubtype);
-
- const char *selected_arch_name = selected_arch_info->name;
- if (strcmp(selected_arch_name, "i386") == 0)
- selected_arch_name = "x86";
-
- // Produce a name to use in error messages that includes the
- // filename, and the architecture, if there is more than one.
- selected_object_name_ = [object_filename_ UTF8String];
- if (object_files_.size() > 1) {
- selected_object_name_ += ", architecture ";
- selected_object_name_ + selected_arch_name;
- }
-
- // Compute a module name, to appear in the MODULE record.
- NSString *module_name = [object_filename_ lastPathComponent];
-
- // Choose an identifier string, to appear in the MODULE record.
- string identifier = Identifier();
- if (identifier.empty())
+bool DumpSymbols::ReadSymbolData(Module** out_module) {
+ scoped_ptr<Module> module;
+ if (!CreateEmptyModule(module))
return false;
- identifier += "0";
-
- // Create a module to hold the debugging information.
- Module module([module_name UTF8String], "mac", selected_arch_name,
- identifier);
// Parse the selected object file.
mach_o::Reader::Reporter reporter(selected_object_name_);
mach_o::Reader reader(&reporter);
- if (!reader.Read(reinterpret_cast<const uint8_t *>([contents_ bytes])
+ if (!reader.Read(&contents_[0]
+ selected_object_file_->offset,
selected_object_file_->size,
selected_object_file_->cputype,
@@ -486,11 +610,37 @@ bool DumpSymbols::WriteSymbolFile(std::ostream &stream, bool cfi) {
return false;
// Walk its load commands, and deal with whatever is there.
- LoadCommandDumper load_command_dumper(*this, &module, reader);
+ LoadCommandDumper load_command_dumper(*this, module.get(), reader,
+ symbol_data_, handle_inter_cu_refs_);
if (!reader.WalkLoadCommands(&load_command_dumper))
return false;
- return module.Write(stream, cfi);
+ *out_module = module.release();
+
+ return true;
+}
+
+bool DumpSymbols::WriteSymbolFile(std::ostream &stream) {
+ Module* module = NULL;
+
+ if (ReadSymbolData(&module) && module) {
+ bool res = module->Write(stream, symbol_data_);
+ delete module;
+ return res;
+ }
+
+ return false;
+}
+
+// Read the selected object file's debugging information, and write out the
+// header only to |stream|. Return true on success; if an error occurs, report
+// it and return false.
+bool DumpSymbols::WriteSymbolFileHeader(std::ostream &stream) {
+ scoped_ptr<Module> module;
+ if (!CreateEmptyModule(module))
+ return false;
+
+ return module->Write(stream, symbol_data_);
}
} // namespace google_breakpad
diff --git a/3rdParty/Breakpad/src/common/mac/dump_syms.h b/3rdParty/Breakpad/src/common/mac/dump_syms.h
index 0e2f464..9463f7d 100644
--- a/3rdParty/Breakpad/src/common/mac/dump_syms.h
+++ b/3rdParty/Breakpad/src/common/mac/dump_syms.h
@@ -35,7 +35,6 @@
// reading debugging information from Mach-O files and writing it out as a
// Breakpad symbol file.
-#include <Foundation/Foundation.h>
#include <mach-o/loader.h>
#include <stdio.h>
#include <stdlib.h>
@@ -46,33 +45,32 @@
#include "common/byte_cursor.h"
#include "common/mac/macho_reader.h"
+#include "common/mac/super_fat_arch.h"
#include "common/module.h"
+#include "common/scoped_ptr.h"
+#include "common/symbol_data.h"
namespace google_breakpad {
class DumpSymbols {
public:
- DumpSymbols()
- : input_pathname_(),
+ DumpSymbols(SymbolData symbol_data, bool handle_inter_cu_refs)
+ : symbol_data_(symbol_data),
+ handle_inter_cu_refs_(handle_inter_cu_refs),
+ input_pathname_(),
object_filename_(),
contents_(),
+ object_files_(),
selected_object_file_(),
selected_object_name_() { }
~DumpSymbols() {
- [input_pathname_ release];
- [object_filename_ release];
- [contents_ release];
}
// Prepare to read debugging information from |filename|. |filename| may be
// the name of a universal binary, a Mach-O file, or a dSYM bundle
// containing either of the above. On success, return true; if there is a
// problem reading |filename|, report it and return false.
- //
- // (This class uses NSString for filenames and related values,
- // because the Mac Foundation framework seems to support
- // filename-related operations more fully on NSString values.)
- bool Read(NSString *filename);
+ bool Read(const std::string &filename);
// If this dumper's file includes an object file for |cpu_type| and
// |cpu_subtype|, then select that object file for dumping, and return
@@ -95,14 +93,14 @@ class DumpSymbols {
// architecture matches that of this dumper program.
bool SetArchitecture(const std::string &arch_name);
- // Return a pointer to an array of 'struct fat_arch' structures,
- // describing the object files contained in this dumper's file. Set
- // *|count| to the number of elements in the array. The returned array is
- // owned by this DumpSymbols instance.
+ // Return a pointer to an array of SuperFatArch structures describing the
+ // object files contained in this dumper's file. Set *|count| to the number
+ // of elements in the array. The returned array is owned by this DumpSymbols
+ // instance.
//
// If there are no available architectures, this function
// may return NULL.
- const struct fat_arch *AvailableArchitectures(size_t *count) {
+ const SuperFatArch* AvailableArchitectures(size_t *count) {
*count = object_files_.size();
if (object_files_.size() > 0)
return &object_files_[0];
@@ -110,24 +108,44 @@ class DumpSymbols {
}
// Read the selected object file's debugging information, and write it out to
- // |stream|. Write the CFI section if |cfi| is true. Return true on success;
- // if an error occurs, report it and return false.
- bool WriteSymbolFile(std::ostream &stream, bool cfi);
+ // |stream|. Return true on success; if an error occurs, report it and
+ // return false.
+ bool WriteSymbolFile(std::ostream &stream);
+
+ // Read the selected object file's debugging information, and write out the
+ // header only to |stream|. Return true on success; if an error occurs, report
+ // it and return false.
+ bool WriteSymbolFileHeader(std::ostream &stream);
+
+ // As above, but simply return the debugging information in module
+ // instead of writing it to a stream. The caller owns the resulting
+ // module object and must delete it when finished.
+ bool ReadSymbolData(Module** module);
private:
// Used internally.
class DumperLineToModule;
class LoadCommandDumper;
+ // This method behaves similarly to NXFindBestFatArch, but it supports
+ // SuperFatArch.
+ SuperFatArch* FindBestMatchForArchitecture(
+ cpu_type_t cpu_type, cpu_subtype_t cpu_subtype);
+
// Return an identifier string for the file this DumpSymbols is dumping.
std::string Identifier();
+
+ // Creates an empty module object.
+ bool CreateEmptyModule(scoped_ptr<Module>& module);
+
// Read debugging information from |dwarf_sections|, which was taken from
// |macho_reader|, and add it to |module|. On success, return true;
// on failure, report the problem and return false.
bool ReadDwarf(google_breakpad::Module *module,
const mach_o::Reader &macho_reader,
- const mach_o::SectionMap &dwarf_sections) const;
+ const mach_o::SectionMap &dwarf_sections,
+ bool handle_inter_cu_refs) const;
// Read DWARF CFI or .eh_frame data from |section|, belonging to
// |macho_reader|, and record it in |module|. If |eh_frame| is true,
@@ -139,28 +157,34 @@ class DumpSymbols {
const mach_o::Section &section,
bool eh_frame) const;
+ // The selection of what type of symbol data to read/write.
+ const SymbolData symbol_data_;
+
+ // Whether to handle references between compilation units.
+ const bool handle_inter_cu_refs_;
+
// The name of the file or bundle whose symbols this will dump.
// This is the path given to Read, for use in error messages.
- NSString *input_pathname_;
+ std::string input_pathname_;
// The name of the file this DumpSymbols will actually read debugging
// information from. Normally, this is the same as input_pathname_, but if
// filename refers to a dSYM bundle, then this is the resource file
// within that bundle.
- NSString *object_filename_;
+ std::string object_filename_;
// The complete contents of object_filename_, mapped into memory.
- NSData *contents_;
+ scoped_array<uint8_t> contents_;
- // A vector of fat_arch structures describing the object files
+ // A vector of SuperFatArch structures describing the object files
// object_filename_ contains. If object_filename_ refers to a fat binary,
// this may have more than one element; if it refers to a Mach-O file, this
// has exactly one element.
- vector<struct fat_arch> object_files_;
+ vector<SuperFatArch> object_files_;
// The object file in object_files_ selected to dump, or NULL if
// SetArchitecture hasn't been called yet.
- const struct fat_arch *selected_object_file_;
+ const SuperFatArch *selected_object_file_;
// A string that identifies the selected object file, for use in error
// messages. This is usually object_filename_, but if that refers to a
diff --git a/3rdParty/Breakpad/src/common/mac/file_id.cc b/3rdParty/Breakpad/src/common/mac/file_id.cc
index 50502e4..4661d5d 100644
--- a/3rdParty/Breakpad/src/common/mac/file_id.cc
+++ b/3rdParty/Breakpad/src/common/mac/file_id.cc
@@ -34,6 +34,7 @@
// Author: Dan Waylonis
#include <fcntl.h>
+#include <stdio.h>
#include <string.h>
#include <unistd.h>
@@ -45,7 +46,7 @@ using MacFileUtilities::MachoID;
namespace google_breakpad {
FileID::FileID(const char *path) {
- strlcpy(path_, path, sizeof(path_));
+ snprintf(path_, sizeof(path_), "%s", path);
}
bool FileID::FileIdentifier(unsigned char identifier[16]) {
@@ -61,7 +62,7 @@ bool FileID::FileIdentifier(unsigned char identifier[16]) {
unsigned char buffer[4096 * 2];
size_t buffer_size = sizeof(buffer);
while ((buffer_size = read(fd, buffer, buffer_size) > 0)) {
- MD5Update(&md5, buffer, buffer_size);
+ MD5Update(&md5, buffer, static_cast<unsigned>(buffer_size));
}
close(fd);
@@ -70,13 +71,15 @@ bool FileID::FileIdentifier(unsigned char identifier[16]) {
return true;
}
-bool FileID::MachoIdentifier(int cpu_type, unsigned char identifier[16]) {
+bool FileID::MachoIdentifier(cpu_type_t cpu_type,
+ cpu_subtype_t cpu_subtype,
+ unsigned char identifier[16]) {
MachoID macho(path_);
- if (macho.UUIDCommand(cpu_type, identifier))
+ if (macho.UUIDCommand(cpu_type, cpu_subtype, identifier))
return true;
- return macho.MD5(cpu_type, identifier);
+ return macho.MD5(cpu_type, cpu_subtype, identifier);
}
// static
@@ -90,8 +93,10 @@ void FileID::ConvertIdentifierToString(const unsigned char identifier[16],
if (idx == 4 || idx == 6 || idx == 8 || idx == 10)
buffer[buffer_idx++] = '-';
- buffer[buffer_idx++] = (hi >= 10) ? 'A' + hi - 10 : '0' + hi;
- buffer[buffer_idx++] = (lo >= 10) ? 'A' + lo - 10 : '0' + lo;
+ buffer[buffer_idx++] =
+ static_cast<char>((hi >= 10) ? ('A' + hi - 10) : ('0' + hi));
+ buffer[buffer_idx++] =
+ static_cast<char>((lo >= 10) ? ('A' + lo - 10) : ('0' + lo));
}
// NULL terminate
diff --git a/3rdParty/Breakpad/src/common/mac/file_id.h b/3rdParty/Breakpad/src/common/mac/file_id.h
index eb06b0d..1d6dfde 100644
--- a/3rdParty/Breakpad/src/common/mac/file_id.h
+++ b/3rdParty/Breakpad/src/common/mac/file_id.h
@@ -35,6 +35,7 @@
#define COMMON_MAC_FILE_ID_H__
#include <limits.h>
+#include <mach/machine.h>
namespace google_breakpad {
@@ -50,15 +51,18 @@ class FileID {
bool FileIdentifier(unsigned char identifier[16]);
// Treat the file as a mach-o file that will contain one or more archicture.
- // Accepted values for |cpu_type| (e.g., CPU_TYPE_X86 or CPU_TYPE_POWERPC)
- // are listed in /usr/include/mach/machine.h.
- // If |cpu_type| is 0, then the native cpu type is used.
- // Returns false if opening the file failed or if the |cpu_type| is not
- // present in the file.
+ // Accepted values for |cpu_type| and |cpu_subtype| (e.g., CPU_TYPE_X86 or
+ // CPU_TYPE_POWERPC) are listed in /usr/include/mach/machine.h.
+ // If |cpu_type| is 0, then the native cpu type is used. If |cpu_subtype| is
+ // CPU_SUBTYPE_MULTIPLE, the match is only done on |cpu_type|.
+ // Returns false if opening the file failed or if the |cpu_type|/|cpu_subtype|
+ // is not present in the file.
// Return the unique identifier in |identifier|.
// The current implementation will look for the (in order of priority):
// LC_UUID, LC_ID_DYLIB, or MD5 hash of the given |cpu_type|.
- bool MachoIdentifier(int cpu_type, unsigned char identifier[16]);
+ bool MachoIdentifier(cpu_type_t cpu_type,
+ cpu_subtype_t cpu_subtype,
+ unsigned char identifier[16]);
// Convert the |identifier| data to a NULL terminated string. The string will
// be formatted as a UUID (e.g., 22F065BB-FC9C-49F7-80FE-26A7CEBD7BCE).
@@ -75,4 +79,3 @@ class FileID {
} // namespace google_breakpad
#endif // COMMON_MAC_FILE_ID_H__
-
diff --git a/3rdParty/Breakpad/src/common/mac/launch_reporter.cc b/3rdParty/Breakpad/src/common/mac/launch_reporter.cc
new file mode 100644
index 0000000..245be82
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/mac/launch_reporter.cc
@@ -0,0 +1,84 @@
+// Copyright (c) 2014, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <stdio.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+namespace google_breakpad {
+
+void LaunchReporter(const char *reporter_executable_path,
+ const char *config_file_path) {
+ const char* argv[] = { reporter_executable_path, config_file_path, NULL };
+
+ // Launch the reporter
+ pid_t pid = fork();
+
+ if (pid == -1) {
+ perror("fork");
+ fprintf(stderr, "Failed to fork reporter process\n");
+ return;
+ }
+
+ // If we're in the child, load in our new executable and run.
+ // The parent will not wait for the child to complete.
+ if (pid == 0) {
+ execv(argv[0], (char* const*)argv);
+ perror("exec");
+ fprintf(stderr,
+ "Failed to launch reporter process from path %s\n",
+ reporter_executable_path);
+ unlink(config_file_path); // launch failed - get rid of config file
+ _exit(1);
+ }
+
+ // Wait until the Reporter child process exits.
+ //
+
+ // We'll use a timeout of one minute.
+ int timeout_count = 60; // 60 seconds
+
+ while (timeout_count-- > 0) {
+ int status;
+ pid_t result = waitpid(pid, &status, WNOHANG);
+
+ if (result == 0) {
+ // The child has not yet finished.
+ sleep(1);
+ } else if (result == -1) {
+ // error occurred.
+ break;
+ } else {
+ // child has finished
+ break;
+ }
+ }
+}
+
+} // namespace google_breakpad
diff --git a/3rdParty/Breakpad/src/common/mac/launch_reporter.h b/3rdParty/Breakpad/src/common/mac/launch_reporter.h
new file mode 100644
index 0000000..4531123
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/mac/launch_reporter.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2014, 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.
+
+#ifndef COMMON_MAC_LAUNCH_REPORTER_H__
+#define COMMON_MAC_LAUNCH_REPORTER_H__
+
+namespace google_breakpad {
+
+// Launch the crash dump sender app.
+// |reporter_executable_path| is the path to the sender executable.
+// |config_file_path| is the path to the config file.
+void LaunchReporter(const char *reporter_executable_path,
+ const char *config_file_path);
+
+} // namespace google_breakpad
+
+#endif // COMMON_MAC_LAUNCH_REPORTER_H__
diff --git a/3rdParty/Breakpad/src/common/mac/macho_id.cc b/3rdParty/Breakpad/src/common/mac/macho_id.cc
index abe1fab..c396ad8 100644
--- a/3rdParty/Breakpad/src/common/mac/macho_id.cc
+++ b/3rdParty/Breakpad/src/common/mac/macho_id.cc
@@ -33,17 +33,15 @@
//
// Author: Dan Waylonis
-extern "C" { // necessary for Leopard
- #include <fcntl.h>
- #include <mach-o/loader.h>
- #include <mach-o/swap.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/time.h>
- #include <sys/types.h>
- #include <unistd.h>
-}
+
+#include <fcntl.h>
+#include <mach-o/loader.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
#include "common/mac/macho_id.h"
#include "common/mac/macho_walker.h"
@@ -61,7 +59,7 @@ MachoID::MachoID(const char *path)
crc_(0),
md5_context_(),
update_function_(NULL) {
- strlcpy(path_, path, sizeof(path_));
+ snprintf(path_, sizeof(path_), "%s", path);
}
MachoID::MachoID(const char *path, void *memory, size_t size)
@@ -70,7 +68,7 @@ MachoID::MachoID(const char *path, void *memory, size_t size)
crc_(0),
md5_context_(),
update_function_(NULL) {
- strlcpy(path_, path, sizeof(path_));
+ snprintf(path_, sizeof(path_), "%s", path);
}
MachoID::~MachoID() {
@@ -125,7 +123,7 @@ void MachoID::UpdateCRC(unsigned char *bytes, size_t size) {
}
void MachoID::UpdateMD5(unsigned char *bytes, size_t size) {
- MD5Update(&md5_context_, bytes, size);
+ MD5Update(&md5_context_, bytes, static_cast<unsigned>(size));
}
void MachoID::Update(MachoWalker *walker, off_t offset, size_t size) {
@@ -153,10 +151,12 @@ void MachoID::Update(MachoWalker *walker, off_t offset, size_t size) {
}
}
-bool MachoID::UUIDCommand(int cpu_type, unsigned char bytes[16]) {
+bool MachoID::UUIDCommand(cpu_type_t cpu_type,
+ cpu_subtype_t cpu_subtype,
+ unsigned char bytes[16]) {
struct breakpad_uuid_command uuid_cmd;
uuid_cmd.cmd = 0;
- if (!WalkHeader(cpu_type, UUIDWalkerCB, &uuid_cmd))
+ if (!WalkHeader(cpu_type, cpu_subtype, UUIDWalkerCB, &uuid_cmd))
return false;
// If we found the command, we'll have initialized the uuid_command
@@ -169,10 +169,12 @@ bool MachoID::UUIDCommand(int cpu_type, unsigned char bytes[16]) {
return false;
}
-bool MachoID::IDCommand(int cpu_type, unsigned char identifier[16]) {
+bool MachoID::IDCommand(cpu_type_t cpu_type,
+ cpu_subtype_t cpu_subtype,
+ unsigned char identifier[16]) {
struct dylib_command dylib_cmd;
dylib_cmd.cmd = 0;
- if (!WalkHeader(cpu_type, IDWalkerCB, &dylib_cmd))
+ if (!WalkHeader(cpu_type, cpu_subtype, IDWalkerCB, &dylib_cmd))
return false;
// If we found the command, we'll have initialized the dylib_command
@@ -210,37 +212,38 @@ bool MachoID::IDCommand(int cpu_type, unsigned char identifier[16]) {
return false;
}
-uint32_t MachoID::Adler32(int cpu_type) {
+uint32_t MachoID::Adler32(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype) {
update_function_ = &MachoID::UpdateCRC;
crc_ = 0;
- if (!WalkHeader(cpu_type, WalkerCB, this))
+ if (!WalkHeader(cpu_type, cpu_subtype, WalkerCB, this))
return 0;
return crc_;
}
-bool MachoID::MD5(int cpu_type, unsigned char identifier[16]) {
+bool MachoID::MD5(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype, unsigned char identifier[16]) {
update_function_ = &MachoID::UpdateMD5;
MD5Init(&md5_context_);
- if (!WalkHeader(cpu_type, WalkerCB, this))
+ if (!WalkHeader(cpu_type, cpu_subtype, WalkerCB, this))
return false;
MD5Final(identifier, &md5_context_);
return true;
}
-bool MachoID::WalkHeader(int cpu_type,
+bool MachoID::WalkHeader(cpu_type_t cpu_type,
+ cpu_subtype_t cpu_subtype,
MachoWalker::LoadCommandCallback callback,
void *context) {
if (memory_) {
MachoWalker walker(memory_, memory_size_, callback, context);
- return walker.WalkHeader(cpu_type);
+ return walker.WalkHeader(cpu_type, cpu_subtype);
} else {
MachoWalker walker(path_, callback, context);
- return walker.WalkHeader(cpu_type);
+ return walker.WalkHeader(cpu_type, cpu_subtype);
}
}
@@ -256,7 +259,7 @@ bool MachoID::WalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,
return false;
if (swap)
- swap_segment_command(&seg, NXHostByteOrder());
+ breakpad_swap_segment_command(&seg);
struct mach_header_64 header;
off_t header_offset;
@@ -273,7 +276,7 @@ bool MachoID::WalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,
return false;
if (swap)
- swap_section(&sec, 1, NXHostByteOrder());
+ breakpad_swap_section(&sec, 1);
// sections of type S_ZEROFILL are "virtual" and contain no data
// in the file itself
@@ -289,7 +292,7 @@ bool MachoID::WalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,
return false;
if (swap)
- breakpad_swap_segment_command_64(&seg64, NXHostByteOrder());
+ breakpad_swap_segment_command_64(&seg64);
struct mach_header_64 header;
off_t header_offset;
@@ -306,7 +309,7 @@ bool MachoID::WalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,
return false;
if (swap)
- breakpad_swap_section_64(&sec64, 1, NXHostByteOrder());
+ breakpad_swap_section_64(&sec64, 1);
// sections of type S_ZEROFILL are "virtual" and contain no data
// in the file itself
@@ -335,7 +338,7 @@ bool MachoID::UUIDWalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,
return false;
if (swap)
- breakpad_swap_uuid_command(uuid_cmd, NXHostByteOrder());
+ breakpad_swap_uuid_command(uuid_cmd);
return false;
}
@@ -354,7 +357,7 @@ bool MachoID::IDWalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,
return false;
if (swap)
- swap_dylib_command(dylib_cmd, NXHostByteOrder());
+ breakpad_swap_dylib_command(dylib_cmd);
return false;
}
diff --git a/3rdParty/Breakpad/src/common/mac/macho_id.h b/3rdParty/Breakpad/src/common/mac/macho_id.h
index ccb126d..1037549 100644
--- a/3rdParty/Breakpad/src/common/mac/macho_id.h
+++ b/3rdParty/Breakpad/src/common/mac/macho_id.h
@@ -35,6 +35,7 @@
#define COMMON_MAC_MACHO_ID_H__
#include <limits.h>
+#include <mach/machine.h>
#include <mach-o/loader.h>
#include "common/mac/macho_walker.h"
@@ -48,22 +49,32 @@ class MachoID {
MachoID(const char *path, void *memory, size_t size);
~MachoID();
- // For the given |cpu_type|, return a UUID from the LC_UUID command.
+ // For the given |cpu_type| and |cpu_subtype|, return a UUID from the LC_UUID
+ // command.
// Return false if there isn't a LC_UUID command.
- bool UUIDCommand(int cpu_type, unsigned char identifier[16]);
+ bool UUIDCommand(cpu_type_t cpu_type,
+ cpu_subtype_t cpu_subtype,
+ unsigned char identifier[16]);
- // For the given |cpu_type|, return a UUID from the LC_ID_DYLIB command.
+ // For the given |cpu_type| and |cpu_subtype|, return a UUID from the
+ // LC_ID_DYLIB command.
// Return false if there isn't a LC_ID_DYLIB command.
- bool IDCommand(int cpu_type, unsigned char identifier[16]);
+ bool IDCommand(cpu_type_t cpu_type,
+ cpu_subtype_t cpu_subtype,
+ unsigned char identifier[16]);
- // For the given |cpu_type|, return the Adler32 CRC for the mach-o data
- // segment(s).
+ // For the given |cpu_type| and |cpu_subtype|, return the Adler32 CRC for the
+ // mach-o data segment(s).
// Return 0 on error (e.g., if the file is not a mach-o file)
- uint32_t Adler32(int cpu_type);
+ uint32_t Adler32(cpu_type_t cpu_type,
+ cpu_subtype_t cpu_subtype);
- // For the given |cpu_type|, return the MD5 for the mach-o data segment(s).
+ // For the given |cpu_type|, and |cpu_subtype| return the MD5 for the mach-o
+ // data segment(s).
// Return true on success, false otherwise
- bool MD5(int cpu_type, unsigned char identifier[16]);
+ bool MD5(cpu_type_t cpu_type,
+ cpu_subtype_t cpu_subtype,
+ unsigned char identifier[16]);
private:
// Signature of class member function to be called with data read from file
@@ -81,8 +92,8 @@ class MachoID {
void Update(MachoWalker *walker, off_t offset, size_t size);
// Factory for the MachoWalker
- bool WalkHeader(int cpu_type, MachoWalker::LoadCommandCallback callback,
- void *context);
+ bool WalkHeader(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype,
+ MachoWalker::LoadCommandCallback callback, void *context);
// The callback from the MachoWalker for CRC and MD5
static bool WalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,
diff --git a/3rdParty/Breakpad/src/common/mac/macho_reader.cc b/3rdParty/Breakpad/src/common/mac/macho_reader.cc
index f1f0a17..52f3c41 100644
--- a/3rdParty/Breakpad/src/common/mac/macho_reader.cc
+++ b/3rdParty/Breakpad/src/common/mac/macho_reader.cc
@@ -43,6 +43,10 @@
#define CPU_TYPE_ARM 12
#endif
+#if !defined(CPU_TYPE_ARM_64)
+#define CPU_TYPE_ARM_64 16777228
+#endif
+
namespace google_breakpad {
namespace mach_o {
@@ -97,22 +101,26 @@ bool FatReader::Read(const uint8_t *buffer, size_t size) {
// Read the list of object files.
object_files_.resize(object_files_count);
for (size_t i = 0; i < object_files_count; i++) {
- struct fat_arch *objfile = &object_files_[i];
+ struct fat_arch objfile;
// Read this object file entry, byte-swapping as appropriate.
- cursor >> objfile->cputype
- >> objfile->cpusubtype
- >> objfile->offset
- >> objfile->size
- >> objfile->align;
+ cursor >> objfile.cputype
+ >> objfile.cpusubtype
+ >> objfile.offset
+ >> objfile.size
+ >> objfile.align;
+
+ SuperFatArch super_fat_arch(objfile);
+ object_files_[i] = super_fat_arch;
+
if (!cursor) {
reporter_->TooShort();
return false;
}
// Does the file actually have the bytes this entry refers to?
size_t fat_size = buffer_.Size();
- if (objfile->offset > fat_size ||
- objfile->size > fat_size - objfile->offset) {
+ if (objfile.offset > fat_size ||
+ objfile.size > fat_size - objfile.offset) {
reporter_->MisplacedObjectFile();
return false;
}
@@ -135,16 +143,14 @@ bool FatReader::Read(const uint8_t *buffer, size_t size) {
}
object_files_[0].offset = 0;
- object_files_[0].size = static_cast<uint32_t>(buffer_.Size());
+ object_files_[0].size = static_cast<uint64_t>(buffer_.Size());
// This alignment is correct for 32 and 64-bit x86 and ppc.
// See get_align in the lipo source for other architectures:
// http://www.opensource.apple.com/source/cctools/cctools-773/misc/lipo.c
object_files_[0].align = 12; // 2^12 == 4096
-
return true;
}
}
-
reporter_->BadHeader();
return false;
}
@@ -175,15 +181,15 @@ void Reader::Reporter::LoadCommandRegionTruncated() {
void Reader::Reporter::LoadCommandsOverrun(size_t claimed, size_t i,
LoadCommandType type) {
- fprintf(stderr, "%s: file's header claims there are %ld"
- " load commands, but load command #%ld",
+ fprintf(stderr, "%s: file's header claims there are %zu"
+ " load commands, but load command #%zu",
filename_.c_str(), claimed, i);
if (type) fprintf(stderr, ", of type %d,", type);
fprintf(stderr, " extends beyond the end of the load command region\n");
}
void Reader::Reporter::LoadCommandTooShort(size_t i, LoadCommandType type) {
- fprintf(stderr, "%s: the contents of load command #%ld, of type %d,"
+ fprintf(stderr, "%s: the contents of load command #%zu, of type %d,"
" extend beyond the size given in the load command's header\n",
filename_.c_str(), i, type);
}
@@ -242,6 +248,7 @@ bool Reader::Read(const uint8_t *buffer,
case CPU_TYPE_POWERPC:
expected_magic = MH_MAGIC;
break;
+ case CPU_TYPE_ARM_64:
case CPU_TYPE_X86_64:
expected_magic = MH_CIGAM_64;
break;
@@ -310,7 +317,7 @@ bool Reader::WalkLoadCommands(Reader::LoadCommandHandler *handler) const {
// remainder of the load command series.
ByteBuffer command(list_cursor.here(), list_cursor.Available());
ByteCursor cursor(&command, big_endian_);
-
+
// Read the command type and size --- fields common to all commands.
uint32_t type, size;
if (!(cursor >> type)) {
@@ -395,7 +402,7 @@ bool Reader::WalkLoadCommands(Reader::LoadCommandHandler *handler) const {
return false;
break;
}
-
+
default: {
if (!handler->UnknownCommand(type, command))
return false;
@@ -414,7 +421,7 @@ class Reader::SegmentFinder : public LoadCommandHandler {
public:
// Create a load command handler that looks for a segment named NAME,
// and sets SEGMENT to describe it if found.
- SegmentFinder(const string &name, Segment *segment)
+ SegmentFinder(const string &name, Segment *segment)
: name_(name), segment_(segment), found_() { }
// Return true if the traversal found the segment, false otherwise.
@@ -474,10 +481,12 @@ bool Reader::WalkSegmentSections(const Segment &segment,
reporter_->SectionsMissing(segment.name);
return false;
}
- if ((section.flags & SECTION_TYPE) == S_ZEROFILL) {
+ const uint32_t section_type = section.flags & SECTION_TYPE;
+ if (section_type == S_ZEROFILL || section_type == S_THREAD_LOCAL_ZEROFILL ||
+ section_type == S_GB_ZEROFILL) {
// Zero-fill sections have a size, but no contents.
section.contents.start = section.contents.end = NULL;
- } else if (segment.contents.start == NULL &&
+ } else if (segment.contents.start == NULL &&
segment.contents.end == NULL) {
// Mach-O files in .dSYM bundles have the contents of the loaded
// segments removed, and their file offsets and file sizes zeroed
diff --git a/3rdParty/Breakpad/src/common/mac/macho_reader.h b/3rdParty/Breakpad/src/common/mac/macho_reader.h
index 7537648..30db742 100644
--- a/3rdParty/Breakpad/src/common/mac/macho_reader.h
+++ b/3rdParty/Breakpad/src/common/mac/macho_reader.h
@@ -47,6 +47,7 @@
#include <vector>
#include "common/byte_cursor.h"
+#include "common/mac/super_fat_arch.h"
namespace google_breakpad {
namespace mach_o {
@@ -93,7 +94,7 @@ class FatReader {
// complete header, or the header implies that contents are present
// beyond the actual end of the file.
virtual void TooShort();
-
+
private:
// The filename to which the reader should attribute problems.
string filename_;
@@ -101,7 +102,7 @@ class FatReader {
// Create a fat binary file reader that uses |reporter| to report problems.
explicit FatReader(Reporter *reporter) : reporter_(reporter) { }
-
+
// Read the |size| bytes at |buffer| as a fat binary file. On success,
// return true; on failure, report the problem to reporter_ and return
// false.
@@ -111,13 +112,13 @@ class FatReader {
// single object file is the Mach-O file.
bool Read(const uint8_t *buffer, size_t size);
- // Return an array of 'struct fat_arch' structures describing the
+ // Return an array of 'SuperFatArch' structures describing the
// object files present in this fat binary file. Set |size| to the
// number of elements in the array.
//
- // Assuming Read returned true, the entries are validated: it is
- // safe to assume that the offsets and sizes in each 'struct
- // fat_arch' refer to subranges of the bytes passed to Read.
+ // Assuming Read returned true, the entries are validated: it is safe to
+ // assume that the offsets and sizes in each SuperFatArch refer to subranges
+ // of the bytes passed to Read.
//
// If there are no object files in this fat binary, then this
// function can return NULL.
@@ -129,7 +130,7 @@ class FatReader {
// possible to use the result with OS X functions like NXFindBestFatArch,
// so that the symbol dumper will behave consistently with other OS X
// utilities that work with fat binaries.
- const struct fat_arch *object_files(size_t *count) const {
+ const SuperFatArch* object_files(size_t *count) const {
*count = object_files_.size();
if (object_files_.size() > 0)
return &object_files_[0];
@@ -149,7 +150,7 @@ class FatReader {
// The list of object files in this binary.
// object_files_.size() == fat_header.nfat_arch
- vector<struct fat_arch> object_files_;
+ vector<SuperFatArch> object_files_;
};
// A segment in a Mach-O file. All these fields have been byte-swapped as
@@ -177,7 +178,7 @@ struct Segment {
// The maximum and initial VM protection of this segment's contents.
uint32_t maxprot;
uint32_t initprot;
-
+
// The number of sections in section_list.
uint32_t nsects;
@@ -376,7 +377,7 @@ class Reader {
return Read(buffer.start,
buffer.Size(),
expected_cpu_type,
- expected_cpu_subtype);
+ expected_cpu_subtype);
}
// Return this file's characteristics, as found in the Mach-O header.
diff --git a/3rdParty/Breakpad/src/common/mac/macho_reader_unittest.cc b/3rdParty/Breakpad/src/common/mac/macho_reader_unittest.cc
new file mode 100644
index 0000000..d8459d8
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/mac/macho_reader_unittest.cc
@@ -0,0 +1,1902 @@
+// 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.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// macho_reader_unittest.cc: Unit tests for google_breakpad::Mach_O::FatReader
+// and google_breakpad::Mach_O::Reader.
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "breakpad_googletest_includes.h"
+#include "common/mac/macho_reader.h"
+#include "common/test_assembler.h"
+
+namespace mach_o = google_breakpad::mach_o;
+namespace test_assembler = google_breakpad::test_assembler;
+
+using mach_o::FatReader;
+using mach_o::FileFlags;
+using mach_o::FileType;
+using mach_o::LoadCommandType;
+using mach_o::Reader;
+using mach_o::Section;
+using mach_o::SectionMap;
+using mach_o::Segment;
+using test_assembler::Endianness;
+using test_assembler::Label;
+using test_assembler::kBigEndian;
+using test_assembler::kLittleEndian;
+using test_assembler::kUnsetEndian;
+using google_breakpad::ByteBuffer;
+using std::map;
+using std::string;
+using std::vector;
+using testing::AllOf;
+using testing::DoAll;
+using testing::Field;
+using testing::InSequence;
+using testing::Matcher;
+using testing::Return;
+using testing::SaveArg;
+using testing::Test;
+using testing::_;
+
+
+// Mock classes for the reader's various reporters and handlers.
+
+class MockFatReaderReporter: public FatReader::Reporter {
+ public:
+ MockFatReaderReporter(const string &filename)
+ : FatReader::Reporter(filename) { }
+ MOCK_METHOD0(BadHeader, void());
+ MOCK_METHOD0(MisplacedObjectFile, void());
+ MOCK_METHOD0(TooShort, void());
+};
+
+class MockReaderReporter: public Reader::Reporter {
+ public:
+ MockReaderReporter(const string &filename) : Reader::Reporter(filename) { }
+ MOCK_METHOD0(BadHeader, void());
+ MOCK_METHOD4(CPUTypeMismatch, void(cpu_type_t cpu_type,
+ cpu_subtype_t cpu_subtype,
+ cpu_type_t expected_cpu_type,
+ cpu_subtype_t expected_cpu_subtype));
+ MOCK_METHOD0(HeaderTruncated, void());
+ MOCK_METHOD0(LoadCommandRegionTruncated, void());
+ MOCK_METHOD3(LoadCommandsOverrun, void(size_t claimed, size_t i,
+ LoadCommandType type));
+ MOCK_METHOD2(LoadCommandTooShort, void(size_t i, LoadCommandType type));
+ MOCK_METHOD1(SectionsMissing, void(const string &name));
+ MOCK_METHOD1(MisplacedSegmentData, void(const string &name));
+ MOCK_METHOD2(MisplacedSectionData, void(const string &section,
+ const string &segment));
+ MOCK_METHOD0(MisplacedSymbolTable, void());
+ MOCK_METHOD1(UnsupportedCPUType, void(cpu_type_t cpu_type));
+};
+
+class MockLoadCommandHandler: public Reader::LoadCommandHandler {
+ public:
+ MOCK_METHOD2(UnknownCommand, bool(LoadCommandType, const ByteBuffer &));
+ MOCK_METHOD1(SegmentCommand, bool(const Segment &));
+ MOCK_METHOD2(SymtabCommand, bool(const ByteBuffer &, const ByteBuffer &));
+};
+
+class MockSectionHandler: public Reader::SectionHandler {
+ public:
+ MOCK_METHOD1(HandleSection, bool(const Section &section));
+};
+
+
+// Tests for mach_o::FatReader.
+
+// Since the effect of these functions is to write to stderr, the
+// results of these tests must be inspected by hand.
+TEST(FatReaderReporter, BadHeader) {
+ FatReader::Reporter reporter("filename");
+ reporter.BadHeader();
+}
+
+TEST(FatReaderReporter, MisplacedObjectFile) {
+ FatReader::Reporter reporter("filename");
+ reporter.MisplacedObjectFile();
+}
+
+TEST(FatReaderReporter, TooShort) {
+ FatReader::Reporter reporter("filename");
+ reporter.TooShort();
+}
+
+TEST(MachOReaderReporter, BadHeader) {
+ Reader::Reporter reporter("filename");
+ reporter.BadHeader();
+}
+
+TEST(MachOReaderReporter, CPUTypeMismatch) {
+ Reader::Reporter reporter("filename");
+ reporter.CPUTypeMismatch(CPU_TYPE_I386, CPU_SUBTYPE_X86_ALL,
+ CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL);
+}
+
+TEST(MachOReaderReporter, HeaderTruncated) {
+ Reader::Reporter reporter("filename");
+ reporter.HeaderTruncated();
+}
+
+TEST(MachOReaderReporter, LoadCommandRegionTruncated) {
+ Reader::Reporter reporter("filename");
+ reporter.LoadCommandRegionTruncated();
+}
+
+TEST(MachOReaderReporter, LoadCommandsOverrun) {
+ Reader::Reporter reporter("filename");
+ reporter.LoadCommandsOverrun(10, 9, LC_DYSYMTAB);
+ reporter.LoadCommandsOverrun(10, 9, 0);
+}
+
+TEST(MachOReaderReporter, LoadCommandTooShort) {
+ Reader::Reporter reporter("filename");
+ reporter.LoadCommandTooShort(11, LC_SYMTAB);
+}
+
+TEST(MachOReaderReporter, SectionsMissing) {
+ Reader::Reporter reporter("filename");
+ reporter.SectionsMissing("segment name");
+}
+
+TEST(MachOReaderReporter, MisplacedSegmentData) {
+ Reader::Reporter reporter("filename");
+ reporter.MisplacedSegmentData("segment name");
+}
+
+TEST(MachOReaderReporter, MisplacedSectionData) {
+ Reader::Reporter reporter("filename");
+ reporter.MisplacedSectionData("section name", "segment name");
+}
+
+TEST(MachOReaderReporter, MisplacedSymbolTable) {
+ Reader::Reporter reporter("filename");
+ reporter.MisplacedSymbolTable();
+}
+
+TEST(MachOReaderReporter, UnsupportedCPUType) {
+ Reader::Reporter reporter("filename");
+ reporter.UnsupportedCPUType(CPU_TYPE_HPPA);
+}
+
+struct FatReaderFixture {
+ FatReaderFixture()
+ : fat(kBigEndian),
+ reporter("reporter filename"),
+ reader(&reporter), object_files() {
+ EXPECT_CALL(reporter, BadHeader()).Times(0);
+ EXPECT_CALL(reporter, TooShort()).Times(0);
+
+ // here, start, and Mark are file offsets in 'fat'.
+ fat.start() = 0;
+ }
+ // Append a 'fat_arch' entry to 'fat', with the given field values.
+ void AppendFatArch(cpu_type_t type, cpu_subtype_t subtype,
+ Label offset, Label size, uint32_t align) {
+ fat
+ .B32(type)
+ .B32(subtype)
+ .B32(offset)
+ .B32(size)
+ .B32(align);
+ }
+ // Append |n| dummy 'fat_arch' entries to 'fat'. The cpu type and
+ // subtype have unrealistic values.
+ void AppendDummyArchEntries(int n) {
+ for (int i = 0; i < n; i++)
+ AppendFatArch(0xb68ad617, 0x715a0840, 0, 0, 1);
+ }
+ void ReadFat(bool expect_parse_success = true) {
+ ASSERT_TRUE(fat.GetContents(&contents));
+ fat_bytes = reinterpret_cast<const uint8_t *>(contents.data());
+ if (expect_parse_success) {
+ EXPECT_TRUE(reader.Read(fat_bytes, contents.size()));
+ size_t fat_files_count;
+ const SuperFatArch* fat_files = reader.object_files(&fat_files_count);
+ object_files.resize(fat_files_count);
+ for (size_t i = 0; i < fat_files_count; ++i) {
+ EXPECT_TRUE(fat_files[i].ConvertToFatArch(&object_files[i]));
+ }
+ }
+ else
+ EXPECT_FALSE(reader.Read(fat_bytes, contents.size()));
+ }
+ test_assembler::Section fat;
+ MockFatReaderReporter reporter;
+ FatReader reader;
+ string contents;
+ const uint8_t *fat_bytes;
+ vector<struct fat_arch> object_files;
+};
+
+class FatReaderTest: public FatReaderFixture, public Test { };
+
+TEST_F(FatReaderTest, BadMagic) {
+ EXPECT_CALL(reporter, BadHeader()).Times(1);
+ fat
+ .B32(0xcafed00d) // magic number (incorrect)
+ .B32(10); // number of architectures
+ AppendDummyArchEntries(10);
+ ReadFat(false);
+}
+
+TEST_F(FatReaderTest, HeaderTooShort) {
+ EXPECT_CALL(reporter, TooShort()).Times(1);
+ fat
+ .B32(0xcafebabe); // magic number
+ ReadFat(false);
+}
+
+TEST_F(FatReaderTest, ObjectListTooShort) {
+ EXPECT_CALL(reporter, TooShort()).Times(1);
+ fat
+ .B32(0xcafebabe) // magic number
+ .B32(10); // number of architectures
+ AppendDummyArchEntries(9); // nine dummy architecture entries...
+ fat // and a tenth, missing a byte at the end
+ .B32(0x3d46c8fc) // cpu type
+ .B32(0x8a7bfb01) // cpu subtype
+ .B32(0) // offset
+ .B32(0) // size
+ .Append(3, '*'); // one byte short of a four-byte alignment
+ ReadFat(false);
+}
+
+TEST_F(FatReaderTest, DataTooShort) {
+ EXPECT_CALL(reporter, MisplacedObjectFile()).Times(1);
+ Label arch_data;
+ fat
+ .B32(0xcafebabe) // magic number
+ .B32(1); // number of architectures
+ AppendFatArch(0xb4d4a366, 0x4ba4f525, arch_data, 40, 0);
+ fat
+ .Mark(&arch_data) // file data begins here
+ .Append(30, '*'); // only 30 bytes, not 40 as header claims
+ ReadFat(false);
+}
+
+TEST_F(FatReaderTest, NoObjectFiles) {
+ fat
+ .B32(0xcafebabe) // magic number
+ .B32(0); // number of architectures
+ ReadFat();
+ EXPECT_EQ(0U, object_files.size());
+}
+
+TEST_F(FatReaderTest, OneObjectFile) {
+ Label obj1_offset;
+ fat
+ .B32(0xcafebabe) // magic number
+ .B32(1); // number of architectures
+ // First object file list entry
+ AppendFatArch(0x5e3a6e91, 0x52ccd852, obj1_offset, 0x42, 0x355b15b2);
+ // First object file data
+ fat
+ .Mark(&obj1_offset)
+ .Append(0x42, '*'); // dummy contents
+ ReadFat();
+ ASSERT_EQ(1U, object_files.size());
+ EXPECT_EQ(0x5e3a6e91, object_files[0].cputype);
+ EXPECT_EQ(0x52ccd852, object_files[0].cpusubtype);
+ EXPECT_EQ(obj1_offset.Value(), object_files[0].offset);
+ EXPECT_EQ(0x42U, object_files[0].size);
+ EXPECT_EQ(0x355b15b2U, object_files[0].align);
+}
+
+TEST_F(FatReaderTest, ThreeObjectFiles) {
+ Label obj1, obj2, obj3;
+ fat
+ .B32(0xcafebabe) // magic number
+ .B32(3); // number of architectures
+ // Three object file list entries.
+ AppendFatArch(0x0cb92c30, 0x6a159a71, obj1, 0xfb4, 0x2615dbe8);
+ AppendFatArch(0x0f3f1cbb, 0x6c55e90f, obj2, 0xc31, 0x83af6ffd);
+ AppendFatArch(0x3717276d, 0x10ecdc84, obj3, 0x4b3, 0x035267d7);
+ fat
+ // First object file data
+ .Mark(&obj1)
+ .Append(0xfb4, '*') // dummy contents
+ // Second object file data
+ .Mark(&obj2)
+ .Append(0xc31, '%') // dummy contents
+ // Third object file data
+ .Mark(&obj3)
+ .Append(0x4b3, '^'); // dummy contents
+
+ ReadFat();
+
+ ASSERT_EQ(3U, object_files.size());
+
+ // First object file.
+ EXPECT_EQ(0x0cb92c30, object_files[0].cputype);
+ EXPECT_EQ(0x6a159a71, object_files[0].cpusubtype);
+ EXPECT_EQ(obj1.Value(), object_files[0].offset);
+ EXPECT_EQ(0xfb4U, object_files[0].size);
+ EXPECT_EQ(0x2615dbe8U, object_files[0].align);
+
+ // Second object file.
+ EXPECT_EQ(0x0f3f1cbb, object_files[1].cputype);
+ EXPECT_EQ(0x6c55e90f, object_files[1].cpusubtype);
+ EXPECT_EQ(obj2.Value(), object_files[1].offset);
+ EXPECT_EQ(0xc31U, object_files[1].size);
+ EXPECT_EQ(0x83af6ffdU, object_files[1].align);
+
+ // Third object file.
+ EXPECT_EQ(0x3717276d, object_files[2].cputype);
+ EXPECT_EQ(0x10ecdc84, object_files[2].cpusubtype);
+ EXPECT_EQ(obj3.Value(), object_files[2].offset);
+ EXPECT_EQ(0x4b3U, object_files[2].size);
+ EXPECT_EQ(0x035267d7U, object_files[2].align);
+}
+
+TEST_F(FatReaderTest, BigEndianMachO32) {
+ fat.set_endianness(kBigEndian);
+ fat
+ .D32(0xfeedface) // Mach-O file magic number
+ .D32(0x1a9d0518) // cpu type
+ .D32(0x1b779357) // cpu subtype
+ .D32(0x009df67e) // file type
+ .D32(0) // no load commands
+ .D32(0) // the load commands occupy no bytes
+ .D32(0x21987a99); // flags
+
+ ReadFat();
+
+ // FatReader should treat a Mach-O file as if it were a fat binary file
+ // containing one object file --- the whole thing.
+ ASSERT_EQ(1U, object_files.size());
+ EXPECT_EQ(0x1a9d0518, object_files[0].cputype);
+ EXPECT_EQ(0x1b779357, object_files[0].cpusubtype);
+ EXPECT_EQ(0U, object_files[0].offset);
+ EXPECT_EQ(contents.size(), object_files[0].size);
+}
+
+TEST_F(FatReaderTest, BigEndianMachO64) {
+ fat.set_endianness(kBigEndian);
+ fat
+ .D32(0xfeedfacf) // Mach-O 64-bit file magic number
+ .D32(0x5aff8487) // cpu type
+ .D32(0x4c6a57f7) // cpu subtype
+ .D32(0x4392d2c8) // file type
+ .D32(0) // no load commands
+ .D32(0) // the load commands occupy no bytes
+ .D32(0x1b033eea); // flags
+
+ ReadFat();
+
+ // FatReader should treat a Mach-O file as if it were a fat binary file
+ // containing one object file --- the whole thing.
+ ASSERT_EQ(1U, object_files.size());
+ EXPECT_EQ(0x5aff8487, object_files[0].cputype);
+ EXPECT_EQ(0x4c6a57f7, object_files[0].cpusubtype);
+ EXPECT_EQ(0U, object_files[0].offset);
+ EXPECT_EQ(contents.size(), object_files[0].size);
+}
+
+TEST_F(FatReaderTest, LittleEndianMachO32) {
+ fat.set_endianness(kLittleEndian);
+ fat
+ .D32(0xfeedface) // Mach-O file magic number
+ .D32(0x1a9d0518) // cpu type
+ .D32(0x1b779357) // cpu subtype
+ .D32(0x009df67e) // file type
+ .D32(0) // no load commands
+ .D32(0) // the load commands occupy no bytes
+ .D32(0x21987a99); // flags
+
+ ReadFat();
+
+ // FatReader should treat a Mach-O file as if it were a fat binary file
+ // containing one object file --- the whole thing.
+ ASSERT_EQ(1U, object_files.size());
+ EXPECT_EQ(0x1a9d0518, object_files[0].cputype);
+ EXPECT_EQ(0x1b779357, object_files[0].cpusubtype);
+ EXPECT_EQ(0U, object_files[0].offset);
+ EXPECT_EQ(contents.size(), object_files[0].size);
+}
+
+TEST_F(FatReaderTest, LittleEndianMachO64) {
+ fat.set_endianness(kLittleEndian);
+ fat
+ .D32(0xfeedfacf) // Mach-O 64-bit file magic number
+ .D32(0x5aff8487) // cpu type
+ .D32(0x4c6a57f7) // cpu subtype
+ .D32(0x4392d2c8) // file type
+ .D32(0) // no load commands
+ .D32(0) // the load commands occupy no bytes
+ .D32(0x1b033eea); // flags
+
+ ReadFat();
+
+ // FatReader should treat a Mach-O file as if it were a fat binary file
+ // containing one object file --- the whole thing.
+ ASSERT_EQ(1U, object_files.size());
+ EXPECT_EQ(0x5aff8487, object_files[0].cputype);
+ EXPECT_EQ(0x4c6a57f7, object_files[0].cpusubtype);
+ EXPECT_EQ(0U, object_files[0].offset);
+ EXPECT_EQ(contents.size(), object_files[0].size);
+}
+
+TEST_F(FatReaderTest, IncompleteMach) {
+ fat.set_endianness(kLittleEndian);
+ fat
+ .D32(0xfeedfacf) // Mach-O 64-bit file magic number
+ .D32(0x5aff8487); // cpu type
+ // Truncated!
+
+ EXPECT_CALL(reporter, TooShort()).WillOnce(Return());
+
+ ReadFat(false);
+}
+
+
+// General mach_o::Reader tests.
+
+// Dynamically scoped configuration data.
+class WithConfiguration {
+ public:
+ // Establish the given parameters as the default for SizedSections
+ // created within the dynamic scope of this instance.
+ WithConfiguration(Endianness endianness, size_t word_size)
+ : endianness_(endianness), word_size_(word_size), saved_(current_) {
+ current_ = this;
+ }
+ ~WithConfiguration() { current_ = saved_; }
+ static Endianness endianness() {
+ assert(current_);
+ return current_->endianness_;
+ }
+ static size_t word_size() {
+ assert(current_);
+ return current_->word_size_;
+ }
+
+ private:
+ // The innermost WithConfiguration in whose dynamic scope we are
+ // currently executing.
+ static WithConfiguration *current_;
+
+ // The innermost WithConfiguration whose dynamic scope encloses this
+ // WithConfiguration.
+ Endianness endianness_;
+ size_t word_size_;
+ WithConfiguration *saved_;
+};
+
+WithConfiguration *WithConfiguration::current_ = NULL;
+
+// A test_assembler::Section with a size that we can cite. The start(),
+// Here() and Mark() member functions of a SizedSection always represent
+// offsets within the overall file.
+class SizedSection: public test_assembler::Section {
+ public:
+ // Construct a section of the given endianness and word size.
+ explicit SizedSection(Endianness endianness, size_t word_size)
+ : test_assembler::Section(endianness), word_size_(word_size) {
+ assert(word_size_ == 32 || word_size_ == 64);
+ }
+ SizedSection()
+ : test_assembler::Section(WithConfiguration::endianness()),
+ word_size_(WithConfiguration::word_size()) {
+ assert(word_size_ == 32 || word_size_ == 64);
+ }
+
+ // Access/set this section's word size.
+ size_t word_size() const { return word_size_; }
+ void set_word_size(size_t word_size) {
+ assert(word_size_ == 32 || word_size_ == 64);
+ word_size_ = word_size;
+ }
+
+ // Return a label representing the size this section will have when it
+ // is Placed in some containing section.
+ Label final_size() const { return final_size_; }
+
+ // Append SECTION to the end of this section, and call its Finish member.
+ // Return a reference to this section.
+ SizedSection &Place(SizedSection *section) {
+ assert(section->endianness() == endianness());
+ section->Finish();
+ section->start() = Here();
+ test_assembler::Section::Append(*section);
+ return *this;
+ }
+
+ protected:
+ // Mark this section's contents as complete. For plain SizedSections, we
+ // set SECTION's start to its position in this section, and its final_size
+ // label to its current size. Derived classes can extend this as needed
+ // for their additional semantics.
+ virtual void Finish() {
+ final_size_ = Size();
+ }
+
+ // The word size for this data: either 32 or 64.
+ size_t word_size_;
+
+ private:
+ // This section's final size, set when we are placed in some other
+ // SizedSection.
+ Label final_size_;
+};
+
+// A SizedSection that is loaded into memory at a particular address.
+class LoadedSection: public SizedSection {
+ public:
+ explicit LoadedSection(Label address = Label()) : address_(address) { }
+
+ // Return a label representing this section's address.
+ Label address() const { return address_; }
+
+ // Placing a loaded section within a loaded section sets the relationship
+ // between their addresses.
+ LoadedSection &Place(LoadedSection *section) {
+ section->address() = address() + Size();
+ SizedSection::Place(section);
+ return *this;
+ }
+
+ protected:
+ // The address at which this section's contents will be loaded.
+ Label address_;
+};
+
+// A SizedSection representing a segment load command.
+class SegmentLoadCommand: public SizedSection {
+ public:
+ SegmentLoadCommand() : section_count_(0) { }
+
+ // Append a segment load command header with the given characteristics.
+ // The load command will refer to CONTENTS, which must be Placed in the
+ // file separately, at the desired position. Return a reference to this
+ // section.
+ SegmentLoadCommand &Header(const string &name, const LoadedSection &contents,
+ uint32_t maxprot, uint32_t initprot,
+ uint32_t flags) {
+ assert(contents.word_size() == word_size());
+ D32(word_size() == 32 ? LC_SEGMENT : LC_SEGMENT_64);
+ D32(final_size());
+ AppendCString(name, 16);
+ Append(endianness(), word_size() / 8, contents.address());
+ Append(endianness(), word_size() / 8, vmsize_);
+ Append(endianness(), word_size() / 8, contents.start());
+ Append(endianness(), word_size() / 8, contents.final_size());
+ D32(maxprot);
+ D32(initprot);
+ D32(final_section_count_);
+ D32(flags);
+
+ content_final_size_ = contents.final_size();
+
+ return *this;
+ }
+
+ // Return a label representing the size of this segment when loaded into
+ // memory. If this label is still undefined by the time we place this
+ // segment, it defaults to the final size of the segment's in-file
+ // contents. Return a reference to this load command.
+ Label &vmsize() { return vmsize_; }
+
+ // Add a section entry with the given characteristics to this segment
+ // load command. Return a reference to this. The section entry will refer
+ // to CONTENTS, which must be Placed in the segment's contents
+ // separately, at the desired position.
+ SegmentLoadCommand &AppendSectionEntry(const string &section_name,
+ const string &segment_name,
+ uint32_t alignment, uint32_t flags,
+ const LoadedSection &contents) {
+ AppendCString(section_name, 16);
+ AppendCString(segment_name, 16);
+ Append(endianness(), word_size() / 8, contents.address());
+ Append(endianness(), word_size() / 8, contents.final_size());
+ D32(contents.start());
+ D32(alignment);
+ D32(0); // relocations start
+ D32(0); // relocations size
+ D32(flags);
+ D32(0x93656b95); // reserved1
+ D32(0xc35a2473); // reserved2
+ if (word_size() == 64)
+ D32(0x70284b95); // reserved3
+
+ section_count_++;
+
+ return *this;
+ }
+
+ protected:
+ void Finish() {
+ final_section_count_ = section_count_;
+ if (!vmsize_.IsKnownConstant())
+ vmsize_ = content_final_size_;
+ SizedSection::Finish();
+ }
+
+ private:
+ // The number of sections that have been added to this segment so far.
+ size_t section_count_;
+
+ // A label representing the final number of sections this segment will hold.
+ Label final_section_count_;
+
+ // The size of the contents for this segment present in the file.
+ Label content_final_size_;
+
+ // A label representing the size of this segment when loaded; this can be
+ // larger than the size of its file contents, the difference being
+ // zero-filled. If not set explicitly by calling set_vmsize, this is set
+ // equal to the size of the contents.
+ Label vmsize_;
+};
+
+// A SizedSection holding a list of Mach-O load commands.
+class LoadCommands: public SizedSection {
+ public:
+ LoadCommands() : command_count_(0) { }
+
+ // Return a label representing the final load command count.
+ Label final_command_count() const { return final_command_count_; }
+
+ // Increment the command count; return a reference to this section.
+ LoadCommands &CountCommand() {
+ command_count_++;
+ return *this;
+ }
+
+ // Place COMMAND, containing a load command, at the end of this section.
+ // Return a reference to this section.
+ LoadCommands &Place(SizedSection *section) {
+ SizedSection::Place(section);
+ CountCommand();
+ return *this;
+ }
+
+ protected:
+ // Mark this load command list as complete.
+ void Finish() {
+ SizedSection::Finish();
+ final_command_count_ = command_count_;
+ }
+
+ private:
+ // The number of load commands we have added to this file so far.
+ size_t command_count_;
+
+ // A label representing the final command count.
+ Label final_command_count_;
+};
+
+// A SizedSection holding the contents of a Mach-O file. Within a
+// MachOFile, the start, Here, and Mark members refer to file offsets.
+class MachOFile: public SizedSection {
+ public:
+ MachOFile() {
+ start() = 0;
+ }
+
+ // Create a Mach-O file header using the given characteristics and load
+ // command list. This Places COMMANDS immediately after the header.
+ // Return a reference to this section.
+ MachOFile &Header(LoadCommands *commands,
+ cpu_type_t cpu_type = CPU_TYPE_X86,
+ cpu_subtype_t cpu_subtype = CPU_SUBTYPE_I386_ALL,
+ FileType file_type = MH_EXECUTE,
+ uint32_t file_flags = (MH_TWOLEVEL |
+ MH_DYLDLINK |
+ MH_NOUNDEFS)) {
+ D32(word_size() == 32 ? 0xfeedface : 0xfeedfacf); // magic number
+ D32(cpu_type); // cpu type
+ D32(cpu_subtype); // cpu subtype
+ D32(file_type); // file type
+ D32(commands->final_command_count()); // number of load commands
+ D32(commands->final_size()); // their size in bytes
+ D32(file_flags); // flags
+ if (word_size() == 64)
+ D32(0x55638b90); // reserved
+ Place(commands);
+ return *this;
+ }
+};
+
+
+struct ReaderFixture {
+ ReaderFixture()
+ : reporter("reporter filename"),
+ reader(&reporter) {
+ EXPECT_CALL(reporter, BadHeader()).Times(0);
+ EXPECT_CALL(reporter, CPUTypeMismatch(_, _, _, _)).Times(0);
+ EXPECT_CALL(reporter, HeaderTruncated()).Times(0);
+ EXPECT_CALL(reporter, LoadCommandRegionTruncated()).Times(0);
+ EXPECT_CALL(reporter, LoadCommandsOverrun(_, _, _)).Times(0);
+ EXPECT_CALL(reporter, LoadCommandTooShort(_, _)).Times(0);
+ EXPECT_CALL(reporter, SectionsMissing(_)).Times(0);
+ EXPECT_CALL(reporter, MisplacedSegmentData(_)).Times(0);
+ EXPECT_CALL(reporter, MisplacedSectionData(_, _)).Times(0);
+ EXPECT_CALL(reporter, MisplacedSymbolTable()).Times(0);
+ EXPECT_CALL(reporter, UnsupportedCPUType(_)).Times(0);
+
+ EXPECT_CALL(load_command_handler, UnknownCommand(_, _)).Times(0);
+ EXPECT_CALL(load_command_handler, SegmentCommand(_)).Times(0);
+ }
+
+ void ReadFile(MachOFile *file,
+ bool expect_parse_success,
+ cpu_type_t expected_cpu_type,
+ cpu_subtype_t expected_cpu_subtype) {
+ ASSERT_TRUE(file->GetContents(&file_contents));
+ file_bytes = reinterpret_cast<const uint8_t *>(file_contents.data());
+ if (expect_parse_success) {
+ EXPECT_TRUE(reader.Read(file_bytes,
+ file_contents.size(),
+ expected_cpu_type,
+ expected_cpu_subtype));
+ } else {
+ EXPECT_FALSE(reader.Read(file_bytes,
+ file_contents.size(),
+ expected_cpu_type,
+ expected_cpu_subtype));
+ }
+ }
+
+ string file_contents;
+ const uint8_t *file_bytes;
+ MockReaderReporter reporter;
+ Reader reader;
+ MockLoadCommandHandler load_command_handler;
+ MockSectionHandler section_handler;
+};
+
+class ReaderTest: public ReaderFixture, public Test { };
+
+TEST_F(ReaderTest, BadMagic) {
+ WithConfiguration config(kLittleEndian, 32);
+ const cpu_type_t kCPUType = 0x46b760df;
+ const cpu_subtype_t kCPUSubType = 0x76a0e7f7;
+ MachOFile file;
+ file
+ .D32(0x67bdebe1) // Not a proper magic number.
+ .D32(kCPUType) // cpu type
+ .D32(kCPUSubType) // cpu subtype
+ .D32(0x149fc717) // file type
+ .D32(0) // no load commands
+ .D32(0) // they occupy no bytes
+ .D32(0x80e71d64) // flags
+ .D32(0); // reserved
+ EXPECT_CALL(reporter, BadHeader()).WillOnce(Return());
+ ReadFile(&file, false, CPU_TYPE_ANY, kCPUSubType);
+}
+
+TEST_F(ReaderTest, MismatchedMagic) {
+ WithConfiguration config(kLittleEndian, 32);
+ const cpu_type_t kCPUType = CPU_TYPE_I386;
+ const cpu_subtype_t kCPUSubType = CPU_SUBTYPE_X86_ALL;
+ MachOFile file;
+ file
+ .D32(MH_CIGAM) // Right magic, but winds up wrong
+ // due to bitswapping
+ .D32(kCPUType) // cpu type
+ .D32(kCPUSubType) // cpu subtype
+ .D32(0x149fc717) // file type
+ .D32(0) // no load commands
+ .D32(0) // they occupy no bytes
+ .D32(0x80e71d64) // flags
+ .D32(0); // reserved
+ EXPECT_CALL(reporter, BadHeader()).WillOnce(Return());
+ ReadFile(&file, false, kCPUType, kCPUSubType);
+}
+
+TEST_F(ReaderTest, ShortMagic) {
+ WithConfiguration config(kBigEndian, 32);
+ MachOFile file;
+ file
+ .D16(0xfeed); // magic number
+ // truncated!
+ EXPECT_CALL(reporter, HeaderTruncated()).WillOnce(Return());
+ ReadFile(&file, false, CPU_TYPE_ANY, 0);
+}
+
+TEST_F(ReaderTest, ShortHeader) {
+ WithConfiguration config(kBigEndian, 32);
+ const cpu_type_t kCPUType = CPU_TYPE_ANY;
+ const cpu_subtype_t kCPUSubType = 0x76a0e7f7;
+ MachOFile file;
+ file
+ .D32(0xfeedface) // magic number
+ .D32(kCPUType) // cpu type
+ .D32(kCPUSubType) // cpu subtype
+ .D32(0x149fc717) // file type
+ .D32(0) // no load commands
+ .D32(0); // they occupy no bytes
+ EXPECT_CALL(reporter, HeaderTruncated()).WillOnce(Return());
+ ReadFile(&file, false, CPU_TYPE_ANY, kCPUSubType);
+}
+
+TEST_F(ReaderTest, MismatchedCPU) {
+ WithConfiguration config(kBigEndian, 32);
+ const cpu_type_t kCPUType = CPU_TYPE_I386;
+ const cpu_subtype_t kCPUSubType = CPU_SUBTYPE_X86_ALL;
+ MachOFile file;
+ file
+ .D32(MH_MAGIC) // Right magic for PPC (once bitswapped)
+ .D32(kCPUType) // cpu type
+ .D32(kCPUSubType) // cpu subtype
+ .D32(0x149fc717) // file type
+ .D32(0) // no load commands
+ .D32(0) // they occupy no bytes
+ .D32(0x80e71d64) // flags
+ .D32(0); // reserved
+ EXPECT_CALL(reporter,
+ CPUTypeMismatch(CPU_TYPE_I386, CPU_SUBTYPE_X86_ALL,
+ CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL))
+ .WillOnce(Return());
+ ReadFile(&file, false, CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL);
+}
+
+TEST_F(ReaderTest, LittleEndian32Bit) {
+ WithConfiguration config(kLittleEndian, 32);
+ const cpu_type_t kCPUType = 0x46b760df;
+ const cpu_subtype_t kCPUSubType = 0x76a0e7f7;
+ MachOFile file;
+ file
+ .D32(0xfeedface) // magic number
+ .D32(kCPUType) // cpu type
+ .D32(kCPUSubType) // cpu subtype
+ .D32(0x149fc717) // file type
+ .D32(0) // no load commands
+ .D32(0) // they occupy no bytes
+ .D32(0x80e71d64) // flags
+ .D32(0); // reserved
+ ReadFile(&file, true, CPU_TYPE_ANY, kCPUSubType);
+ EXPECT_FALSE(reader.bits_64());
+ EXPECT_FALSE(reader.big_endian());
+ EXPECT_EQ(kCPUType, reader.cpu_type());
+ EXPECT_EQ(kCPUSubType, reader.cpu_subtype());
+ EXPECT_EQ(FileType(0x149fc717), reader.file_type());
+ EXPECT_EQ(FileFlags(0x80e71d64), reader.flags());
+}
+
+TEST_F(ReaderTest, LittleEndian64Bit) {
+ WithConfiguration config(kLittleEndian, 64);
+ const cpu_type_t kCPUType = 0x46b760df;
+ const cpu_subtype_t kCPUSubType = 0x76a0e7f7;
+ MachOFile file;
+ file
+ .D32(0xfeedfacf) // magic number
+ .D32(kCPUType) // cpu type
+ .D32(kCPUSubType) // cpu subtype
+ .D32(0x149fc717) // file type
+ .D32(0) // no load commands
+ .D32(0) // they occupy no bytes
+ .D32(0x80e71d64) // flags
+ .D32(0); // reserved
+ ReadFile(&file, true, CPU_TYPE_ANY, kCPUSubType);
+ EXPECT_TRUE(reader.bits_64());
+ EXPECT_FALSE(reader.big_endian());
+ EXPECT_EQ(kCPUType, reader.cpu_type());
+ EXPECT_EQ(kCPUSubType, reader.cpu_subtype());
+ EXPECT_EQ(FileType(0x149fc717), reader.file_type());
+ EXPECT_EQ(FileFlags(0x80e71d64), reader.flags());
+}
+
+TEST_F(ReaderTest, BigEndian32Bit) {
+ WithConfiguration config(kBigEndian, 32);
+ const cpu_type_t kCPUType = 0x46b760df;
+ const cpu_subtype_t kCPUSubType = 0x76a0e7f7;
+ MachOFile file;
+ file
+ .D32(0xfeedface) // magic number
+ .D32(kCPUType) // cpu type
+ .D32(kCPUSubType) // cpu subtype
+ .D32(0x149fc717) // file type
+ .D32(0) // no load commands
+ .D32(0) // they occupy no bytes
+ .D32(0x80e71d64) // flags
+ .D32(0); // reserved
+ ReadFile(&file, true, CPU_TYPE_ANY, kCPUSubType);
+ EXPECT_FALSE(reader.bits_64());
+ EXPECT_TRUE(reader.big_endian());
+ EXPECT_EQ(kCPUType, reader.cpu_type());
+ EXPECT_EQ(kCPUSubType, reader.cpu_subtype());
+ EXPECT_EQ(FileType(0x149fc717), reader.file_type());
+ EXPECT_EQ(FileFlags(0x80e71d64), reader.flags());
+}
+
+TEST_F(ReaderTest, BigEndian64Bit) {
+ WithConfiguration config(kBigEndian, 64);
+ const cpu_type_t kCPUType = 0x46b760df;
+ const cpu_subtype_t kCPUSubType = 0x76a0e7f7;
+ MachOFile file;
+ file
+ .D32(0xfeedfacf) // magic number
+ .D32(kCPUType) // cpu type
+ .D32(kCPUSubType) // cpu subtype
+ .D32(0x149fc717) // file type
+ .D32(0) // no load commands
+ .D32(0) // they occupy no bytes
+ .D32(0x80e71d64) // flags
+ .D32(0); // reserved
+ ReadFile(&file, true, CPU_TYPE_ANY, kCPUSubType);
+ EXPECT_TRUE(reader.bits_64());
+ EXPECT_TRUE(reader.big_endian());
+ EXPECT_EQ(kCPUType, reader.cpu_type());
+ EXPECT_EQ(kCPUSubType, reader.cpu_subtype());
+ EXPECT_EQ(FileType(0x149fc717), reader.file_type());
+ EXPECT_EQ(FileFlags(0x80e71d64), reader.flags());
+}
+
+
+// Load command tests.
+
+class LoadCommand: public ReaderFixture, public Test { };
+
+TEST_F(LoadCommand, RegionTruncated) {
+ WithConfiguration config(kBigEndian, 64);
+ const cpu_type_t kCPUType = 0x46b760df;
+ const cpu_subtype_t kCPUSubType = 0x76a0e7f7;
+ MachOFile file;
+ file
+ .D32(0xfeedfacf) // magic number
+ .D32(kCPUType) // cpu type
+ .D32(kCPUSubType) // cpu subtype
+ .D32(0x149fc717) // file type
+ .D32(1) // one load command
+ .D32(40) // occupying 40 bytes
+ .D32(0x80e71d64) // flags
+ .D32(0) // reserved
+ .Append(20, 0); // load command region, not as long as
+ // Mach-O header promised
+
+ EXPECT_CALL(reporter, LoadCommandRegionTruncated()).WillOnce(Return());
+
+ ReadFile(&file, false, CPU_TYPE_ANY, kCPUSubType);
+}
+
+TEST_F(LoadCommand, None) {
+ WithConfiguration config(kLittleEndian, 32);
+ LoadCommands load_commands;
+ MachOFile file;
+ file.Header(&load_commands);
+
+ ReadFile(&file, true, CPU_TYPE_X86, CPU_SUBTYPE_I386_ALL);
+
+ EXPECT_FALSE(reader.bits_64());
+ EXPECT_FALSE(reader.big_endian());
+ EXPECT_EQ(CPU_TYPE_X86, reader.cpu_type());
+ EXPECT_EQ(CPU_SUBTYPE_I386_ALL, reader.cpu_subtype());
+ EXPECT_EQ(static_cast<uint32_t>(MH_EXECUTE), reader.file_type());
+ EXPECT_EQ(FileFlags(MH_TWOLEVEL |
+ MH_DYLDLINK |
+ MH_NOUNDEFS),
+ FileFlags(reader.flags()));
+
+ EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
+}
+
+TEST_F(LoadCommand, Unknown) {
+ WithConfiguration config(kBigEndian, 32);
+ LoadCommands load_commands;
+ load_commands
+ .CountCommand()
+ .D32(0x33293d4a) // unknown load command
+ .D32(40) // total size in bytes
+ .Append(32, '*'); // dummy data
+ MachOFile file;
+ file.Header(&load_commands);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ EXPECT_FALSE(reader.bits_64());
+ EXPECT_TRUE(reader.big_endian());
+ EXPECT_EQ(CPU_TYPE_X86, reader.cpu_type());
+ EXPECT_EQ(CPU_SUBTYPE_I386_ALL, reader.cpu_subtype());
+ EXPECT_EQ(static_cast<uint32_t>(MH_EXECUTE), reader.file_type());
+ EXPECT_EQ(FileFlags(MH_TWOLEVEL |
+ MH_DYLDLINK |
+ MH_NOUNDEFS),
+ reader.flags());
+
+ ByteBuffer expected;
+ expected.start = file_bytes + load_commands.start().Value();
+ expected.end = expected.start + load_commands.final_size().Value();
+ EXPECT_CALL(load_command_handler, UnknownCommand(0x33293d4a,
+ expected))
+ .WillOnce(Return(true));
+
+ EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
+}
+
+TEST_F(LoadCommand, TypeIncomplete) {
+ WithConfiguration config(kLittleEndian, 32);
+ LoadCommands load_commands;
+ load_commands
+ .CountCommand()
+ .Append(3, 0); // load command type, incomplete
+
+ MachOFile file;
+ file.Header(&load_commands);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ EXPECT_CALL(reporter, LoadCommandsOverrun(1, 0, 0))
+ .WillOnce(Return());
+ EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler));
+}
+
+TEST_F(LoadCommand, LengthIncomplete) {
+ WithConfiguration config(kBigEndian, 64);
+ LoadCommands load_commands;
+ load_commands
+ .CountCommand()
+ .D32(LC_SEGMENT); // load command
+ // no length
+ MachOFile file;
+ file.Header(&load_commands);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ EXPECT_CALL(reporter, LoadCommandsOverrun(1, 0, LC_SEGMENT))
+ .WillOnce(Return());
+ EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler));
+}
+
+TEST_F(LoadCommand, ContentIncomplete) {
+ WithConfiguration config(kLittleEndian, 64);
+ LoadCommands load_commands;
+ load_commands
+ .CountCommand()
+ .D32(LC_SEGMENT) // load command
+ .D32(40) // total size in bytes
+ .Append(28, '*'); // not enough dummy data
+ MachOFile file;
+ file.Header(&load_commands);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ EXPECT_CALL(reporter, LoadCommandsOverrun(1, 0, LC_SEGMENT))
+ .WillOnce(Return());
+ EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler));
+}
+
+TEST_F(LoadCommand, SegmentBE32) {
+ WithConfiguration config(kBigEndian, 32);
+ LoadedSection segment;
+ segment.address() = 0x1891139c;
+ segment.Append(42, '*'); // segment contents
+ SegmentLoadCommand segment_command;
+ segment_command
+ .Header("froon", segment, 0x94d6dd22, 0x8bdbc319, 0x990a16dd);
+ segment_command.vmsize() = 0xcb76584fU;
+ LoadCommands load_commands;
+ load_commands.Place(&segment_command);
+ MachOFile file;
+ file
+ .Header(&load_commands)
+ .Place(&segment);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ Segment actual_segment;
+ EXPECT_CALL(load_command_handler, SegmentCommand(_))
+ .WillOnce(DoAll(SaveArg<0>(&actual_segment),
+ Return(true)));
+ EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
+
+ EXPECT_FALSE(actual_segment.bits_64);
+ EXPECT_EQ("froon", actual_segment.name);
+ EXPECT_EQ(0x1891139cU, actual_segment.vmaddr);
+ EXPECT_EQ(0xcb76584fU, actual_segment.vmsize);
+ EXPECT_EQ(0x94d6dd22U, actual_segment.maxprot);
+ EXPECT_EQ(0x8bdbc319U, actual_segment.initprot);
+ EXPECT_EQ(0x990a16ddU, actual_segment.flags);
+ EXPECT_EQ(0U, actual_segment.nsects);
+ EXPECT_EQ(0U, actual_segment.section_list.Size());
+ EXPECT_EQ(segment.final_size().Value(), actual_segment.contents.Size());
+}
+
+TEST_F(LoadCommand, SegmentLE32) {
+ WithConfiguration config(kLittleEndian, 32);
+ LoadedSection segment;
+ segment.address() = 0x4b877866;
+ segment.Append(42, '*'); // segment contents
+ SegmentLoadCommand segment_command;
+ segment_command
+ .Header("sixteenprecisely", segment,
+ 0x350759ed, 0x6cf5a62e, 0x990a16dd);
+ segment_command.vmsize() = 0xcb76584fU;
+ LoadCommands load_commands;
+ load_commands.Place(&segment_command);
+ MachOFile file;
+ file
+ .Header(&load_commands)
+ .Place(&segment);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ Segment actual_segment;
+ EXPECT_CALL(load_command_handler, SegmentCommand(_))
+ .WillOnce(DoAll(SaveArg<0>(&actual_segment),
+ Return(true)));
+ EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
+
+ EXPECT_FALSE(actual_segment.bits_64);
+ EXPECT_EQ("sixteenprecisely", actual_segment.name);
+ EXPECT_EQ(0x4b877866U, actual_segment.vmaddr);
+ EXPECT_EQ(0xcb76584fU, actual_segment.vmsize);
+ EXPECT_EQ(0x350759edU, actual_segment.maxprot);
+ EXPECT_EQ(0x6cf5a62eU, actual_segment.initprot);
+ EXPECT_EQ(0x990a16ddU, actual_segment.flags);
+ EXPECT_EQ(0U, actual_segment.nsects);
+ EXPECT_EQ(0U, actual_segment.section_list.Size());
+ EXPECT_EQ(segment.final_size().Value(), actual_segment.contents.Size());
+}
+
+TEST_F(LoadCommand, SegmentBE64) {
+ WithConfiguration config(kBigEndian, 64);
+ LoadedSection segment;
+ segment.address() = 0x79f484f77009e511ULL;
+ segment.Append(42, '*'); // segment contents
+ SegmentLoadCommand segment_command;
+ segment_command
+ .Header("froon", segment, 0x42b45da5, 0x8bdbc319, 0xb2335220);
+ segment_command.vmsize() = 0x8d92397ce6248abaULL;
+ LoadCommands load_commands;
+ load_commands.Place(&segment_command);
+ MachOFile file;
+ file
+ .Header(&load_commands)
+ .Place(&segment);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ Segment actual_segment;
+ EXPECT_CALL(load_command_handler, SegmentCommand(_))
+ .WillOnce(DoAll(SaveArg<0>(&actual_segment),
+ Return(true)));
+ EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
+
+ EXPECT_EQ(true, actual_segment.bits_64);
+ EXPECT_EQ("froon", actual_segment.name);
+ EXPECT_EQ(0x79f484f77009e511ULL, actual_segment.vmaddr);
+ EXPECT_EQ(0x8d92397ce6248abaULL, actual_segment.vmsize);
+ EXPECT_EQ(0x42b45da5U, actual_segment.maxprot);
+ EXPECT_EQ(0x8bdbc319U, actual_segment.initprot);
+ EXPECT_EQ(0xb2335220U, actual_segment.flags);
+ EXPECT_EQ(0U, actual_segment.nsects);
+ EXPECT_EQ(0U, actual_segment.section_list.Size());
+ EXPECT_EQ(segment.final_size().Value(), actual_segment.contents.Size());
+}
+
+TEST_F(LoadCommand, SegmentLE64) {
+ WithConfiguration config(kLittleEndian, 64);
+ LoadedSection segment;
+ segment.address() = 0x50c0501dc5922d35ULL;
+ segment.Append(42, '*'); // segment contents
+ SegmentLoadCommand segment_command;
+ segment_command
+ .Header("sixteenprecisely", segment,
+ 0x917c339d, 0xdbc446fa, 0xb650b563);
+ segment_command.vmsize() = 0x84ae73e7c75469bfULL;
+ LoadCommands load_commands;
+ load_commands.Place(&segment_command);
+ MachOFile file;
+ file
+ .Header(&load_commands)
+ .Place(&segment);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ Segment actual_segment;
+ EXPECT_CALL(load_command_handler, SegmentCommand(_))
+ .WillOnce(DoAll(SaveArg<0>(&actual_segment),
+ Return(true)));
+ EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
+
+ EXPECT_EQ(true, actual_segment.bits_64);
+ EXPECT_EQ("sixteenprecisely", actual_segment.name);
+ EXPECT_EQ(0x50c0501dc5922d35ULL, actual_segment.vmaddr);
+ EXPECT_EQ(0x84ae73e7c75469bfULL, actual_segment.vmsize);
+ EXPECT_EQ(0x917c339dU, actual_segment.maxprot);
+ EXPECT_EQ(0xdbc446faU, actual_segment.initprot);
+ EXPECT_EQ(0xb650b563U, actual_segment.flags);
+ EXPECT_EQ(0U, actual_segment.nsects);
+ EXPECT_EQ(0U, actual_segment.section_list.Size());
+ EXPECT_EQ(segment.final_size().Value(), actual_segment.contents.Size());
+}
+
+TEST_F(LoadCommand, SegmentCommandTruncated) {
+ WithConfiguration config(kBigEndian, 32);
+ LoadedSection segment_contents;
+ segment_contents.Append(20, '*'); // lah di dah
+ SizedSection command;
+ command
+ .D32(LC_SEGMENT) // command type
+ .D32(command.final_size()) // command size
+ .AppendCString("too-short", 16) // segment name
+ .D32(0x9c759211) // vmaddr
+ .D32(segment_contents.final_size()) // vmsize
+ .D32(segment_contents.start()) // file offset
+ .D32(segment_contents.final_size()) // file size
+ .D32(0x56f28446) // max protection
+ .D32(0xe7910dcb) // initial protection
+ .D32(0) // no sections
+ .Append(3, 0); // flags (one byte short!)
+ LoadCommands load_commands;
+ load_commands.Place(&command);
+ MachOFile file;
+ file
+ .Header(&load_commands)
+ .Place(&segment_contents);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ EXPECT_CALL(reporter, LoadCommandTooShort(0, LC_SEGMENT))
+ .WillOnce(Return());
+
+ EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler));
+}
+
+TEST_F(LoadCommand, SegmentBadContentOffset) {
+ WithConfiguration config(kLittleEndian, 32);
+ // Instead of letting a Place call set the segment's file offset and size,
+ // set them ourselves, to check that the parser catches invalid offsets
+ // instead of handing us bogus pointers.
+ LoadedSection segment;
+ segment.address() = 0x4db5489c;
+ segment.start() = 0x7e189e76; // beyond end of file
+ segment.final_size() = 0x98b9c3ab;
+ SegmentLoadCommand segment_command;
+ segment_command
+ .Header("notmerelyfifteen", segment, 0xcbab25ee, 0x359a20db, 0x68a3933f);
+ LoadCommands load_commands;
+ load_commands.Place(&segment_command);
+ MachOFile file;
+ file.Header(&load_commands);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ EXPECT_CALL(reporter, MisplacedSegmentData("notmerelyfifteen"))
+ .WillOnce(Return());
+
+ EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler));
+}
+
+TEST_F(LoadCommand, ThreeLoadCommands) {
+ WithConfiguration config(kBigEndian, 32);
+ LoadedSection seg1, seg2, seg3;
+ SegmentLoadCommand cmd1, cmd2, cmd3;
+
+ seg1.Append(128, '@');
+ seg1.address() = 0xa7f61ef6;
+ cmd1.Header("head", seg1, 0x88bf1cc7, 0x889a26a4, 0xe9b80d87);
+ // Include some dummy data at the end of the load command. Since we
+ // didn't claim to have any sections, the reader should ignore this. But
+ // making sure the commands have different lengths ensures that we're
+ // using the right command's length to advance the LoadCommandIterator.
+ cmd1.Append(128, '!');
+
+ seg2.Append(42, '*');
+ seg2.address() = 0xc70fc909;
+ cmd2.Header("thorax", seg2, 0xde7327f4, 0xfdaf771d, 0x65e74b30);
+ // More dummy data at the end of the load command.
+ cmd2.Append(32, '^');
+
+ seg3.Append(42, '%');
+ seg3.address() = 0x46b3ab05;
+ cmd3.Header("abdomen", seg3, 0x7098b70d, 0x8d8d7728, 0x5131419b);
+ // More dummy data at the end of the load command.
+ cmd3.Append(64, '&');
+
+ LoadCommands load_commands;
+ load_commands.Place(&cmd1).Place(&cmd2).Place(&cmd3);
+
+ MachOFile file;
+ file.Header(&load_commands).Place(&seg1).Place(&seg2).Place(&seg3);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ {
+ InSequence s;
+ EXPECT_CALL(load_command_handler,
+ SegmentCommand(Field(&Segment::name, "head")))
+ .WillOnce(Return(true));
+ EXPECT_CALL(load_command_handler,
+ SegmentCommand(Field(&Segment::name, "thorax")))
+ .WillOnce(Return(true));
+ EXPECT_CALL(load_command_handler,
+ SegmentCommand(Field(&Segment::name, "abdomen")))
+ .WillOnce(Return(true));
+ }
+
+ EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
+}
+
+static inline Matcher<const Section &> MatchSection(
+ Matcher<bool> bits_64,
+ Matcher<const string &> section_name,
+ Matcher<const string &> segment_name,
+ Matcher<uint64_t> address,
+ Matcher<uint32_t> alignment,
+ Matcher<uint32_t> flags,
+ Matcher<const ByteBuffer &> contents) {
+ return AllOf(AllOf(Field(&Section::bits_64, bits_64),
+ Field(&Section::section_name, section_name),
+ Field(&Section::segment_name, segment_name),
+ Field(&Section::address, address)),
+ AllOf(Field(&Section::align, alignment),
+ Field(&Section::flags, flags),
+ Field(&Section::contents, contents)));
+}
+
+static inline Matcher<const Section &> MatchSection(
+ Matcher<bool> bits_64,
+ Matcher<const string &> section_name,
+ Matcher<const string &> segment_name,
+ Matcher<uint64_t> address) {
+ return AllOf(Field(&Section::bits_64, bits_64),
+ Field(&Section::section_name, section_name),
+ Field(&Section::segment_name, segment_name),
+ Field(&Section::address, address));
+}
+
+TEST_F(LoadCommand, OneSegmentTwoSections) {
+ WithConfiguration config(kBigEndian, 64);
+
+ // Create some sections with some data.
+ LoadedSection section1, section2;
+ section1.Append("buddha's hand");
+ section2.Append("kumquat");
+
+ // Create a segment to hold them.
+ LoadedSection segment;
+ segment.address() = 0xe1d0eeec;
+ segment.Place(&section2).Place(&section1);
+
+ SegmentLoadCommand segment_command;
+ segment_command
+ .Header("head", segment, 0x92c9568c, 0xa89f2627, 0x4dc7a1e2)
+ .AppendSectionEntry("mandarin", "kishu", 12, 0x8cd4604bU, section1)
+ .AppendSectionEntry("bergamot", "cara cara", 12, 0x98746efaU, section2);
+
+ LoadCommands commands;
+ commands.Place(&segment_command);
+
+ MachOFile file;
+ file.Header(&commands).Place(&segment);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ Segment actual_segment;
+ EXPECT_CALL(load_command_handler, SegmentCommand(_))
+ .WillOnce(DoAll(SaveArg<0>(&actual_segment),
+ Return(true)));
+ EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
+
+ {
+ InSequence s;
+ ByteBuffer contents1;
+ contents1.start = file_bytes + section1.start().Value();
+ contents1.end = contents1.start + section1.final_size().Value();
+ EXPECT_EQ("buddha's hand",
+ string(reinterpret_cast<const char *>(contents1.start),
+ contents1.Size()));
+ EXPECT_CALL(section_handler,
+ HandleSection(MatchSection(true, "mandarin", "kishu",
+ section1.address().Value(), 12,
+ 0x8cd4604bU, contents1)))
+ .WillOnce(Return(true));
+
+ ByteBuffer contents2;
+ contents2.start = file_bytes + section2.start().Value();
+ contents2.end = contents2.start + section2.final_size().Value();
+ EXPECT_EQ("kumquat",
+ string(reinterpret_cast<const char *>(contents2.start),
+ contents2.Size()));
+ EXPECT_CALL(section_handler,
+ HandleSection(MatchSection(true, "bergamot", "cara cara",
+ section2.address().Value(), 12,
+ 0x98746efaU, contents2)))
+ .WillOnce(Return(true));
+ }
+
+ EXPECT_TRUE(reader.WalkSegmentSections(actual_segment, &section_handler));
+}
+
+TEST_F(LoadCommand, MisplacedSectionBefore) {
+ WithConfiguration config(kLittleEndian, 64);
+
+ // The segment.
+ LoadedSection segment;
+ segment.address() = 0x696d83cc;
+ segment.Append(10, '0');
+
+ // The contents of the following sections don't matter, because
+ // we're not really going to Place them in segment; we're just going
+ // to set all their labels by hand to get the (impossible)
+ // configurations we want.
+
+ // A section whose starting offset is before that of its section.
+ LoadedSection before;
+ before.Append(10, '1');
+ before.start() = segment.start() - 1;
+ before.address() = segment.address() - 1;
+ before.final_size() = before.Size();
+
+ SegmentLoadCommand command;
+ command
+ .Header("segment", segment, 0x173baa29, 0x8407275d, 0xed8f7057)
+ .AppendSectionEntry("before", "segment", 0, 0x686c6921, before);
+
+ LoadCommands commands;
+ commands.Place(&command);
+
+ MachOFile file;
+ file.Header(&commands).Place(&segment);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ Segment actual_segment;
+ EXPECT_TRUE(reader.FindSegment("segment", &actual_segment));
+
+ EXPECT_CALL(reporter, MisplacedSectionData("before", "segment"))
+ .WillOnce(Return());
+ EXPECT_FALSE(reader.WalkSegmentSections(actual_segment, &section_handler));
+}
+
+TEST_F(LoadCommand, MisplacedSectionAfter) {
+ WithConfiguration config(kLittleEndian, 64);
+
+ // The segment.
+ LoadedSection segment;
+ segment.address() = 0x696d83cc;
+ segment.Append(10, '0');
+
+ // The contents of the following sections don't matter, because
+ // we're not really going to Place them in segment; we're just going
+ // to set all their labels by hand to get the (impossible)
+ // configurations we want.
+
+ // A section whose starting offset is after the end of its section.
+ LoadedSection after;
+ after.Append(10, '2');
+ after.start() = segment.start() + 11;
+ after.address() = segment.address() + 11;
+ after.final_size() = after.Size();
+
+ SegmentLoadCommand command;
+ command
+ .Header("segment", segment, 0x173baa29, 0x8407275d, 0xed8f7057)
+ .AppendSectionEntry("after", "segment", 0, 0x2ee50124, after);
+
+ LoadCommands commands;
+ commands.Place(&command);
+
+ MachOFile file;
+ file.Header(&commands).Place(&segment);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ Segment actual_segment;
+ EXPECT_TRUE(reader.FindSegment("segment", &actual_segment));
+
+ EXPECT_CALL(reporter, MisplacedSectionData("after", "segment"))
+ .WillOnce(Return());
+ EXPECT_FALSE(reader.WalkSegmentSections(actual_segment, &section_handler));
+}
+
+TEST_F(LoadCommand, MisplacedSectionTooBig) {
+ WithConfiguration config(kLittleEndian, 64);
+
+ // The segment.
+ LoadedSection segment;
+ segment.address() = 0x696d83cc;
+ segment.Append(10, '0');
+
+ // The contents of the following sections don't matter, because
+ // we're not really going to Place them in segment; we're just going
+ // to set all their labels by hand to get the (impossible)
+ // configurations we want.
+
+ // A section that extends beyond the end of its section.
+ LoadedSection too_big;
+ too_big.Append(10, '3');
+ too_big.start() = segment.start() + 1;
+ too_big.address() = segment.address() + 1;
+ too_big.final_size() = too_big.Size();
+
+ SegmentLoadCommand command;
+ command
+ .Header("segment", segment, 0x173baa29, 0x8407275d, 0xed8f7057)
+ .AppendSectionEntry("too big", "segment", 0, 0x8b53ae5c, too_big);
+
+ LoadCommands commands;
+ commands.Place(&command);
+
+ MachOFile file;
+ file.Header(&commands).Place(&segment);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ Segment actual_segment;
+ EXPECT_TRUE(reader.FindSegment("segment", &actual_segment));
+
+ EXPECT_CALL(reporter, MisplacedSectionData("too big", "segment"))
+ .WillOnce(Return());
+ EXPECT_FALSE(reader.WalkSegmentSections(actual_segment, &section_handler));
+}
+
+
+// The segments in a .dSYM bundle's Mach-O file have their file offset
+// and size set to zero, but the sections don't. The reader shouldn't
+// report an error in this case.
+TEST_F(LoadCommand, ZappedSegment) {
+ WithConfiguration config(kBigEndian, 32);
+
+ // The segment.
+ LoadedSection segment;
+ segment.address() = 0x696d83cc;
+ segment.start() = 0;
+ segment.final_size() = 0;
+
+ // The section.
+ LoadedSection section;
+ section.address() = segment.address();
+ section.start() = 0;
+ section.final_size() = 1000; // extends beyond its segment
+
+ SegmentLoadCommand command;
+ command
+ .Header("zapped", segment, 0x0861a5cb, 0x68ccff67, 0x0b66255c)
+ .AppendSectionEntry("twitching", "zapped", 0, 0x93b3bd42, section);
+
+ LoadCommands commands;
+ commands.Place(&command);
+
+ MachOFile file;
+ file.Header(&commands);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ Segment actual_segment;
+ EXPECT_TRUE(reader.FindSegment("zapped", &actual_segment));
+
+ ByteBuffer zapped_extent(NULL, 0);
+ EXPECT_CALL(section_handler,
+ HandleSection(MatchSection(false, "twitching", "zapped",
+ 0x696d83cc, 0, 0x93b3bd42,
+ zapped_extent)))
+ .WillOnce(Return(true));
+
+ EXPECT_TRUE(reader.WalkSegmentSections(actual_segment, &section_handler));
+}
+
+TEST_F(LoadCommand, MapSegmentSections) {
+ WithConfiguration config(kLittleEndian, 32);
+
+ // Create some sections with some data.
+ LoadedSection section1, section2, section3, section4;
+ section1.Append("buddha's hand");
+ section2.start() = 0; // Section 2 is an S_ZEROFILL section.
+ section2.final_size() = 0;
+ section3.Append("shasta gold");
+ section4.Append("satsuma");
+
+ // Create two segments to hold them.
+ LoadedSection segment1, segment2;
+ segment1.address() = 0x13e6c8a9;
+ segment1.Place(&section3).Place(&section1);
+ segment2.set_word_size(64);
+ segment2.address() = 0x04d462e2;
+ segment2.Place(&section4);
+ section2.address() = segment2.address() + segment2.Size();
+
+ SegmentLoadCommand segment_command1, segment_command2;
+ segment_command1
+ .Header("head", segment1, 0x67d955a6, 0x7a61c13e, 0xe3e50c64)
+ .AppendSectionEntry("mandarin", "head", 12, 0x5bb565d7, section1)
+ .AppendSectionEntry("bergamot", "head", 12, 0x8620de73, section3);
+ segment_command2.set_word_size(64);
+ segment_command2
+ .Header("thorax", segment2, 0x7aab2419, 0xe908007f, 0x17961d33)
+ .AppendSectionEntry("sixteenprecisely", "thorax",
+ 12, S_ZEROFILL, section2)
+ .AppendSectionEntry("cara cara", "thorax", 12, 0xb6c5dd8a, section4);
+
+ LoadCommands commands;
+ commands.Place(&segment_command1).Place(&segment_command2);
+
+ MachOFile file;
+ file.Header(&commands).Place(&segment1).Place(&segment2);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ Segment segment;
+ SectionMap section_map;
+
+ EXPECT_FALSE(reader.FindSegment("smoot", &segment));
+
+ ASSERT_TRUE(reader.FindSegment("thorax", &segment));
+ ASSERT_TRUE(reader.MapSegmentSections(segment, &section_map));
+
+ EXPECT_FALSE(section_map.find("sixteenpreciselyandthensome")
+ != section_map.end());
+ EXPECT_FALSE(section_map.find("mandarin") != section_map.end());
+ ASSERT_TRUE(section_map.find("cara cara") != section_map.end());
+ EXPECT_THAT(section_map["cara cara"],
+ MatchSection(true, "cara cara", "thorax", 0x04d462e2));
+ ASSERT_TRUE(section_map.find("sixteenprecisely")
+ != section_map.end());
+ ByteBuffer sixteenprecisely_contents(NULL, 0);
+ EXPECT_THAT(section_map["sixteenprecisely"],
+ MatchSection(true, "sixteenprecisely", "thorax",
+ 0x04d462e2 + 7, 12, S_ZEROFILL,
+ sixteenprecisely_contents));
+
+ ASSERT_TRUE(reader.FindSegment("head", &segment));
+ ASSERT_TRUE(reader.MapSegmentSections(segment, &section_map));
+
+ ASSERT_TRUE(section_map.find("mandarin") != section_map.end());
+ EXPECT_THAT(section_map["mandarin"],
+ MatchSection(false, "mandarin", "head", 0x13e6c8a9 + 11));
+ ASSERT_TRUE(section_map.find("bergamot") != section_map.end());
+ EXPECT_THAT(section_map["bergamot"],
+ MatchSection(false, "bergamot", "head", 0x13e6c8a9));
+}
+
+TEST_F(LoadCommand, FindSegment) {
+ WithConfiguration config(kBigEndian, 32);
+
+ LoadedSection segment1, segment2, segment3;
+ segment1.address() = 0xb8ae5752;
+ segment1.Append("Some contents!");
+ segment2.address() = 0xd6b0ce83;
+ segment2.Append("Different stuff.");
+ segment3.address() = 0x7374fd2a;
+ segment3.Append("Further materials.");
+
+ SegmentLoadCommand cmd1, cmd2, cmd3;
+ cmd1.Header("first", segment1, 0xfadb6932, 0x175bf529, 0x0de790ad);
+ cmd2.Header("second", segment2, 0xeef716e0, 0xe103a9d7, 0x7d38a8ef);
+ cmd3.Header("third", segment3, 0xe172b39e, 0x86012f07, 0x080ac94d);
+
+ LoadCommands commands;
+ commands.Place(&cmd1).Place(&cmd2).Place(&cmd3);
+
+ MachOFile file;
+ file.Header(&commands).Place(&segment1).Place(&segment2).Place(&segment3);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ Segment actual_segment;
+
+ EXPECT_FALSE(reader.FindSegment("murphy", &actual_segment));
+
+ EXPECT_TRUE(reader.FindSegment("second", &actual_segment));
+ EXPECT_EQ(0xd6b0ce83, actual_segment.vmaddr);
+}
+
+
+// Symtab tests.
+
+// A StringAssembler is a class for generating .stabstr sections to present
+// as input to the STABS parser.
+class StringAssembler: public SizedSection {
+ public:
+ // Add the string S to this StringAssembler, and return the string's
+ // offset within this compilation unit's strings.
+ size_t Add(const string &s) {
+ size_t offset = Size();
+ AppendCString(s);
+ return offset;
+ }
+};
+
+// A SymbolAssembler is a class for generating .stab sections to present as
+// test input for the STABS parser.
+class SymbolAssembler: public SizedSection {
+ public:
+ // Create a SymbolAssembler that uses StringAssembler for its strings.
+ explicit SymbolAssembler(StringAssembler *string_assembler)
+ : string_assembler_(string_assembler),
+ entry_count_(0) { }
+
+ // Append a STAB entry to the end of this section with the given
+ // characteristics. NAME is the offset of this entry's name string within
+ // its compilation unit's portion of the .stabstr section; this can be a
+ // value generated by a StringAssembler. Return a reference to this
+ // SymbolAssembler.
+ SymbolAssembler &Symbol(uint8_t type, uint8_t other, Label descriptor,
+ Label value, Label name) {
+ D32(name);
+ D8(type);
+ D8(other);
+ D16(descriptor);
+ Append(endianness(), word_size_ / 8, value);
+ entry_count_++;
+ return *this;
+ }
+
+ // As above, but automatically add NAME to our StringAssembler.
+ SymbolAssembler &Symbol(uint8_t type, uint8_t other, Label descriptor,
+ Label value, const string &name) {
+ return Symbol(type, other, descriptor, value, string_assembler_->Add(name));
+ }
+
+ private:
+ // The strings for our STABS entries.
+ StringAssembler *string_assembler_;
+
+ // The number of entries in this compilation unit so far.
+ size_t entry_count_;
+};
+
+class Symtab: public ReaderFixture, public Test { };
+
+TEST_F(Symtab, Symtab32) {
+ WithConfiguration config(kLittleEndian, 32);
+
+ StringAssembler strings;
+ SymbolAssembler symbols(&strings);
+ symbols
+ .Symbol(0x52, 0x7c, 0x3470, 0x9bb02e7c, "hrududu")
+ .Symbol(0x50, 0x90, 0x7520, 0x1122525d, "Frith");
+
+ SizedSection symtab_command;
+ symtab_command
+ .D32(LC_SYMTAB) // command
+ .D32(symtab_command.final_size()) // size
+ .D32(symbols.start()) // file offset of symbols
+ .D32(2) // symbol count
+ .D32(strings.start()) // file offset of strings
+ .D32(strings.final_size()); // strings size
+
+ LoadCommands load_commands;
+ load_commands.Place(&symtab_command);
+
+ MachOFile file;
+ file.Header(&load_commands).Place(&symbols).Place(&strings);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ ByteBuffer symbols_found, strings_found;
+ EXPECT_CALL(load_command_handler, SymtabCommand(_, _))
+ .WillOnce(DoAll(SaveArg<0>(&symbols_found),
+ SaveArg<1>(&strings_found),
+ Return(true)));
+ EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
+
+ EXPECT_EQ(24U, symbols_found.Size());
+ EXPECT_EQ(14U, strings_found.Size());
+}
+
+TEST_F(Symtab, Symtab64) {
+ WithConfiguration config(kBigEndian, 64);
+
+ StringAssembler strings;
+ SymbolAssembler symbols(&strings);
+ symbols
+ .Symbol(0xa7, 0xaf, 0x03af, 0x42f3072c74335181ULL, "foo")
+ .Symbol(0xb0, 0x9a, 0x2aa7, 0x2e2d349b3d5744a0ULL, "bar");
+
+ SizedSection symtab_command;
+ symtab_command
+ .D32(LC_SYMTAB) // command
+ .D32(symtab_command.final_size()) // size
+ .D32(symbols.start()) // file offset of symbols
+ .D32(2) // symbol count
+ .D32(strings.start()) // file offset of strings
+ .D32(strings.final_size()); // strings size
+
+ LoadCommands load_commands;
+ load_commands.Place(&symtab_command);
+
+ MachOFile file;
+ file.Header(&load_commands).Place(&symbols).Place(&strings);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ ByteBuffer symbols_found, strings_found;
+ EXPECT_CALL(load_command_handler, SymtabCommand(_, _))
+ .WillOnce(DoAll(SaveArg<0>(&symbols_found),
+ SaveArg<1>(&strings_found),
+ Return(true)));
+ EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
+
+ EXPECT_EQ(32U, symbols_found.Size());
+ EXPECT_EQ(8U, strings_found.Size());
+}
+
+TEST_F(Symtab, SymtabMisplacedSymbols) {
+ WithConfiguration config(kBigEndian, 32);
+
+ StringAssembler strings;
+ SymbolAssembler symbols(&strings);
+ symbols
+ .Symbol(0xa7, 0xaf, 0x03af, 0x42f3072c74335181ULL, "foo")
+ .Symbol(0xb0, 0x9a, 0x2aa7, 0x2e2d349b3d5744a0ULL, "bar");
+
+ SizedSection symtab_command;
+ symtab_command
+ .D32(LC_SYMTAB) // command
+ .D32(symtab_command.final_size()) // size
+ .D32(symbols.start()) // file offset of symbols
+ .D32(3) // symbol count (too many)
+ .D32(strings.start()) // file offset of strings
+ .D32(strings.final_size()); // strings size
+
+ LoadCommands load_commands;
+ load_commands.Place(&symtab_command);
+
+ MachOFile file;
+ // Put symbols at end, so the excessive length will be noticed.
+ file.Header(&load_commands).Place(&strings).Place(&symbols);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ EXPECT_CALL(reporter, MisplacedSymbolTable()).Times(1);
+ EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler));
+}
+
+TEST_F(Symtab, SymtabMisplacedStrings) {
+ WithConfiguration config(kLittleEndian, 32);
+
+ StringAssembler strings;
+ SymbolAssembler symbols(&strings);
+ symbols
+ .Symbol(0xa7, 0xaf, 0x03af, 0x42f3072c74335181ULL, "foo")
+ .Symbol(0xb0, 0x9a, 0x2aa7, 0x2e2d349b3d5744a0ULL, "bar");
+
+ SizedSection symtab_command;
+ symtab_command
+ .D32(LC_SYMTAB) // command
+ .D32(symtab_command.final_size()) // size
+ .D32(symbols.start()) // file offset of symbols
+ .D32(2) // symbol count
+ .D32(strings.start()) // file offset of strings
+ .D32(strings.final_size() + 1); // strings size (too long)
+
+ LoadCommands load_commands;
+ load_commands.Place(&symtab_command);
+
+ MachOFile file;
+ // Put strings at end, so the excessive length will be noticed.
+ file.Header(&load_commands).Place(&symbols).Place(&strings);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ EXPECT_CALL(reporter, MisplacedSymbolTable()).Times(1);
+ EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler));
+}
+
diff --git a/3rdParty/Breakpad/src/common/mac/macho_utilities.cc b/3rdParty/Breakpad/src/common/mac/macho_utilities.cc
index 89f9e77..f56fe76 100644
--- a/3rdParty/Breakpad/src/common/mac/macho_utilities.cc
+++ b/3rdParty/Breakpad/src/common/mac/macho_utilities.cc
@@ -34,16 +34,44 @@
#include "common/mac/byteswap.h"
#include "common/mac/macho_utilities.h"
-void breakpad_swap_uuid_command(struct breakpad_uuid_command *uc,
- enum NXByteOrder target_byte_order)
-{
+#include <mach-o/fat.h>
+#include <mach-o/loader.h>
+
+void breakpad_swap_uuid_command(struct breakpad_uuid_command *uc) {
uc->cmd = ByteSwap(uc->cmd);
uc->cmdsize = ByteSwap(uc->cmdsize);
}
-void breakpad_swap_segment_command_64(struct segment_command_64 *sg,
- enum NXByteOrder target_byte_order)
-{
+void breakpad_swap_load_command(struct load_command *lc) {
+ lc->cmd = ByteSwap(lc->cmd);
+ lc->cmdsize = ByteSwap(lc->cmdsize);
+}
+
+void breakpad_swap_dylib_command(struct dylib_command *dc) {
+ dc->cmd = ByteSwap(dc->cmd);
+ dc->cmdsize = ByteSwap(dc->cmdsize);
+
+ dc->dylib.name.offset = ByteSwap(dc->dylib.name.offset);
+ dc->dylib.timestamp = ByteSwap(dc->dylib.timestamp);
+ dc->dylib.current_version = ByteSwap(dc->dylib.current_version);
+ dc->dylib.compatibility_version = ByteSwap(dc->dylib.compatibility_version);
+}
+
+void breakpad_swap_segment_command(struct segment_command *sc) {
+ sc->cmd = ByteSwap(sc->cmd);
+ sc->cmdsize = ByteSwap(sc->cmdsize);
+
+ sc->vmaddr = ByteSwap(sc->vmaddr);
+ sc->vmsize = ByteSwap(sc->vmsize);
+ sc->fileoff = ByteSwap(sc->fileoff);
+ sc->filesize = ByteSwap(sc->filesize);
+ sc->maxprot = ByteSwap(sc->maxprot);
+ sc->initprot = ByteSwap(sc->initprot);
+ sc->nsects = ByteSwap(sc->nsects);
+ sc->flags = ByteSwap(sc->flags);
+}
+
+void breakpad_swap_segment_command_64(struct segment_command_64 *sg) {
sg->cmd = ByteSwap(sg->cmd);
sg->cmdsize = ByteSwap(sg->cmdsize);
@@ -58,9 +86,32 @@ void breakpad_swap_segment_command_64(struct segment_command_64 *sg,
sg->flags = ByteSwap(sg->flags);
}
-void breakpad_swap_mach_header_64(struct mach_header_64 *mh,
- enum NXByteOrder target_byte_order)
-{
+void breakpad_swap_fat_header(struct fat_header *fh) {
+ fh->magic = ByteSwap(fh->magic);
+ fh->nfat_arch = ByteSwap(fh->nfat_arch);
+}
+
+void breakpad_swap_fat_arch(struct fat_arch *fa, uint32_t narchs) {
+ for (uint32_t i = 0; i < narchs; ++i) {
+ fa[i].cputype = ByteSwap(fa[i].cputype);
+ fa[i].cpusubtype = ByteSwap(fa[i].cpusubtype);
+ fa[i].offset = ByteSwap(fa[i].offset);
+ fa[i].size = ByteSwap(fa[i].size);
+ fa[i].align = ByteSwap(fa[i].align);
+ }
+}
+
+void breakpad_swap_mach_header(struct mach_header *mh) {
+ mh->magic = ByteSwap(mh->magic);
+ mh->cputype = ByteSwap(mh->cputype);
+ mh->cpusubtype = ByteSwap(mh->cpusubtype);
+ mh->filetype = ByteSwap(mh->filetype);
+ mh->ncmds = ByteSwap(mh->ncmds);
+ mh->sizeofcmds = ByteSwap(mh->sizeofcmds);
+ mh->flags = ByteSwap(mh->flags);
+}
+
+void breakpad_swap_mach_header_64(struct mach_header_64 *mh) {
mh->magic = ByteSwap(mh->magic);
mh->cputype = ByteSwap(mh->cputype);
mh->cpusubtype = ByteSwap(mh->cpusubtype);
@@ -71,10 +122,24 @@ void breakpad_swap_mach_header_64(struct mach_header_64 *mh,
mh->reserved = ByteSwap(mh->reserved);
}
+void breakpad_swap_section(struct section *s,
+ uint32_t nsects) {
+ for (uint32_t i = 0; i < nsects; i++) {
+ s[i].addr = ByteSwap(s[i].addr);
+ s[i].size = ByteSwap(s[i].size);
+
+ s[i].offset = ByteSwap(s[i].offset);
+ s[i].align = ByteSwap(s[i].align);
+ s[i].reloff = ByteSwap(s[i].reloff);
+ s[i].nreloc = ByteSwap(s[i].nreloc);
+ s[i].flags = ByteSwap(s[i].flags);
+ s[i].reserved1 = ByteSwap(s[i].reserved1);
+ s[i].reserved2 = ByteSwap(s[i].reserved2);
+ }
+}
+
void breakpad_swap_section_64(struct section_64 *s,
- uint32_t nsects,
- enum NXByteOrder target_byte_order)
-{
+ uint32_t nsects) {
for (uint32_t i = 0; i < nsects; i++) {
s[i].addr = ByteSwap(s[i].addr);
s[i].size = ByteSwap(s[i].size);
diff --git a/3rdParty/Breakpad/src/common/mac/macho_utilities.h b/3rdParty/Breakpad/src/common/mac/macho_utilities.h
index a07945f..00563a7 100644
--- a/3rdParty/Breakpad/src/common/mac/macho_utilities.h
+++ b/3rdParty/Breakpad/src/common/mac/macho_utilities.h
@@ -54,14 +54,6 @@
# define LC_UUID 0x1b /* the uuid */
#endif
-#if TARGET_CPU_X86
-# define BREAKPAD_MACHINE_THREAD_STATE i386_THREAD_STATE
-#elif TARGET_CPU_X86_64
-# define BREAKPAD_MACHINE_THREAD_STATE x86_THREAD_STATE64
-#else
-# define BREAKPAD_MACHINE_THREAD_STATE MACHINE_THREAD_STATE
-#endif
-
// The uuid_command struct/swap routines were added during the 10.4 series.
// Their presence isn't guaranteed.
struct breakpad_uuid_command {
@@ -70,23 +62,34 @@ struct breakpad_uuid_command {
uint8_t uuid[16]; /* the 128-bit uuid */
};
-void breakpad_swap_uuid_command(struct breakpad_uuid_command *uc,
- enum NXByteOrder target_byte_order);
+void breakpad_swap_uuid_command(struct breakpad_uuid_command *uc);
+
+void breakpad_swap_load_command(struct load_command *lc);
+
+void breakpad_swap_dylib_command(struct dylib_command *dc);
// Older SDKs defines thread_state_data_t as an int[] instead
// of the natural_t[] it should be.
typedef natural_t breakpad_thread_state_data_t[THREAD_STATE_MAX];
+void breakpad_swap_segment_command(struct segment_command *sc);
+
// The 64-bit swap routines were added during the 10.4 series, their
// presence isn't guaranteed.
-void breakpad_swap_segment_command_64(struct segment_command_64 *sg,
- enum NXByteOrder target_byte_order);
+void breakpad_swap_segment_command_64(struct segment_command_64 *sg);
+
+void breakpad_swap_fat_header(struct fat_header *fh);
+
+void breakpad_swap_fat_arch(struct fat_arch *fa, uint32_t narchs);
+
+void breakpad_swap_mach_header(struct mach_header *mh);
+
+void breakpad_swap_mach_header_64(struct mach_header_64 *mh);
-void breakpad_swap_mach_header_64(struct mach_header_64 *mh,
- enum NXByteOrder target_byte_order);
+void breakpad_swap_section(struct section *s,
+ uint32_t nsects);
void breakpad_swap_section_64(struct section_64 *s,
- uint32_t nsects,
- enum NXByteOrder target_byte_order);
+ uint32_t nsects);
#endif
diff --git a/3rdParty/Breakpad/src/common/mac/macho_walker.cc b/3rdParty/Breakpad/src/common/mac/macho_walker.cc
index 92da7b1..1acd866 100644
--- a/3rdParty/Breakpad/src/common/mac/macho_walker.cc
+++ b/3rdParty/Breakpad/src/common/mac/macho_walker.cc
@@ -33,15 +33,13 @@
//
// Author: Dan Waylonis
-extern "C" { // necessary for Leopard
- #include <assert.h>
- #include <fcntl.h>
- #include <mach-o/arch.h>
- #include <mach-o/loader.h>
- #include <mach-o/swap.h>
- #include <string.h>
- #include <unistd.h>
-}
+#include <assert.h>
+#include <fcntl.h>
+#include <mach-o/arch.h>
+#include <mach-o/fat.h>
+#include <mach-o/loader.h>
+#include <string.h>
+#include <unistd.h>
#include "common/mac/byteswap.h"
#include "common/mac/macho_walker.h"
@@ -51,7 +49,7 @@ namespace MacFileUtilities {
MachoWalker::MachoWalker(const char *path, LoadCommandCallback callback,
void *context)
- : file_(0),
+ : file_(-1),
memory_(NULL),
memory_size_(0),
callback_(callback),
@@ -64,7 +62,7 @@ MachoWalker::MachoWalker(const char *path, LoadCommandCallback callback,
MachoWalker::MachoWalker(void *memory, size_t size,
LoadCommandCallback callback, void *context)
- : file_(0),
+ : file_(-1),
memory_(memory),
memory_size_(size),
callback_(callback),
@@ -79,21 +77,18 @@ MachoWalker::~MachoWalker() {
close(file_);
}
-int MachoWalker::ValidateCPUType(int cpu_type) {
- // If the user didn't specify, use the local architecture.
+bool MachoWalker::WalkHeader(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype) {
+ cpu_type_t valid_cpu_type = cpu_type;
+ cpu_subtype_t valid_cpu_subtype = cpu_subtype;
+ // if |cpu_type| is 0, use the native cpu type.
if (cpu_type == 0) {
const NXArchInfo *arch = NXGetLocalArchInfo();
assert(arch);
- cpu_type = arch->cputype;
+ valid_cpu_type = arch->cputype;
+ valid_cpu_subtype = CPU_SUBTYPE_MULTIPLE;
}
-
- return cpu_type;
-}
-
-bool MachoWalker::WalkHeader(int cpu_type) {
- int valid_cpu_type = ValidateCPUType(cpu_type);
off_t offset;
- if (FindHeader(valid_cpu_type, offset)) {
+ if (FindHeader(valid_cpu_type, valid_cpu_subtype, offset)) {
if (cpu_type & CPU_ARCH_ABI64)
return WalkHeader64AtOffset(offset);
@@ -111,7 +106,7 @@ bool MachoWalker::ReadBytes(void *buffer, size_t size, off_t offset) {
if (offset + size > memory_size_) {
if (static_cast<size_t>(offset) >= memory_size_)
return false;
- size = memory_size_ - offset;
+ size = memory_size_ - static_cast<size_t>(offset);
result = false;
}
memcpy(buffer, static_cast<char *>(memory_) + offset, size);
@@ -131,8 +126,9 @@ bool MachoWalker::CurrentHeader(struct mach_header_64 *header, off_t *offset) {
return false;
}
-bool MachoWalker::FindHeader(int cpu_type, off_t &offset) {
- int valid_cpu_type = ValidateCPUType(cpu_type);
+bool MachoWalker::FindHeader(cpu_type_t cpu_type,
+ cpu_subtype_t cpu_subtype,
+ off_t &offset) {
// Read the magic bytes that's common amongst all mach-o files
uint32_t magic;
if (!ReadBytes(&magic, sizeof(magic), 0))
@@ -153,15 +149,18 @@ bool MachoWalker::FindHeader(int cpu_type, off_t &offset) {
if (!is_fat) {
// If we don't have a fat header, check if the cpu type matches the single
// header
- cpu_type_t header_cpu_type;
- if (!ReadBytes(&header_cpu_type, sizeof(header_cpu_type), offset))
+ struct mach_header header;
+ if (!ReadBytes(&header, sizeof(header), 0))
return false;
if (magic == MH_CIGAM || magic == MH_CIGAM_64)
- header_cpu_type = ByteSwap(header_cpu_type);
+ breakpad_swap_mach_header(&header);
- if (valid_cpu_type != header_cpu_type)
+ if (cpu_type != header.cputype ||
+ (cpu_subtype != CPU_SUBTYPE_MULTIPLE &&
+ cpu_subtype != header.cpusubtype)) {
return false;
+ }
offset = 0;
return true;
@@ -173,7 +172,7 @@ bool MachoWalker::FindHeader(int cpu_type, off_t &offset) {
return false;
if (NXHostByteOrder() != NX_BigEndian)
- swap_fat_header(&fat, NXHostByteOrder());
+ breakpad_swap_fat_header(&fat);
offset += sizeof(fat);
@@ -184,9 +183,11 @@ bool MachoWalker::FindHeader(int cpu_type, off_t &offset) {
return false;
if (NXHostByteOrder() != NX_BigEndian)
- swap_fat_arch(&arch, 1, NXHostByteOrder());
+ breakpad_swap_fat_arch(&arch, 1);
- if (arch.cputype == valid_cpu_type) {
+ if (arch.cputype == cpu_type &&
+ (cpu_subtype == CPU_SUBTYPE_MULTIPLE ||
+ arch.cpusubtype == cpu_subtype)) {
offset = arch.offset;
return true;
}
@@ -205,7 +206,7 @@ bool MachoWalker::WalkHeaderAtOffset(off_t offset) {
bool swap = (header.magic == MH_CIGAM);
if (swap)
- swap_mach_header(&header, NXHostByteOrder());
+ breakpad_swap_mach_header(&header);
// Copy the data into the mach_header_64 structure. Since the 32-bit and
// 64-bit only differ in the last field (reserved), this is safe to do.
@@ -231,7 +232,7 @@ bool MachoWalker::WalkHeader64AtOffset(off_t offset) {
bool swap = (header.magic == MH_CIGAM_64);
if (swap)
- breakpad_swap_mach_header_64(&header, NXHostByteOrder());
+ breakpad_swap_mach_header_64(&header);
current_header_ = &header;
current_header_size_ = sizeof(header);
@@ -252,7 +253,7 @@ bool MachoWalker::WalkHeaderCore(off_t offset, uint32_t number_of_commands,
return false;
if (swap)
- swap_load_command(&cmd, NXHostByteOrder());
+ breakpad_swap_load_command(&cmd);
// Call the user callback
if (callback_ && !callback_(this, &cmd, offset, swap, callback_context_))
diff --git a/3rdParty/Breakpad/src/common/mac/macho_walker.h b/3rdParty/Breakpad/src/common/mac/macho_walker.h
index cee3eb8..dd53581 100644
--- a/3rdParty/Breakpad/src/common/mac/macho_walker.h
+++ b/3rdParty/Breakpad/src/common/mac/macho_walker.h
@@ -34,6 +34,7 @@
#ifndef COMMON_MAC_MACHO_WALKER_H__
#define COMMON_MAC_MACHO_WALKER_H__
+#include <mach/machine.h>
#include <mach-o/loader.h>
#include <sys/types.h>
@@ -56,16 +57,14 @@ class MachoWalker {
void *context);
~MachoWalker();
- // Begin walking the header for |cpu_type|. If |cpu_type| is 0, then the
- // native cpu type is used. Otherwise, accepted values are listed in
- // /usr/include/mach/machine.h (e.g., CPU_TYPE_X86 or CPU_TYPE_POWERPC).
- // Returns false if opening the file failed or if the |cpu_type| is not
- // present in the file.
- bool WalkHeader(int cpu_type);
-
- // Locate (if any) the header offset for |cpu_type| and return in |offset|.
- // Return true if found, false otherwise.
- bool FindHeader(int cpu_type, off_t &offset);
+ // Begin walking the header for |cpu_type| and |cpu_subtype|. If |cpu_type|
+ // is 0, then the native cpu type is used. Otherwise, accepted values are
+ // listed in /usr/include/mach/machine.h (e.g., CPU_TYPE_X86 or
+ // CPU_TYPE_POWERPC). If |cpu_subtype| is CPU_SUBTYPE_MULTIPLE, the match is
+ // only done on |cpu_type|.
+ // Returns false if opening the file failed or if the |cpu_type|/|cpu_subtype|
+ // is not present in the file.
+ bool WalkHeader(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype);
// Read |size| bytes from the opened file at |offset| into |buffer|
bool ReadBytes(void *buffer, size_t size, off_t offset);
@@ -74,8 +73,11 @@ class MachoWalker {
bool CurrentHeader(struct mach_header_64 *header, off_t *offset);
private:
- // Validate the |cpu_type|
- int ValidateCPUType(int cpu_type);
+ // Locate (if any) the header offset for |cpu_type| and return in |offset|.
+ // Return true if found, false otherwise.
+ bool FindHeader(cpu_type_t cpu_type,
+ cpu_subtype_t cpu_subtype,
+ off_t &offset);
// Process an individual header starting at |offset| from the start of the
// file. Return true if successful, false otherwise.
diff --git a/3rdParty/Breakpad/src/common/mac/string_utilities.cc b/3rdParty/Breakpad/src/common/mac/string_utilities.cc
index e1f63a9..07c0f42 100644
--- a/3rdParty/Breakpad/src/common/mac/string_utilities.cc
+++ b/3rdParty/Breakpad/src/common/mac/string_utilities.cc
@@ -27,7 +27,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#include "processor/scoped_ptr.h"
+#include "common/scoped_ptr.h"
#include "common/mac/string_utilities.h"
namespace MacStringUtils {
diff --git a/3rdParty/Breakpad/src/common/mac/super_fat_arch.h b/3rdParty/Breakpad/src/common/mac/super_fat_arch.h
new file mode 100644
index 0000000..501c865
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/mac/super_fat_arch.h
@@ -0,0 +1,88 @@
+// Copyright (c) 2015, 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.
+
+// Original author: Erik Chen <erikchen@chromium.org>
+
+// super_fat_arch.h: A class to handle 64-bit object files. Has conversions to
+// and from struct fat_arch.
+
+#ifndef BREAKPAD_COMMON_MAC_SUPER_FAT_ARCH_H_
+#define BREAKPAD_COMMON_MAC_SUPER_FAT_ARCH_H_
+
+#include <limits>
+#include <mach-o/fat.h>
+#include <stdint.h>
+
+// Similar to struct fat_arch, except size-related parameters support
+// 64-bits.
+class SuperFatArch {
+ public:
+ uint32_t cputype;
+ uint32_t cpusubtype;
+ uint64_t offset;
+ uint64_t size;
+ uint64_t align;
+
+ SuperFatArch() :
+ cputype(0),
+ cpusubtype(0),
+ offset(0),
+ size(0),
+ align(0) {
+ }
+
+ explicit SuperFatArch(const struct fat_arch &arch) :
+ cputype(arch.cputype),
+ cpusubtype(arch.cpusubtype),
+ offset(arch.offset),
+ size(arch.size),
+ align(arch.align) {
+ }
+
+ // Returns false if the conversion cannot be made.
+ // If the conversion succeeds, the result is placed in |output_arch|.
+ bool ConvertToFatArch(struct fat_arch* output_arch) const {
+ if (offset > std::numeric_limits<uint32_t>::max())
+ return false;
+ if (size > std::numeric_limits<uint32_t>::max())
+ return false;
+ if (align > std::numeric_limits<uint32_t>::max())
+ return false;
+ struct fat_arch arch;
+ arch.cputype = cputype;
+ arch.cpusubtype = cpusubtype;
+ arch.offset = offset;
+ arch.size = size;
+ arch.align = align;
+ *output_arch = arch;
+ return true;
+ }
+};
+
+#endif // BREAKPAD_COMMON_MAC_SUPER_FAT_ARCH_H_