diff options
Diffstat (limited to '3rdParty/Breakpad/src/common/mac')
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 §ion,                 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 §ion, +                                          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 §ion)); +}; + + +// 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 §ion_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(§ion2).Place(§ion1); + +  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, §ion_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, §ion_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, §ion_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, §ion_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, §ion_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(§ion3).Place(§ion1); +  segment2.set_word_size(64); +  segment2.address() = 0x04d462e2; +  segment2.Place(§ion4); +  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, §ion_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, §ion_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_ | 
 Swift
 Swift