diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 1244010f1..4fa434c3d 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -37,6 +37,7 @@ Related to # * Added a changelog entry? * Checked the scope to ensure the commits are only related to the goal above? +* Added any new headers to the "Copy Files" stage of the static iOS target? --> diff --git a/CHANGELOG.md b/CHANGELOG.md index bd6dce68d..f25f3967a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ Changelog ========= +## TBD + +## Bug Fixes + +* Fixed an issue where an app could deadlock during a crash if unfavourable + timing caused DYLD lock contention. + [#580](https://github.com/bugsnag/bugsnag-cocoa/pull/580) + ## 5.23.1 (2020-04-08) ## Bug fixes diff --git a/OSX/Bugsnag.xcodeproj/project.pbxproj b/OSX/Bugsnag.xcodeproj/project.pbxproj index f8694438f..d299a1547 100644 --- a/OSX/Bugsnag.xcodeproj/project.pbxproj +++ b/OSX/Bugsnag.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 0071CB53246074BD00F562B1 /* BSG_KSMachHeaders.m in Sources */ = {isa = PBXBuildFile; fileRef = 0071CB51246074BD00F562B1 /* BSG_KSMachHeaders.m */; }; + 0071CB54246074BD00F562B1 /* BSG_KSMachHeaders.h in Headers */ = {isa = PBXBuildFile; fileRef = 0071CB52246074BD00F562B1 /* BSG_KSMachHeaders.h */; }; 4B406C1822CAD96400464D1D /* BugsnagCollectionsBSGDictMergeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B406C1622CAD96400464D1D /* BugsnagCollectionsBSGDictMergeTest.m */; }; 4B406C1922CAD96400464D1D /* BugsnagCollectionsBSGDictSetSafeObjectTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B406C1722CAD96400464D1D /* BugsnagCollectionsBSGDictSetSafeObjectTest.m */; }; 4B775FD422CBE02F004839C5 /* BugsnagCollectionsBSGDictInsertIfNotNilTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B775FD222CBE02A004839C5 /* BugsnagCollectionsBSGDictInsertIfNotNilTest.m */; }; @@ -183,6 +185,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 0071CB51246074BD00F562B1 /* BSG_KSMachHeaders.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BSG_KSMachHeaders.m; sourceTree = ""; }; + 0071CB52246074BD00F562B1 /* BSG_KSMachHeaders.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BSG_KSMachHeaders.h; sourceTree = ""; }; 4B406C1622CAD96400464D1D /* BugsnagCollectionsBSGDictMergeTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagCollectionsBSGDictMergeTest.m; sourceTree = ""; }; 4B406C1722CAD96400464D1D /* BugsnagCollectionsBSGDictSetSafeObjectTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagCollectionsBSGDictSetSafeObjectTest.m; sourceTree = ""; }; 4B775FD222CBE02A004839C5 /* BugsnagCollectionsBSGDictInsertIfNotNilTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagCollectionsBSGDictInsertIfNotNilTest.m; sourceTree = ""; }; @@ -582,6 +586,8 @@ E79E6B411F4E3850002B35F9 /* Tools */ = { isa = PBXGroup; children = ( + 0071CB52246074BD00F562B1 /* BSG_KSMachHeaders.h */, + 0071CB51246074BD00F562B1 /* BSG_KSMachHeaders.m */, E79E6B421F4E3850002B35F9 /* BSG_KSArchSpecific.h */, E79E6B431F4E3850002B35F9 /* BSG_KSBacktrace.c */, E79E6B441F4E3850002B35F9 /* BSG_KSBacktrace.h */, @@ -720,6 +726,7 @@ E79E6B9C1F4E3850002B35F9 /* BSG_KSSystemInfo.h in Headers */, 8A2C8FD71C6BC2C800846019 /* BugsnagMetaData.h in Headers */, E79E6B021F4E3847002B35F9 /* BugsnagCrashSentry.h in Headers */, + 0071CB54246074BD00F562B1 /* BSG_KSMachHeaders.h in Headers */, E79E6B911F4E3850002B35F9 /* BSG_KSCrashReport.h in Headers */, 8A48EF271EAA805D00B70024 /* BugsnagLogger.h in Headers */, E79E6BCC1F4E3850002B35F9 /* BSG_KSSingleton.h in Headers */, @@ -895,6 +902,7 @@ E79E6BA81F4E3850002B35F9 /* BSG_KSCrashSentry_NSException.m in Sources */, E79E6B901F4E3850002B35F9 /* BSG_KSCrashReport.c in Sources */, 8A2C8FD81C6BC2C800846019 /* BugsnagMetaData.m in Sources */, + 0071CB53246074BD00F562B1 /* BSG_KSMachHeaders.m in Sources */, E79E6BA51F4E3850002B35F9 /* BSG_KSCrashSentry_MachException.c in Sources */, E79E6BB41F4E3850002B35F9 /* BSG_KSDynamicLinker.c in Sources */, E79E6B031F4E3847002B35F9 /* BugsnagCrashSentry.m in Sources */, diff --git a/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.m b/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.m index c703c0c90..12294f1e0 100644 --- a/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.m +++ b/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.m @@ -24,6 +24,7 @@ // THE SOFTWARE. // +#import #import "BSG_KSCrashAdvanced.h" #import "BSG_KSCrashC.h" @@ -31,6 +32,7 @@ #import "BSG_KSJSONCodecObjC.h" #import "BSG_KSSingleton.h" #import "BSG_KSSystemCapabilities.h" +#import "BSG_KSMachHeaders.h" #import "NSError+BSG_SimpleConstructor.h" //#define BSG_KSLogger_LocalLevel TRACE @@ -49,6 +51,10 @@ #define BSG_KSCRASH_DefaultReportFilesDirectory @"KSCrashReports" #endif +#ifndef BSG_INITIAL_MACH_BINARY_IMAGE_ARRAY_SIZE +#define BSG_INITIAL_MACH_BINARY_IMAGE_ARRAY_SIZE 400 +#endif + // ============================================================================ #pragma mark - Constants - // ============================================================================ @@ -219,13 +225,16 @@ - (NSString *)stateFilePath { } - (BOOL)install { + // Maintain a cache of info about dynamically loaded binary images + [self listenForLoadedBinaries]; + _handlingCrashTypes = bsg_kscrash_install( [self.crashReportPath UTF8String], [self.recrashReportPath UTF8String], [self.stateFilePath UTF8String], [self.nextCrashID UTF8String]); if (self.handlingCrashTypes == 0) { return false; } - + #if BSG_KSCRASH_HAS_UIKIT NSNotificationCenter *nCenter = [NSNotificationCenter defaultCenter]; [nCenter addObserver:self @@ -253,6 +262,21 @@ - (BOOL)install { return true; } +/** + * Set up listeners for un/loaded frameworks. Maintaining our own list of framework Mach + * headers means that we avoid potential deadlock situations where we try and suspend + * lock-holding threads prior to loading mach headers as part of our normal event handling + * behaviour. + */ +- (void)listenForLoadedBinaries { + bsg_initialise_mach_binary_headers(BSG_INITIAL_MACH_BINARY_IMAGE_ARRAY_SIZE); + + // Note: Internally, access to DYLD's binary image store is guarded by an OSSpinLock. We therefore don't need to + // add additional guards around our access. + _dyld_register_func_for_remove_image(&bsg_mach_binary_image_removed); + _dyld_register_func_for_add_image(&bsg_mach_binary_image_added); +} + - (void)sendAllReportsWithCompletion: (BSG_KSCrashReportFilterCompletion)onCompletion { [self.crashReportStore pruneFilesLeaving:self.maxStoredReports]; diff --git a/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c b/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c index 46af061dd..616de183b 100644 --- a/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c +++ b/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c @@ -36,6 +36,7 @@ #include "BSG_KSObjC.h" #include "BSG_KSSignalInfo.h" #include "BSG_KSString.h" +#include "BSG_KSMachHeaders.h" //#define BSG_kSLogger_LocalLevel TRACE #include "BSG_KSLogger.h" @@ -1084,70 +1085,23 @@ int bsg_kscrw_i_threadIndex(const thread_t thread) { * * @param key The object key, if needed. * - * @param index Which image to write about. + * @param index Cached info about the binary image. */ void bsg_kscrw_i_writeBinaryImage(const BSG_KSCrashReportWriter *const writer, - const char *const key, const uint32_t index) { - const struct mach_header *header = _dyld_get_image_header(index); - if (header == NULL) { - return; - } - - uintptr_t cmdPtr = bsg_ksdlfirstCmdAfterHeader(header); - if (cmdPtr == 0) { - return; - } - - // Look for the TEXT segment to get the image size. - // Also look for a UUID command. - uint64_t imageSize = 0; - uint64_t imageVmAddr = 0; - uint8_t *uuid = NULL; - - for (uint32_t iCmd = 0; iCmd < header->ncmds; iCmd++) { - struct load_command *loadCmd = (struct load_command *)cmdPtr; - switch (loadCmd->cmd) { - case LC_SEGMENT: { - struct segment_command *segCmd = (struct segment_command *)cmdPtr; - if (strcmp(segCmd->segname, SEG_TEXT) == 0) { - imageSize = segCmd->vmsize; - imageVmAddr = segCmd->vmaddr; - } - break; - } - case LC_SEGMENT_64: { - struct segment_command_64 *segCmd = - (struct segment_command_64 *)cmdPtr; - if (strcmp(segCmd->segname, SEG_TEXT) == 0) { - imageSize = segCmd->vmsize; - imageVmAddr = segCmd->vmaddr; - } - break; - } - case LC_UUID: { - struct uuid_command *uuidCmd = (struct uuid_command *)cmdPtr; - uuid = uuidCmd->uuid; - break; - } - } - cmdPtr += loadCmd->cmdsize; - } - + const char *const key, + const uint32_t index) +{ + BSG_Mach_Binary_Image_Info info = *bsg_dyld_get_image_info(index); + writer->beginObject(writer, key); { - writer->addUIntegerElement(writer, BSG_KSCrashField_ImageAddress, - (uintptr_t)header); - writer->addUIntegerElement(writer, BSG_KSCrashField_ImageVmAddress, - imageVmAddr); - writer->addUIntegerElement(writer, BSG_KSCrashField_ImageSize, - imageSize); - writer->addStringElement(writer, BSG_KSCrashField_Name, - _dyld_get_image_name(index)); - writer->addUUIDElement(writer, BSG_KSCrashField_UUID, uuid); - writer->addIntegerElement(writer, BSG_KSCrashField_CPUType, - header->cputype); - writer->addIntegerElement(writer, BSG_KSCrashField_CPUSubType, - header->cpusubtype); + writer->addUIntegerElement(writer, BSG_KSCrashField_ImageAddress, (uintptr_t)info.header); + writer->addUIntegerElement(writer, BSG_KSCrashField_ImageVmAddress, info.imageVmAddr); + writer->addUIntegerElement(writer, BSG_KSCrashField_ImageSize, info.imageSize); + writer->addStringElement(writer, BSG_KSCrashField_Name, info.name); + writer->addUUIDElement(writer, BSG_KSCrashField_UUID, info.uuid); + writer->addIntegerElement(writer, BSG_KSCrashField_CPUType, info.header->cputype); + writer->addIntegerElement(writer, BSG_KSCrashField_CPUSubType, info.header->cpusubtype); } writer->endContainer(writer); } @@ -1159,12 +1113,14 @@ void bsg_kscrw_i_writeBinaryImage(const BSG_KSCrashReportWriter *const writer, * @param key The object key, if needed. */ void bsg_kscrw_i_writeBinaryImages(const BSG_KSCrashReportWriter *const writer, - const char *const key) { - const uint32_t imageCount = _dyld_image_count(); + const char *const key) +{ + const uint32_t imageCount = bsg_dyld_image_count(); writer->beginArray(writer, key); { for (uint32_t iImg = 0; iImg < imageCount; iImg++) { + // Threads are suspended at this point; no need to synchronise/lock bsg_kscrw_i_writeBinaryImage(writer, NULL, iImg); } } diff --git a/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSDynamicLinker.c b/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSDynamicLinker.c index 7f79499a8..148647937 100644 --- a/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSDynamicLinker.c +++ b/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSDynamicLinker.c @@ -26,6 +26,7 @@ #include "BSG_KSDynamicLinker.h" #include "BSG_KSArchSpecific.h" +#include "BSG_KSMachHeaders.h" #include #include @@ -33,10 +34,10 @@ uint32_t bsg_ksdlimageNamed(const char *const imageName, bool exactMatch) { if (imageName != NULL) { - const uint32_t imageCount = _dyld_image_count(); - + const uint32_t imageCount = bsg_dyld_image_count(); + for (uint32_t iImg = 0; iImg < imageCount; iImg++) { - const char *name = _dyld_get_image_name(iImg); + const char *name = bsg_dyld_get_image_name(iImg); if (name == NULL) { continue; // name is null if the index is out of range per dyld(3) } else if (exactMatch) { @@ -57,7 +58,7 @@ const uint8_t *bsg_ksdlimageUUID(const char *const imageName, bool exactMatch) { if (imageName != NULL) { const uint32_t iImg = bsg_ksdlimageNamed(imageName, exactMatch); if (iImg != UINT32_MAX) { - const struct mach_header *header = _dyld_get_image_header(iImg); + const struct mach_header *header = bsg_dyld_get_image_header(iImg); if (header != NULL) { uintptr_t cmdPtr = bsg_ksdlfirstCmdAfterHeader(header); if (cmdPtr != 0) { @@ -96,15 +97,15 @@ uintptr_t bsg_ksdlfirstCmdAfterHeader(const struct mach_header *const header) { } uint32_t bsg_ksdlimageIndexContainingAddress(const uintptr_t address) { - const uint32_t imageCount = _dyld_image_count(); + const uint32_t imageCount = bsg_dyld_image_count(); const struct mach_header *header = 0; for (uint32_t iImg = 0; iImg < imageCount; iImg++) { - header = _dyld_get_image_header(iImg); + header = bsg_dyld_get_image_header(iImg); if (header != NULL) { // Look for a segment command with this address within its range. uintptr_t addressWSlide = - address - (uintptr_t)_dyld_get_image_vmaddr_slide(iImg); + address - bsg_dyld_get_image_vmaddr_slide(iImg); uintptr_t cmdPtr = bsg_ksdlfirstCmdAfterHeader(header); if (cmdPtr == 0) { continue; @@ -135,7 +136,7 @@ uint32_t bsg_ksdlimageIndexContainingAddress(const uintptr_t address) { } uintptr_t bsg_ksdlsegmentBaseOfImageIndex(const uint32_t idx) { - const struct mach_header *header = _dyld_get_image_header(idx); + const struct mach_header *header = bsg_dyld_get_image_header(idx); if (header == NULL) { return 0; } @@ -176,12 +177,12 @@ bool bsg_ksdldladdr(const uintptr_t address, Dl_info *const info) { if (idx == UINT_MAX) { return false; } - const struct mach_header *header = _dyld_get_image_header(idx); + const struct mach_header *header = bsg_dyld_get_image_header(idx); if (header == NULL) { return false; } const uintptr_t imageVMAddrSlide = - (uintptr_t)_dyld_get_image_vmaddr_slide(idx); + bsg_dyld_get_image_vmaddr_slide(idx); const uintptr_t addressWithSlide = address - imageVMAddrSlide; const uintptr_t segmentBase = bsg_ksdlsegmentBaseOfImageIndex(idx) + imageVMAddrSlide; @@ -189,7 +190,7 @@ bool bsg_ksdldladdr(const uintptr_t address, Dl_info *const info) { return false; } - info->dli_fname = _dyld_get_image_name(idx); + info->dli_fname = bsg_dyld_get_image_name(idx); info->dli_fbase = (void *)header; // Find symbol tables and get whichever symbol is closest to the address. @@ -244,12 +245,12 @@ bool bsg_ksdldladdr(const uintptr_t address, Dl_info *const info) { const void *bsg_ksdlgetSymbolAddrInImage(uint32_t imageIdx, const char *symbolName) { - const struct mach_header *header = _dyld_get_image_header(imageIdx); + const struct mach_header *header = bsg_dyld_get_image_header(imageIdx); if (header == NULL) { return NULL; } const uintptr_t imageVMAddrSlide = - (uintptr_t)_dyld_get_image_vmaddr_slide(imageIdx); + bsg_dyld_get_image_vmaddr_slide(imageIdx); const uintptr_t segmentBase = bsg_ksdlsegmentBaseOfImageIndex(imageIdx) + imageVMAddrSlide; if (segmentBase == 0) { @@ -290,7 +291,7 @@ const void *bsg_ksdlgetSymbolAddrInImage(uint32_t imageIdx, } const void *bsg_ksdlgetSymbolAddrInAnyImage(const char *symbolName) { - const uint32_t imageCount = _dyld_image_count(); + const uint32_t imageCount = bsg_dyld_image_count(); for (uint32_t iImg = 0; iImg < imageCount; iImg++) { const void *symbolAddr = bsg_ksdlgetSymbolAddrInImage(iImg, symbolName); diff --git a/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.h b/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.h new file mode 100644 index 000000000..339d67d17 --- /dev/null +++ b/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.h @@ -0,0 +1,129 @@ +// +// BSG_KSMachHeaders.h +// Bugsnag +// +// Created by Robin Macharg on 04/05/2020. +// Copyright © 2020 Bugsnag. All rights reserved. +// + +#ifndef BSG_KSMachHeaders_h +#define BSG_KSMachHeaders_h + +#import + +/** + * An encapsulation of the Mach header - either 64 or 32 bit, along with some additional information required for + * detailing a crash report's binary images. + */ +typedef struct { + const struct mach_header *header; /* The mach_header - 32 or 64 bit */ + uint64_t imageVmAddr; + uint64_t imageSize; + uint8_t *uuid; + const char* name; + intptr_t slide; +} BSG_Mach_Binary_Image_Info; + +/** + * MARK: - A Dynamic array container + * See: https://stackoverflow.com/a/3536261/2431627 + */ +typedef struct { + uint32_t used; + uint32_t size; + BSG_Mach_Binary_Image_Info *contents; +} BSG_Mach_Binary_Images; + +static BSG_Mach_Binary_Images bsg_mach_binary_images; + +/** + * An OS-version-specific lock, used to synchronise access to the array of binary image info. + * + * os_unfair_lock is available from specific OS versions onwards: + * https://developer.apple.com/documentation/os/os_unfair_lock + * + * It deprecates OSSpinLock: + * https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/spinlock.3.html + * + * The #defined BSG_DYLD_CACHE_LOCK/UNLOCK avoid spurious warnings on later OSs. + */ + +#if defined(__IPHONE_10_0) || defined(__MAC_10_12) || defined(__TVOS_10_0) || defined(__WATCHOS_3_0) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wunguarded-availability" + + #import + static os_unfair_lock bsg_mach_binary_images_access_lock = OS_UNFAIR_LOCK_INIT; + + #ifndef BSG_DYLD_CACHE_LOCK + #define BSG_DYLD_CACHE_LOCK \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wunguarded-availability\"") \ + os_unfair_lock_lock(&bsg_mach_binary_images_access_lock); \ + _Pragma("clang diagnostic pop") + #endif + + #ifndef BSG_DYLD_CACHE_UNLOCK + #define BSG_DYLD_CACHE_UNLOCK \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wunguarded-availability\"") \ + os_unfair_lock_unlock(&bsg_mach_binary_images_access_lock); \ + _Pragma("clang diagnostic pop") + #endif + + #pragma clang diagnostic pop +#else + #import + static OSSpinLock bsg_mach_binary_images_access_lock = OS_SPINLOCK_INIT; + + #ifndef BSG_DYLD_CACHE_LOCK + #define BSG_DYLD_CACHE_LOCK OSSpinLockLock(&bsg_mach_binary_images_access_lock); + #endif + + #ifndef BSG_DYLD_CACHE_UNLOCK + #define BSG_DYLD_CACHE_UNLOCK OSSpinLockUnlock(&bsg_mach_binary_images_access_lock); + #endif +#endif + +// MARK: - Replicate the DYLD API + +/** + * Returns the current number of images mapped in by dyld + */ +uint32_t bsg_dyld_image_count(void); + +/** + * Returns a pointer to the mach header of the image indexed by image_index. If imageIndex + * is out of range, NULL is returned. + */ +const struct mach_header* bsg_dyld_get_image_header(uint32_t imageIndex); + +/** + * Returns the virtural memory address slide amount of the image indexed by imageIndex. + * If image_index is out of range zero is returned. + */ +intptr_t bsg_dyld_get_image_vmaddr_slide(uint32_t imageIndex); + +/** + * Returns the name of the image indexed by imageIndex. + */ +const char* bsg_dyld_get_image_name(uint32_t imageIndex); + +BSG_Mach_Binary_Image_Info *bsg_dyld_get_image_info(uint32_t imageIndex); // An additional convenience function + +/** + * Called when a binary image is loaded. + */ +void bsg_mach_binary_image_added(const struct mach_header *mh, intptr_t slide); + +/** + * Called when a binary image is unloaded. + */ +void bsg_mach_binary_image_removed(const struct mach_header *mh, intptr_t slide); + +/** + * Create an empty array with initial capacity to hold Mach header info. + */ +BSG_Mach_Binary_Images *bsg_initialise_mach_binary_headers(uint32_t initialSize); + +#endif /* BSG_KSMachHeaders_h */ diff --git a/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.m b/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.m new file mode 100644 index 000000000..c9a867544 --- /dev/null +++ b/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.m @@ -0,0 +1,216 @@ +// +// BSG_KSMachHeaders.m +// Bugsnag +// +// Created by Robin Macharg on 04/05/2020. +// Copyright © 2020 Bugsnag. All rights reserved. +// + +#import +#import +#import +#import "BSG_KSDynamicLinker.h" +#import "BSG_KSMachHeaders.h" + +// MARK: - Replicate the DYLD API + +uint32_t bsg_dyld_image_count(void) { + return bsg_mach_binary_images.used; +} + +const struct mach_header* bsg_dyld_get_image_header(uint32_t imageIndex) { + if (imageIndex < bsg_mach_binary_images.used) { + return bsg_mach_binary_images.contents[imageIndex].header; + } + return NULL; +} + +intptr_t bsg_dyld_get_image_vmaddr_slide(uint32_t imageIndex) { + if (imageIndex < bsg_mach_binary_images.used) { + return bsg_mach_binary_images.contents[imageIndex].slide; + } + return 0; +} + +const char* bsg_dyld_get_image_name(uint32_t imageIndex) { + if (imageIndex < bsg_mach_binary_images.used) { + return bsg_mach_binary_images.contents[imageIndex].name; + } + return NULL; +} + +BSG_Mach_Binary_Image_Info *bsg_dyld_get_image_info(uint32_t imageIndex) { + if (imageIndex < bsg_mach_binary_images.used) { + return &bsg_mach_binary_images.contents[imageIndex]; + } + return NULL; +} + +/** + * Store a Mach binary image-excapsulating struct in a dynamic array. + * The array doubles on filling-up. Typical sizes is expected to be in < 1000 (i.e. 2-3 doublings, at app start-up) + * This should be called in a threadsafe way; we lock against a simultaneous add and remove. + */ +void bsg_add_mach_binary_image(BSG_Mach_Binary_Image_Info element) { + + BSG_DYLD_CACHE_LOCK + + // Expand array if necessary. We're slightly paranoid here. An OOM is likely to be indicative of bigger problems + // but we should still do *our* best not to crash the app. + if (bsg_mach_binary_images.used == bsg_mach_binary_images.size) { + uint32_t newSize = bsg_mach_binary_images.size *= 2; + uint32_t newAllocationSize = newSize * sizeof(BSG_Mach_Binary_Image_Info); + errno = 0; + BSG_Mach_Binary_Image_Info *newAllocation = (BSG_Mach_Binary_Image_Info *)realloc(bsg_mach_binary_images.contents, newAllocationSize); + + if (newAllocation != NULL && errno != ENOMEM) { + bsg_mach_binary_images.size = newSize; + bsg_mach_binary_images.contents = newAllocation; + } + else { + // Exit early, don't expand the array, don't store the header info and unlock + BSG_DYLD_CACHE_UNLOCK + return; + } + } + + // Store the value, increment the number of used elements + bsg_mach_binary_images.contents[bsg_mach_binary_images.used++] = element; + + BSG_DYLD_CACHE_UNLOCK +} + +/** + * Binary images can only be loaded at most once. We can use the VMAddress as a key, without needing to compare the + * other fields. Element order is not important; deletion is accomplished by copying the last item into the deleted + * position. + */ +void bsg_remove_mach_binary_image(uint64_t imageVmAddr) { + + BSG_DYLD_CACHE_LOCK + + for (uint32_t i=0; ilast. + if (bsg_mach_binary_images.used >= 2) { + bsg_mach_binary_images.contents[i] = bsg_mach_binary_images.contents[--bsg_mach_binary_images.used]; + } + else { + bsg_mach_binary_images.used = 0; + } + break; // an image can only be loaded singularly; exit loop once found + } + } + + BSG_DYLD_CACHE_UNLOCK +} + +BSG_Mach_Binary_Images *bsg_initialise_mach_binary_headers(uint32_t initialSize) { + bsg_mach_binary_images.contents = (BSG_Mach_Binary_Image_Info *)malloc(initialSize * sizeof(BSG_Mach_Binary_Image_Info)); + bsg_mach_binary_images.used = 0; + bsg_mach_binary_images.size = initialSize; + return &bsg_mach_binary_images; +} + +/** + * Populate a Mach binary image info structure + * + * @param header The Mach binary image header + * + * @param info Encapsulated Binary Image info + * + * @returns a boolean indicating success + */ +bool bsg_populate_mach_image_info(const struct mach_header *header, intptr_t slide, BSG_Mach_Binary_Image_Info *info) { + + // Early exit conditions; this is not a valid/useful binary image + // 1. We can't find a sensible Mach command + uintptr_t cmdPtr = bsg_ksdlfirstCmdAfterHeader(header); + if (cmdPtr == 0) { + return false; + } + + // 2. The image doesn't have a name. Note: running with a debugger attached causes this condition to match. + Dl_info DlInfo = (const Dl_info) { 0 }; + dladdr(header, &DlInfo); + const char *imageName = DlInfo.dli_fname; + if (!imageName) { + return false; + } + + // Look for the TEXT segment to get the image size. + // Also look for a UUID command. + uint64_t imageSize = 0; + uint64_t imageVmAddr = 0; + uint8_t *uuid = NULL; + + for (uint32_t iCmd = 0; iCmd < header->ncmds; iCmd++) { + struct load_command *loadCmd = (struct load_command *)cmdPtr; + switch (loadCmd->cmd) { + case LC_SEGMENT: { + struct segment_command *segCmd = (struct segment_command *)cmdPtr; + if (strcmp(segCmd->segname, SEG_TEXT) == 0) { + imageSize = segCmd->vmsize; + imageVmAddr = segCmd->vmaddr; + } + break; + } + case LC_SEGMENT_64: { + struct segment_command_64 *segCmd = + (struct segment_command_64 *)cmdPtr; + if (strcmp(segCmd->segname, SEG_TEXT) == 0) { + imageSize = segCmd->vmsize; + imageVmAddr = segCmd->vmaddr; + } + break; + } + case LC_UUID: { + struct uuid_command *uuidCmd = (struct uuid_command *)cmdPtr; + uuid = uuidCmd->uuid; + break; + } + } + cmdPtr += loadCmd->cmdsize; + } + + // Save these values + info->header = header; + info->imageSize = imageSize; + info->imageVmAddr = imageVmAddr; + info->uuid = uuid; + info->name = imageName; + info->slide = slide; + + return true; +} + +/** + * A callback invoked when dyld loads binary images. It stores enough relevant info about the + * image to populate a crash report later. + * + * @param header A mach_header structure + * + * @param slide A virtual memory slide amount. The virtual memory slide amount specifies the difference between the + * address at which the image was linked and the address at which the image is loaded. + */ +void bsg_mach_binary_image_added(const struct mach_header *header, intptr_t slide) +{ + BSG_Mach_Binary_Image_Info info = { 0 }; + if (bsg_populate_mach_image_info(header, slide, &info)) { + bsg_add_mach_binary_image(info); + } +} + +/** + * Called when a binary image is unloaded. + */ +void bsg_mach_binary_image_removed(const struct mach_header *header, intptr_t slide) +{ + // Convert header into an info struct + BSG_Mach_Binary_Image_Info info = { 0 }; + if (bsg_populate_mach_image_info(header, slide, &info)) { + bsg_remove_mach_binary_image(info.imageVmAddr); + } +} diff --git a/Tests/KSCrash/KSMachHeader_Tests.m b/Tests/KSCrash/KSMachHeader_Tests.m new file mode 100644 index 000000000..d4d355332 --- /dev/null +++ b/Tests/KSCrash/KSMachHeader_Tests.m @@ -0,0 +1,173 @@ +// +// KSMachHeader_Tests.m +// Tests +// +// Created by Robin Macharg on 04/05/2020. +// Copyright © 2020 Bugsnag. All rights reserved. +// + +#import +#import "BSG_KSMachHeaders.h" +#import + +// Private methods +void bsg_add_mach_binary_image(BSG_Mach_Binary_Image_Info element); +void bsg_remove_mach_binary_image(uint64_t imageVmAddr); + +const struct mach_header mh = { + .magic = 1, + .cputype = 42, + .cpusubtype = 27, + .filetype = 1, + .ncmds = 1, + .sizeofcmds = 1, + .flags = 1 +}; + +const BSG_Mach_Binary_Image_Info info1 = {.header = &mh, .imageVmAddr = 12345, .imageSize = 6789, .uuid = (uint8_t *)123, .name = "header the first", .slide = 123 }; +const BSG_Mach_Binary_Image_Info info2 = {.header = &mh, .imageVmAddr = 23456, .imageSize = 6789, .uuid = (uint8_t *)123, .name = "header the second", .slide = 1234 }; +const BSG_Mach_Binary_Image_Info info3 = {.header = &mh, .imageVmAddr = 34567, .imageSize = 6789, .uuid = (uint8_t *)123, .name = "header the third", .slide = 12345 }; +const BSG_Mach_Binary_Image_Info info4 = {.header = &mh, .imageVmAddr = 45678, .imageSize = 6789, .uuid = (uint8_t *)123, .name = "header the fourth", .slide = 123456 }; + +@interface KSMachHeader_Tests : XCTestCase +@end + +@implementation KSMachHeader_Tests + +- (void)testDynamicArray { + + BSG_Mach_Binary_Images *images = bsg_initialise_mach_binary_headers(2); + XCTAssertEqual(images->size, 2); + XCTAssertEqual(bsg_dyld_image_count(), 0); + + // Add + bsg_add_mach_binary_image(info1); + XCTAssertEqual(images->size, 2); + XCTAssertEqual(bsg_dyld_image_count(), 1); + + bsg_add_mach_binary_image(info2); + XCTAssertEqual(images->size, 2); + XCTAssertEqual(bsg_dyld_image_count(), 2); + + // Expand - double size + bsg_add_mach_binary_image(info3); + XCTAssertEqual(images->size, 4); + XCTAssertEqual(bsg_dyld_image_count(), 3); + + // Delete - third will be copied + bsg_remove_mach_binary_image(12345); + XCTAssertEqual(images->size, 4); + XCTAssertEqual(bsg_dyld_image_count(), 2); + + XCTAssertEqual(strcmp(bsg_dyld_get_image_name(0), "header the third"), 0); + XCTAssertEqual(strcmp(bsg_dyld_get_image_name(1), "header the second"), 0); + XCTAssertEqual(images->size, 4); + + // Nothing happens + bsg_remove_mach_binary_image(12345); + XCTAssertEqual(images->size, 4); + XCTAssertEqual(bsg_dyld_image_count(), 2); + + bsg_remove_mach_binary_image(23456); + XCTAssertEqual(images->size, 4); + XCTAssertEqual(bsg_dyld_image_count(), 1); + XCTAssertEqual(strcmp(bsg_dyld_get_image_name(0), "header the third"), 0); + + bsg_remove_mach_binary_image(34567); + XCTAssertEqual(images->size, 4); + XCTAssertEqual(bsg_dyld_image_count(), 0); + + bsg_remove_mach_binary_image(34567); + XCTAssertEqual(images->size, 4); + XCTAssertEqual(bsg_dyld_image_count(), 0); + + // Readd + bsg_add_mach_binary_image(info1); + XCTAssertEqual(images->size, 4); + XCTAssertEqual(bsg_dyld_image_count(), 1); +} + +- (void)testRemoveLast1 { + BSG_Mach_Binary_Images *images = bsg_initialise_mach_binary_headers(2); + bsg_add_mach_binary_image(info1); + XCTAssertEqual(images->size, 2); + XCTAssertEqual(bsg_dyld_image_count(), 1); + + bsg_remove_mach_binary_image(12345); + XCTAssertEqual(images->size, 2); + XCTAssertEqual(bsg_dyld_image_count(), 0); +} + +- (void)testRemoveLast2 { + BSG_Mach_Binary_Images *images = bsg_initialise_mach_binary_headers(2); + bsg_add_mach_binary_image(info1); + bsg_add_mach_binary_image(info2); + XCTAssertEqual(images->size, 2); + XCTAssertEqual(bsg_dyld_image_count(), 2); + + bsg_remove_mach_binary_image(23456); + XCTAssertEqual(images->size, 2); + XCTAssertEqual(bsg_dyld_image_count(), 1); + + bsg_remove_mach_binary_image(12345); + XCTAssertEqual(images->size, 2); + XCTAssertEqual(bsg_dyld_image_count(), 0); +} + +- (void)testRemoveLast3 { + BSG_Mach_Binary_Images *images = bsg_initialise_mach_binary_headers(2); + + bsg_add_mach_binary_image(info1); + bsg_add_mach_binary_image(info2); + bsg_add_mach_binary_image(info3); + bsg_add_mach_binary_image(info4); + XCTAssertEqual(images->size, 4); + XCTAssertEqual(bsg_dyld_image_count(), 4); + + bsg_remove_mach_binary_image(45678); + XCTAssertEqual(images->size, 4); + XCTAssertEqual(bsg_dyld_image_count(), 3); + + bsg_remove_mach_binary_image(12345); + XCTAssertEqual(images->size, 4); + XCTAssertEqual(bsg_dyld_image_count(), 2); + + bsg_remove_mach_binary_image(12345); + XCTAssertEqual(images->size, 4); + XCTAssertEqual(bsg_dyld_image_count(), 2); + + bsg_remove_mach_binary_image(34567); + XCTAssertEqual(images->size, 4); + XCTAssertEqual(bsg_dyld_image_count(), 1); +} + +// Test out-of-bounds behaviour of the replicated dyld API +- (void)testBSGDYLDAPI { + BSG_Mach_Binary_Images *images = bsg_initialise_mach_binary_headers(2); + + bsg_add_mach_binary_image(info1); + bsg_add_mach_binary_image(info2); + XCTAssertEqual(images->size, 2); + XCTAssertEqual(bsg_dyld_image_count(), 2); + + XCTAssertEqual(bsg_dyld_get_image_vmaddr_slide(0), 123); + XCTAssertEqual(bsg_dyld_get_image_vmaddr_slide(1), 1234); + XCTAssertEqual(bsg_dyld_get_image_vmaddr_slide(2), 0); + XCTAssertEqual(bsg_dyld_get_image_vmaddr_slide(999), 0); + + XCTAssertEqualObjects([NSString stringWithUTF8String:bsg_dyld_get_image_name(0)], @"header the first"); + XCTAssertEqualObjects([NSString stringWithUTF8String:bsg_dyld_get_image_name(1)], @"header the second"); + XCTAssertTrue(bsg_dyld_get_image_name(2) == NULL); + XCTAssertTrue(bsg_dyld_get_image_name(999) == NULL); + + XCTAssertEqual(bsg_dyld_get_image_header(0)->filetype, 1); + XCTAssertEqual(bsg_dyld_get_image_header(1)->filetype, 1); + XCTAssertTrue(bsg_dyld_get_image_header(2) == NULL); + XCTAssertTrue(bsg_dyld_get_image_header(999) == NULL); + + XCTAssertEqualObjects([NSString stringWithUTF8String:bsg_dyld_get_image_info(0)->name], @"header the first"); + XCTAssertEqualObjects([NSString stringWithUTF8String:bsg_dyld_get_image_info(1)->name], @"header the second"); + XCTAssertTrue(bsg_dyld_get_image_info(999) == NULL); +} + +@end diff --git a/iOS/Bugsnag.xcodeproj/project.pbxproj b/iOS/Bugsnag.xcodeproj/project.pbxproj index 014d1dcb6..5da865998 100644 --- a/iOS/Bugsnag.xcodeproj/project.pbxproj +++ b/iOS/Bugsnag.xcodeproj/project.pbxproj @@ -7,6 +7,11 @@ objects = { /* Begin PBXBuildFile section */ + 0003F3C52461B29A00AE0C93 /* BSG_KSMachHeaders.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 0071CB4D2460216200F562B1 /* BSG_KSMachHeaders.h */; }; + 0071CB4B2460213200F562B1 /* BSG_KSMachHeaders.m in Sources */ = {isa = PBXBuildFile; fileRef = 0071CB4A2460213200F562B1 /* BSG_KSMachHeaders.m */; }; + 0071CB4C2460213200F562B1 /* BSG_KSMachHeaders.m in Sources */ = {isa = PBXBuildFile; fileRef = 0071CB4A2460213200F562B1 /* BSG_KSMachHeaders.m */; }; + 0071CB4F246025AF00F562B1 /* KSMachHeader_Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0071CB4E246025AF00F562B1 /* KSMachHeader_Tests.m */; }; + 0071CB502460705D00F562B1 /* BSG_KSMachHeaders.h in Headers */ = {isa = PBXBuildFile; fileRef = 0071CB4D2460216200F562B1 /* BSG_KSMachHeaders.h */; }; 4B47970A22A9AE1F00FF9C2E /* BugsnagCrashReportFromKSCrashReportTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B47970922A9AE1F00FF9C2E /* BugsnagCrashReportFromKSCrashReportTest.m */; }; 4B775FCF22CBDEB4004839C5 /* BugsnagCollectionsBSGDictInsertIfNotNilTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B775FCE22CBDEB4004839C5 /* BugsnagCollectionsBSGDictInsertIfNotNilTest.m */; }; 4BE6C42622CAD61A0056305D /* BugsnagCollectionsBSGDictMergeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BE6C42522CAD61A0056305D /* BugsnagCollectionsBSGDictMergeTest.m */; }; @@ -323,6 +328,7 @@ dstPath = "include/${PRODUCT_NAME}"; dstSubfolderSpec = 16; files = ( + 0003F3C52461B29A00AE0C93 /* BSG_KSMachHeaders.h in CopyFiles */, 8A3C590923965D9400B344AA /* BugsnagPlugin.h in CopyFiles */, E79148251FD828E6003EFEBF /* BugsnagKeys.h in CopyFiles */, E79148261FD828E6003EFEBF /* BugsnagSessionTracker.h in CopyFiles */, @@ -397,6 +403,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 0071CB4A2460213200F562B1 /* BSG_KSMachHeaders.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BSG_KSMachHeaders.m; sourceTree = ""; }; + 0071CB4D2460216200F562B1 /* BSG_KSMachHeaders.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BSG_KSMachHeaders.h; sourceTree = ""; }; + 0071CB4E246025AF00F562B1 /* KSMachHeader_Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KSMachHeader_Tests.m; sourceTree = ""; }; 4B3B193422CA7B0900475354 /* BugsnagCollectionsBSGDictSetSafeObjectTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = BugsnagCollectionsBSGDictSetSafeObjectTest.m; path = ../../Tests/BugsnagCollectionsBSGDictSetSafeObjectTest.m; sourceTree = ""; }; 4B47970922A9AE1F00FF9C2E /* BugsnagCrashReportFromKSCrashReportTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BugsnagCrashReportFromKSCrashReportTest.m; sourceTree = ""; }; 4B775FCE22CBDEB4004839C5 /* BugsnagCollectionsBSGDictInsertIfNotNilTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = BugsnagCollectionsBSGDictInsertIfNotNilTest.m; path = ../../Tests/BugsnagCollectionsBSGDictInsertIfNotNilTest.m; sourceTree = ""; }; @@ -771,6 +780,7 @@ E7B970321FD7031500590C27 /* XCTestCase+KSCrash.h */, E7B970331FD7031500590C27 /* XCTestCase+KSCrash.m */, E7B970301FD702DA00590C27 /* KSLogger_Tests.m */, + 0071CB4E246025AF00F562B1 /* KSMachHeader_Tests.m */, ); name = KSCrash; path = ../../Tests/KSCrash; @@ -883,6 +893,8 @@ E7107C011F4C97F100BB3F98 /* BSG_KSMach_x86_32.c */, E7107C021F4C97F100BB3F98 /* BSG_KSMach_x86_64.c */, E7107C031F4C97F100BB3F98 /* BSG_KSMachApple.h */, + 0071CB4A2460213200F562B1 /* BSG_KSMachHeaders.m */, + 0071CB4D2460216200F562B1 /* BSG_KSMachHeaders.h */, E7107C041F4C97F100BB3F98 /* BSG_KSObjC.c */, E7107C051F4C97F100BB3F98 /* BSG_KSObjC.h */, E7107C061F4C97F100BB3F98 /* BSG_KSObjCApple.h */, @@ -965,6 +977,7 @@ E7107C451F4C97F100BB3F98 /* BSG_KSCrashType.h in Headers */, E7107C431F4C97F100BB3F98 /* BSG_KSCrashState.h in Headers */, E7107C471F4C97F100BB3F98 /* BSG_KSSystemInfo.h in Headers */, + 0071CB502460705D00F562B1 /* BSG_KSMachHeaders.h in Headers */, E72962D61F4BBA8B00CEA15D /* BugsnagCrashSentry.h in Headers */, E7107C3C1F4C97F100BB3F98 /* BSG_KSCrashReport.h in Headers */, 8A381D4B1EAA49A700AF8429 /* BugsnagLogger.h in Headers */, @@ -1170,6 +1183,7 @@ E72BF7801FC86A7A004BE82F /* BugsnagUser.m in Sources */, 8A2C8F541C6BBE3C00846019 /* BugsnagCollections.m in Sources */, E7107C501F4C97F100BB3F98 /* BSG_KSCrashSentry_MachException.c in Sources */, + 0071CB4B2460213200F562B1 /* BSG_KSMachHeaders.m in Sources */, E7107C5F1F4C97F100BB3F98 /* BSG_KSDynamicLinker.c in Sources */, E737DEA31F73AD7400BC7C80 /* BugsnagHandledState.m in Sources */, E72962D71F4BBA8B00CEA15D /* BugsnagCrashSentry.m in Sources */, @@ -1218,6 +1232,7 @@ E784D25A1FD70C25004B01E1 /* KSJSONCodec_Tests.m in Sources */, E70EE0881FD7047800FA745C /* KSSystemInfo_Tests.m in Sources */, 4BE6C42622CAD61A0056305D /* BugsnagCollectionsBSGDictMergeTest.m in Sources */, + 0071CB4F246025AF00F562B1 /* KSMachHeader_Tests.m in Sources */, E70EE0871FD7047800FA745C /* KSSysCtl_Tests.m in Sources */, E78C1EF31FCC615400B976D3 /* BugsnagSessionTrackingPayloadTest.m in Sources */, E78C1EF11FCC2F1700B976D3 /* BugsnagSessionTrackerTest.m in Sources */, @@ -1277,6 +1292,7 @@ E72BF7811FC86A7A004BE82F /* BugsnagUser.m in Sources */, E7397E291F83BC2A0034242A /* Bugsnag.m in Sources */, E7397E2A1F83BC2A0034242A /* BugsnagBreadcrumb.m in Sources */, + 0071CB4C2460213200F562B1 /* BSG_KSMachHeaders.m in Sources */, E7397E2B1F83BC2A0034242A /* BugsnagCollections.m in Sources */, E7397E2C1F83BC2A0034242A /* BugsnagConfiguration.m in Sources */, E7397E2D1F83BC2A0034242A /* BugsnagCrashReport.m in Sources */, diff --git a/tvOS/Bugsnag.xcodeproj/project.pbxproj b/tvOS/Bugsnag.xcodeproj/project.pbxproj index 491b46e3c..eaeda7fee 100644 --- a/tvOS/Bugsnag.xcodeproj/project.pbxproj +++ b/tvOS/Bugsnag.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 0071CB572460755500F562B1 /* BSG_KSMachHeaders.m in Sources */ = {isa = PBXBuildFile; fileRef = 0071CB552460755500F562B1 /* BSG_KSMachHeaders.m */; }; + 0071CB582460755500F562B1 /* BSG_KSMachHeaders.h in Headers */ = {isa = PBXBuildFile; fileRef = 0071CB562460755500F562B1 /* BSG_KSMachHeaders.h */; }; 4B3CD2B622C5625800DBFF33 /* BugsnagKSCrashSysInfoParserTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B3CD2B522C5625800DBFF33 /* BugsnagKSCrashSysInfoParserTest.m */; }; 4B3CD2BB22C5676800DBFF33 /* BSGOutOfMemoryWatchdogTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B3CD2B722C5676700DBFF33 /* BSGOutOfMemoryWatchdogTests.m */; }; 4B3CD2BC22C5676800DBFF33 /* BugsnagCrashReportFromKSCrashReportTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B3CD2B822C5676700DBFF33 /* BugsnagCrashReportFromKSCrashReportTest.m */; }; @@ -15,7 +17,6 @@ 4B406C1422CAD94100464D1D /* BugsnagCollectionsBSGDictSetSafeObjectTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B406C1222CAD94100464D1D /* BugsnagCollectionsBSGDictSetSafeObjectTest.m */; }; 4B406C1522CAD94100464D1D /* BugsnagCollectionsBSGDictMergeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B406C1322CAD94100464D1D /* BugsnagCollectionsBSGDictMergeTest.m */; }; 4B775FD122CBE01F004839C5 /* BugsnagCollectionsBSGDictInsertIfNotNilTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B775FD022CBE01F004839C5 /* BugsnagCollectionsBSGDictInsertIfNotNilTest.m */; }; - 8A12006E221C51550008C9C3 /* BSGFilepathTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8A12006D221C51550008C9C3 /* BSGFilepathTests.m */; }; 8A3C591023968B2000B344AA /* BugsnagPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 8A3C590F23968B2000B344AA /* BugsnagPlugin.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8A48EF291EAA824100B70024 /* BugsnagLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 8A48EF281EAA824100B70024 /* BugsnagLogger.h */; }; 8A530CBC22FDC39300F0C108 /* BSG_KSCrashIdentifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 8A530CBA22FDC39300F0C108 /* BSG_KSCrashIdentifier.m */; }; @@ -189,6 +190,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 0071CB552460755500F562B1 /* BSG_KSMachHeaders.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BSG_KSMachHeaders.m; sourceTree = ""; }; + 0071CB562460755500F562B1 /* BSG_KSMachHeaders.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BSG_KSMachHeaders.h; sourceTree = ""; }; 4B3CD2B522C5625800DBFF33 /* BugsnagKSCrashSysInfoParserTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BugsnagKSCrashSysInfoParserTest.m; path = ../../iOS/BugsnagTests/BugsnagKSCrashSysInfoParserTest.m; sourceTree = ""; }; 4B3CD2B722C5676700DBFF33 /* BSGOutOfMemoryWatchdogTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BSGOutOfMemoryWatchdogTests.m; path = ../../iOS/BugsnagTests/BSGOutOfMemoryWatchdogTests.m; sourceTree = ""; }; 4B3CD2B822C5676700DBFF33 /* BugsnagCrashReportFromKSCrashReportTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BugsnagCrashReportFromKSCrashReportTest.m; path = ../../iOS/BugsnagTests/BugsnagCrashReportFromKSCrashReportTest.m; sourceTree = ""; }; @@ -197,7 +200,6 @@ 4B406C1222CAD94100464D1D /* BugsnagCollectionsBSGDictSetSafeObjectTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BugsnagCollectionsBSGDictSetSafeObjectTest.m; path = ../../Tests/BugsnagCollectionsBSGDictSetSafeObjectTest.m; sourceTree = ""; }; 4B406C1322CAD94100464D1D /* BugsnagCollectionsBSGDictMergeTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BugsnagCollectionsBSGDictMergeTest.m; path = ../../Tests/BugsnagCollectionsBSGDictMergeTest.m; sourceTree = ""; }; 4B775FD022CBE01F004839C5 /* BugsnagCollectionsBSGDictInsertIfNotNilTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BugsnagCollectionsBSGDictInsertIfNotNilTest.m; path = ../../Tests/BugsnagCollectionsBSGDictInsertIfNotNilTest.m; sourceTree = ""; }; - 8A12006D221C51550008C9C3 /* BSGFilepathTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BSGFilepathTests.m; path = ../../Tests/BSGFilepathTests.m; sourceTree = ""; }; 8A3C590F23968B2000B344AA /* BugsnagPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BugsnagPlugin.h; path = ../Source/BugsnagPlugin.h; sourceTree = ""; }; 8A48EF281EAA824100B70024 /* BugsnagLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BugsnagLogger.h; path = ../Source/BugsnagLogger.h; sourceTree = ""; }; 8A530CBA22FDC39300F0C108 /* BSG_KSCrashIdentifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BSG_KSCrashIdentifier.m; sourceTree = ""; }; @@ -598,6 +600,8 @@ E76617361F4E459C0094CECF /* Tools */ = { isa = PBXGroup; children = ( + 0071CB562460755500F562B1 /* BSG_KSMachHeaders.h */, + 0071CB552460755500F562B1 /* BSG_KSMachHeaders.m */, E76617371F4E459C0094CECF /* BSG_KSArchSpecific.h */, E76617381F4E459C0094CECF /* BSG_KSBacktrace.c */, E76617391F4E459C0094CECF /* BSG_KSBacktrace.h */, @@ -736,6 +740,7 @@ E76617911F4E459C0094CECF /* BSG_KSSystemInfo.h in Headers */, 8AD9A4FA1D42EE96004E1CC5 /* BugsnagMetaData.h in Headers */, E76616F71F4E45950094CECF /* BugsnagCrashSentry.h in Headers */, + 0071CB582460755500F562B1 /* BSG_KSMachHeaders.h in Headers */, E76617861F4E459C0094CECF /* BSG_KSCrashReport.h in Headers */, 8A48EF291EAA824100B70024 /* BugsnagLogger.h in Headers */, E76617C11F4E459C0094CECF /* BSG_KSSingleton.h in Headers */, @@ -911,6 +916,7 @@ E76616FA1F4E45950094CECF /* BugsnagErrorReportApiClient.m in Sources */, E76617AD1F4E459C0094CECF /* BSG_KSJSONCodec.c in Sources */, E766179D1F4E459C0094CECF /* BSG_KSCrashSentry_NSException.m in Sources */, + 0071CB572460755500F562B1 /* BSG_KSMachHeaders.m in Sources */, E76617851F4E459C0094CECF /* BSG_KSCrashReport.c in Sources */, 8AD9A5021D42EEB0004E1CC5 /* BugsnagMetaData.m in Sources */, E766179A1F4E459C0094CECF /* BSG_KSCrashSentry_MachException.c in Sources */,