diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.m b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.m index 7396238aa..8c5e1cc7b 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.m +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.m @@ -26,14 +26,12 @@ #import "BugsnagPlatformConditional.h" -#import #import #import "BSG_KSCrashAdvanced.h" #import "BSG_KSCrashC.h" #import "BSG_KSJSONCodecObjC.h" #import "BSG_KSSingleton.h" -#import "BSG_KSMachHeaders.h" #import "NSError+BSG_SimpleConstructor.h" //#define BSG_KSLogger_LocalLevel TRACE @@ -234,8 +232,6 @@ - (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], @@ -271,21 +267,6 @@ - (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_check_unfair_lock_support(); - bsg_initialise_mach_binary_headers(BSG_INITIAL_MACH_BINARY_IMAGE_ARRAY_SIZE); - - // Note: Access to DYLD's binary image store is guarded by locks. - _dyld_register_func_for_remove_image(&bsg_mach_binary_image_removed); - _dyld_register_func_for_add_image(&bsg_mach_binary_image_added); -} - - (void)sendAllReports { [self.crashReportStore pruneFilesLeaving:self.maxStoredReports]; diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashC.c b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashC.c index 1981d84c5..07548fd1a 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashC.c +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashC.c @@ -29,6 +29,7 @@ #include "BSG_KSCrashReport.h" #include "BSG_KSCrashSentry_User.h" #include "BSG_KSMach.h" +#include "BSG_KSMachHeaders.h" #include "BSG_KSObjC.h" #include "BSG_KSString.h" #include "BSG_KSSystemInfoC.h" @@ -97,7 +98,11 @@ BSG_KSCrashType bsg_kscrash_install(const char *const crashReportFilePath, BSG_KSLOG_DEBUG("Installing crash reporter."); BSG_KSCrash_Context *context = crashContext(); - + + // Initialize local store of dynamically loaded libraries so that binary + // image information can be extracted for reports + bsg_mach_headers_initialize(); + if (bsg_g_installed) { BSG_KSLOG_DEBUG("Crash reporter already installed."); return context->config.handlingCrashTypes; diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c index 432974d50..ccae26368 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c @@ -1104,23 +1104,21 @@ int bsg_kscrw_i_threadIndex(const thread_t thread) { * * @param key The object key, if needed. * - * @param index Cached info about the binary image. + * @param img 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 BSG_Mach_Header_Info *img) { - BSG_Mach_Binary_Image_Info info = *bsg_dyld_get_image_info(index); - writer->beginObject(writer, key); { - 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->addUIntegerElement(writer, BSG_KSCrashField_ImageAddress, (uintptr_t)img->header); + writer->addUIntegerElement(writer, BSG_KSCrashField_ImageVmAddress, img->imageVmAddr); + writer->addUIntegerElement(writer, BSG_KSCrashField_ImageSize, img->imageSize); + writer->addStringElement(writer, BSG_KSCrashField_Name, img->name); + writer->addUUIDElement(writer, BSG_KSCrashField_UUID, img->uuid); + writer->addIntegerElement(writer, BSG_KSCrashField_CPUType, img->header->cputype); + writer->addIntegerElement(writer, BSG_KSCrashField_CPUSubType, img->header->cpusubtype); } writer->endContainer(writer); } @@ -1134,13 +1132,12 @@ void bsg_kscrw_i_writeBinaryImage(const BSG_KSCrashReportWriter *const writer, void bsg_kscrw_i_writeBinaryImages(const BSG_KSCrashReportWriter *const writer, 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); + for (BSG_Mach_Header_Info *img = bsg_mach_headers_get_images(); img != NULL; img = img->next) { + if (!img->unloaded) { + bsg_kscrw_i_writeBinaryImage(writer, NULL, img); + } } } writer->endContainer(writer); diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m index 07a82910d..109ecac92 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m @@ -29,6 +29,7 @@ #import "BSG_KSSystemInfo.h" #import "BSG_KSSystemInfoC.h" #import "BSG_KSDynamicLinker.h" +#import "BSG_KSMachHeaders.h" #import "BSG_KSJSONCodecObjC.h" #import "BSG_KSMach.h" #import "BSG_KSSysCtl.h" @@ -259,7 +260,7 @@ + (NSString *)currentCPUArch { * @return YES if the device is jailbroken. */ + (BOOL)isJailbroken { - return bsg_ksdlimageNamed("MobileSubstrate", false) != UINT32_MAX; + return bsg_mach_headers_image_named("MobileSubstrate", false) != NULL; } /** Check if the current build is a debug build. diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSDynamicLinker.c b/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSDynamicLinker.c index 148647937..e3bae0284 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSDynamicLinker.c +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSDynamicLinker.c @@ -32,46 +32,19 @@ #include #include -uint32_t bsg_ksdlimageNamed(const char *const imageName, bool exactMatch) { - if (imageName != NULL) { - const uint32_t imageCount = bsg_dyld_image_count(); - - for (uint32_t iImg = 0; iImg < imageCount; 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) { - if (strcmp(name, imageName) == 0) { - return iImg; - } - } else { - if (strstr(name, imageName) != NULL) { - return iImg; - } - } - } - } - return UINT32_MAX; -} - 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 = bsg_dyld_get_image_header(iImg); - if (header != NULL) { - uintptr_t cmdPtr = bsg_ksdlfirstCmdAfterHeader(header); - if (cmdPtr != 0) { - for (uint32_t iCmd = 0; iCmd < header->ncmds; iCmd++) { - const struct load_command *loadCmd = - (struct load_command *)cmdPtr; - if (loadCmd->cmd == LC_UUID) { - struct uuid_command *uuidCmd = - (struct uuid_command *)cmdPtr; - return uuidCmd->uuid; - } - cmdPtr += loadCmd->cmdsize; + BSG_Mach_Header_Info *img = bsg_mach_headers_image_named(imageName, exactMatch); + if (img != NULL) { + uintptr_t cmdPtr = bsg_mach_headers_first_cmd_after_header(img->header); + if (cmdPtr != 0) { + for (uint32_t iCmd = 0; iCmd < img->header->ncmds; iCmd++) { + const struct load_command *loadCmd = (struct load_command *)cmdPtr; + if (loadCmd->cmd == LC_UUID) { + struct uuid_command *uuidCmd = (struct uuid_command *)cmdPtr; + return uuidCmd->uuid; } + cmdPtr += loadCmd->cmdsize; } } } @@ -79,128 +52,34 @@ const uint8_t *bsg_ksdlimageUUID(const char *const imageName, bool exactMatch) { return NULL; } -uintptr_t bsg_ksdlfirstCmdAfterHeader(const struct mach_header *const header) { - if (header == NULL) { - return 0; - } - switch (header->magic) { - case MH_MAGIC: - case MH_CIGAM: - return (uintptr_t)(header + 1); - case MH_MAGIC_64: - case MH_CIGAM_64: - return (uintptr_t)(((struct mach_header_64 *)header) + 1); - default: - // Header is corrupt - return 0; - } -} - -uint32_t bsg_ksdlimageIndexContainingAddress(const uintptr_t address) { - const uint32_t imageCount = bsg_dyld_image_count(); - const struct mach_header *header = 0; - - for (uint32_t iImg = 0; iImg < imageCount; 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 - bsg_dyld_get_image_vmaddr_slide(iImg); - uintptr_t cmdPtr = bsg_ksdlfirstCmdAfterHeader(header); - if (cmdPtr == 0) { - continue; - } - for (uint32_t iCmd = 0; iCmd < header->ncmds; iCmd++) { - const struct load_command *loadCmd = - (struct load_command *)cmdPtr; - if (loadCmd->cmd == LC_SEGMENT) { - const struct segment_command *segCmd = - (struct segment_command *)cmdPtr; - if (addressWSlide >= segCmd->vmaddr && - addressWSlide < segCmd->vmaddr + segCmd->vmsize) { - return iImg; - } - } else if (loadCmd->cmd == LC_SEGMENT_64) { - const struct segment_command_64 *segCmd = - (struct segment_command_64 *)cmdPtr; - if (addressWSlide >= segCmd->vmaddr && - addressWSlide < segCmd->vmaddr + segCmd->vmsize) { - return iImg; - } - } - cmdPtr += loadCmd->cmdsize; - } - } - } - return UINT_MAX; -} - -uintptr_t bsg_ksdlsegmentBaseOfImageIndex(const uint32_t idx) { - const struct mach_header *header = bsg_dyld_get_image_header(idx); - if (header == NULL) { - return 0; - } - - // Look for a segment command and return the file image address. - uintptr_t cmdPtr = bsg_ksdlfirstCmdAfterHeader(header); - if (cmdPtr == 0) { - return 0; - } - for (uint32_t i = 0; i < header->ncmds; i++) { - const struct load_command *loadCmd = (struct load_command *)cmdPtr; - if (loadCmd->cmd == LC_SEGMENT) { - const struct segment_command *segmentCmd = - (struct segment_command *)cmdPtr; - if (strcmp(segmentCmd->segname, SEG_LINKEDIT) == 0) { - return segmentCmd->vmaddr - segmentCmd->fileoff; - } - } else if (loadCmd->cmd == LC_SEGMENT_64) { - const struct segment_command_64 *segmentCmd = - (struct segment_command_64 *)cmdPtr; - if (strcmp(segmentCmd->segname, SEG_LINKEDIT) == 0) { - return (uintptr_t)(segmentCmd->vmaddr - segmentCmd->fileoff); - } - } - cmdPtr += loadCmd->cmdsize; - } - - return 0; -} - bool bsg_ksdldladdr(const uintptr_t address, Dl_info *const info) { info->dli_fname = NULL; info->dli_fbase = NULL; info->dli_sname = NULL; info->dli_saddr = NULL; - const uint32_t idx = bsg_ksdlimageIndexContainingAddress(address); - if (idx == UINT_MAX) { + BSG_Mach_Header_Info *image = bsg_mach_headers_image_at_address(address); + if (image == NULL) { return false; } - const struct mach_header *header = bsg_dyld_get_image_header(idx); - if (header == NULL) { - return false; - } - const uintptr_t imageVMAddrSlide = - bsg_dyld_get_image_vmaddr_slide(idx); - const uintptr_t addressWithSlide = address - imageVMAddrSlide; + const uintptr_t addressWithSlide = address - image->slide; const uintptr_t segmentBase = - bsg_ksdlsegmentBaseOfImageIndex(idx) + imageVMAddrSlide; + bsg_mach_headers_image_at_base_of_image_index(image->header) + image->slide; if (segmentBase == 0) { return false; } - info->dli_fname = bsg_dyld_get_image_name(idx); - info->dli_fbase = (void *)header; + info->dli_fname = image->name; + info->dli_fbase = (void *)image->header; // Find symbol tables and get whichever symbol is closest to the address. const BSG_STRUCT_NLIST *bestMatch = NULL; uintptr_t bestDistance = ULONG_MAX; - uintptr_t cmdPtr = bsg_ksdlfirstCmdAfterHeader(header); + uintptr_t cmdPtr = bsg_mach_headers_first_cmd_after_header(image->header); if (cmdPtr == 0) { return false; } - for (uint32_t iCmd = 0; iCmd < header->ncmds; iCmd++) { + for (uint32_t iCmd = 0; iCmd < image->header->ncmds; iCmd++) { const struct load_command *loadCmd = (struct load_command *)cmdPtr; if (loadCmd->cmd == LC_SYMTAB) { const struct symtab_command *symtabCmd = @@ -223,7 +102,7 @@ bool bsg_ksdldladdr(const uintptr_t address, Dl_info *const info) { } if (bestMatch != NULL) { info->dli_saddr = - (void *)(bestMatch->n_value + imageVMAddrSlide); + (void *)(bestMatch->n_value + image->slide); info->dli_sname = (char *)((intptr_t)stringTable + (intptr_t)bestMatch->n_un.n_strx); if (*info->dli_sname == '_') { @@ -243,61 +122,3 @@ bool bsg_ksdldladdr(const uintptr_t address, Dl_info *const info) { return true; } -const void *bsg_ksdlgetSymbolAddrInImage(uint32_t imageIdx, - const char *symbolName) { - const struct mach_header *header = bsg_dyld_get_image_header(imageIdx); - if (header == NULL) { - return NULL; - } - const uintptr_t imageVMAddrSlide = - bsg_dyld_get_image_vmaddr_slide(imageIdx); - const uintptr_t segmentBase = - bsg_ksdlsegmentBaseOfImageIndex(imageIdx) + imageVMAddrSlide; - if (segmentBase == 0) { - return NULL; - } - uintptr_t cmdPtr = bsg_ksdlfirstCmdAfterHeader(header); - if (cmdPtr == 0) { - return NULL; - } - for (uint32_t iCmd = 0; iCmd < header->ncmds; iCmd++) { - const struct load_command *loadCmd = (struct load_command *)cmdPtr; - if (loadCmd->cmd == LC_SYMTAB) { - const struct symtab_command *symtabCmd = - (struct symtab_command *)cmdPtr; - const BSG_STRUCT_NLIST *symbolTable = - (BSG_STRUCT_NLIST *)(segmentBase + symtabCmd->symoff); - const uintptr_t stringTable = segmentBase + symtabCmd->stroff; - - for (uint32_t iSym = 0; iSym < symtabCmd->nsyms; iSym++) { - // If n_value is 0, the symbol refers to an external object. - if (symbolTable[iSym].n_value != 0) { - const char *sname = - (char *)((intptr_t)stringTable + - (intptr_t)symbolTable[iSym].n_un.n_strx); - if (*sname == '_') { - sname++; - } - if (strcmp(sname, symbolName) == 0) { - return (void *)(symbolTable[iSym].n_value + - imageVMAddrSlide); - } - } - } - } - cmdPtr += loadCmd->cmdsize; - } - return NULL; -} - -const void *bsg_ksdlgetSymbolAddrInAnyImage(const char *symbolName) { - const uint32_t imageCount = bsg_dyld_image_count(); - - for (uint32_t iImg = 0; iImg < imageCount; iImg++) { - const void *symbolAddr = bsg_ksdlgetSymbolAddrInImage(iImg, symbolName); - if (symbolAddr != NULL) { - return symbolAddr; - } - } - return NULL; -} diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSDynamicLinker.h b/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSDynamicLinker.h index b3f8f96d8..adb5f48d2 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSDynamicLinker.h +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSDynamicLinker.h @@ -34,15 +34,7 @@ extern "C" { #include #include -/** Find a loaded binary image with the specified name. - * - * @param imageName The image name to look for. - * - * @param exactMatch If true, look for an exact match instead of a partial one. - * - * @return the index of the matched image, or UINT32_MAX if not found. - */ -uint32_t bsg_ksdlimageNamed(const char *const imageName, bool exactMatch); +#include "BSG_KSMachHeaders.h" /** Get the UUID of a loaded binary image with the specified name. * @@ -55,32 +47,6 @@ uint32_t bsg_ksdlimageNamed(const char *const imageName, bool exactMatch); */ const uint8_t *bsg_ksdlimageUUID(const char *const imageName, bool exactMatch); -/** Get the address of the first command following a header (which will be of - * type struct load_command). - * - * @param header The header to get commands for. - * - * @return The address of the first command, or NULL if none was found (which - * should not happen unless the header or image is corrupt). - */ -uintptr_t bsg_ksdlfirstCmdAfterHeader(const struct mach_header *header); - -/** Get the image index that the specified address is part of. - * - * @param address The address to examine. - * @return The index of the image it is part of, or UINT_MAX if none was found. - */ -uint32_t bsg_ksdlimageIndexContainingAddress(const uintptr_t address); - -/** Get the segment base address of the specified image. - * - * This is required for any symtab command offsets. - * - * @param idx The image index. - * @return The image's base address, or 0 if none was found. - */ -uintptr_t bsg_ksdlsegmentBaseOfImageIndex(const uint32_t idx); - /** async-safe version of dladdr. * * This method searches the dynamic loader for information about any image @@ -97,23 +63,6 @@ uintptr_t bsg_ksdlsegmentBaseOfImageIndex(const uint32_t idx); */ bool bsg_ksdldladdr(const uintptr_t address, Dl_info *const info); -/** Get the address of a symbol in the specified image. - * - * @param imageIdx The index of the image to search. - * @param symbolName The symbol to search for. - * @return The address of the symbol or NULL if not found. - */ -const void *bsg_ksdlgetSymbolAddrInImage(uint32_t imageIdx, - const char *symbolName); - -/** Get the address of a symbol in any image. - * Searches all images starting at index 0. - * - * @param symbolName The symbol to search for. - * @return The address of the symbol or NULL if not found. - */ -const void *bsg_ksdlgetSymbolAddrInAnyImage(const char *symbolName); - #ifdef __cplusplus } #endif diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.h b/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.h index 99cf90f7f..94def6cfd 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.h +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.h @@ -13,97 +13,85 @@ #import #import +/* 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. + */ + /** - * 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. + * An encapsulation of the Mach header data as a linked list - either 64 or 32 bit, along with some additiona + * information required for detailing a crash report's binary images. + * + * The removed field indicates that the library has since been unloaded by dyld and can be ignored. */ -typedef struct { +typedef struct bsg_mach_image { 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; - + bool unloaded; + struct bsg_mach_image *next; +} BSG_Mach_Header_Info; -// MARK: - Locking +// MARK: - Operations /** - * An OS-version-specific lock, used to synchronise access to the array of binary image info. A combination of - * compile-time determination of the OS and and run-time determination of the OS version is used to ensure that - * the correct lock mechanism is used. - * - * 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 imported headers have specific version info: and + * Resets mach header data */ - -void bsg_spin_lock(void); -void bsg_spin_unlock(void); -void bsg_unfair_lock(void); -void bsg_unfair_unlock(void); -void bsg_dyld_cache_lock(void); -void bsg_dyld_cache_unlock(void); - -// MARK: - Replicate the DYLD API +void bsg_mach_headers_initialize(void); /** - * Returns the current number of images mapped in by dyld + * Returns the head of the link list of headers */ -uint32_t bsg_dyld_image_count(void); +BSG_Mach_Header_Info *bsg_mach_headers_get_images(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. + * Called when a binary image is loaded. */ -intptr_t bsg_dyld_get_image_vmaddr_slide(uint32_t imageIndex); +void bsg_mach_headers_add_image(const struct mach_header *mh, intptr_t slide); /** - * Returns the name of the image indexed by imageIndex. + * Called when a binary image is unloaded. */ -const char* bsg_dyld_get_image_name(uint32_t imageIndex); +void bsg_mach_headers_remove_image(const struct mach_header *mh, intptr_t slide); -BSG_Mach_Binary_Image_Info *bsg_dyld_get_image_info(uint32_t imageIndex); // An additional convenience function +/** Get the image index that the specified address is part of. +* +* @param address The address to examine. +* @return The index of the image it is part of, or UINT_MAX if none was found. +*/ +BSG_Mach_Header_Info *bsg_mach_headers_image_at_address(const uintptr_t address); -/** - * 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. +/** Find a loaded binary image with the specified name. + * + * @param imageName The image name to look for. + * + * @param exactMatch If true, look for an exact match instead of a partial one. + * + * @return the matched image, or NULL if not found. */ -void bsg_mach_binary_image_removed(const struct mach_header *mh, intptr_t slide); +BSG_Mach_Header_Info *bsg_mach_headers_image_named(const char *const imageName, bool exactMatch); -/** - * Create an empty array with initial capacity to hold Mach header info. +/** Get the address of the first command following a header (which will be of + * type struct load_command). + * + * @param header The header to get commands for. + * + * @return The address of the first command, or NULL if none was found (which + * should not happen unless the header or image is corrupt). */ -BSG_Mach_Binary_Images *bsg_initialise_mach_binary_headers(uint32_t initialSize); +uintptr_t bsg_mach_headers_first_cmd_after_header(const struct mach_header *header); -/** - * Determines whether the OS supports unfair locks or not. +/** Get the segment base address of the specified image. + * + * This is required for any symtab command offsets. + * + * @param header The header to get commands for. + * @return The image's base address, or 0 if none was found. */ -void bsg_check_unfair_lock_support(void); +uintptr_t bsg_mach_headers_image_at_base_of_image_index(const struct mach_header *header); #endif /* BSG_KSMachHeaders_h */ diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.m b/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.m index b3e7f4e1b..7c091c07e 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.m +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.m @@ -15,7 +15,16 @@ // MARK: - Locking -static BSG_Mach_Binary_Images bsg_mach_binary_images; +static const NSOperatingSystemVersion minSdkForUnfairLock = + #if BSG_PLATFORM_IOS + {10,0,0}; + #elif BSG_PLATFORM_OSX + {10,12,0}; + #elif BSG_PLATFORM_TVOS + {10,0,0}; + #elif BSG_PLATFORM_WATCHOS + {3,0,0} + #endif // Pragma's hide unavoidable (and expected) deprecation/unavailable warnings _Pragma("clang diagnostic push") @@ -28,8 +37,6 @@ static OSSpinLock bsg_mach_binary_images_access_lock_spin = OS_SPINLOCK_INIT; _Pragma("clang diagnostic pop") -static BOOL bsg_unfair_lock_supported; - // Lock helpers. These use bulky Pragmas to hide warnings so are in their own functions for clarity. void bsg_spin_lock() { @@ -60,150 +67,48 @@ void bsg_unfair_unlock() { _Pragma("clang diagnostic pop") } -// Lock and unlock sections of code - -void bsg_dyld_cache_lock() { - if (bsg_unfair_lock_supported) { +void bsg_mach_headers_cache_lock() { + if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:minSdkForUnfairLock]) { bsg_unfair_lock(); } else { bsg_spin_lock(); } } -void bsg_dyld_cache_unlock() { - if (bsg_unfair_lock_supported) { +void bsg_mach_headers_cache_unlock() { + if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:minSdkForUnfairLock]) { bsg_unfair_unlock(); } else { bsg_spin_unlock(); } } -BOOL IsUnfairLockSupported(NSProcessInfo *processInfo) { - NSOperatingSystemVersion minSdk = {0,0,0}; -#if BSG_PLATFORM_IOS - minSdk.majorVersion = 10; -#elif BSG_PLATFORM_OSX - minSdk.majorVersion = 10; - minSdk.minorVersion = 12; -#elif BSG_PLATFORM_TVOS - minSdk.majorVersion = 10; -#elif BSG_PLATFORM_WATCHOS - minSdk.majorVersion = 3; -#endif - return [processInfo isOperatingSystemAtLeastVersion:minSdk]; -} - -void bsg_check_unfair_lock_support() { - bsg_unfair_lock_supported = IsUnfairLockSupported([NSProcessInfo processInfo]); -} - -// MARK: - Replicate the DYLD API +// MARK: - Mach Header Linked List -uint32_t bsg_dyld_image_count(void) { - return bsg_mach_binary_images.used; -} +static BSG_Mach_Header_Info *bsg_g_mach_headers_images_head; +static BSG_Mach_Header_Info *bsg_g_mach_headers_images_tail; -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; +BSG_Mach_Header_Info *bsg_mach_headers_get_images() { + return bsg_g_mach_headers_images_head; } -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) { +void bsg_mach_headers_initialize() { - 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; - } + // Clear any existing headers to reset the head/tail pointers + for (BSG_Mach_Header_Info *img = bsg_g_mach_headers_images_head; img != NULL; ) { + BSG_Mach_Header_Info *imgToDelete = img; + img = img->next; + free(imgToDelete); } - // 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_g_mach_headers_images_head = NULL; + bsg_g_mach_headers_images_tail = NULL; - bsg_dyld_cache_unlock(); -} - -/** - * Create an empty array with initial capacity to hold Mach header info. - * - * @param initialSize The initial array capacity - * - * @returns A struct for holding Mach binary image info -*/ -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; + // Register for binary images being loaded and unloaded. dyld calls the add function once + // for each library that has already been loaded and then keeps this cache up-to-date + // with future changes + _dyld_register_func_for_add_image(&bsg_mach_headers_add_image); + _dyld_register_func_for_remove_image(&bsg_mach_headers_remove_image); } /** @@ -215,11 +120,11 @@ void bsg_remove_mach_binary_image(uint64_t imageVmAddr) { * * @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) { +bool bsg_mach_headers_populate_info(const struct mach_header *header, intptr_t slide, BSG_Mach_Header_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); + uintptr_t cmdPtr = bsg_mach_headers_first_cmd_after_header(header); if (cmdPtr == 0) { return false; } @@ -274,35 +179,149 @@ bool bsg_populate_mach_image_info(const struct mach_header *header, intptr_t sli info->uuid = uuid; info->name = imageName; info->slide = slide; + info->unloaded = FALSE; + info->next = NULL; 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); +void bsg_mach_headers_add_image(const struct mach_header *header, intptr_t slide) { + + BSG_Mach_Header_Info *newImage = malloc(sizeof(BSG_Mach_Header_Info)); + if (newImage != NULL && errno != ENOMEM) { + if (bsg_mach_headers_populate_info(header, slide, newImage)) { + + bsg_mach_headers_cache_lock(); + + if (bsg_g_mach_headers_images_head == NULL) { + bsg_g_mach_headers_images_head = newImage; + } else { + bsg_g_mach_headers_images_tail->next = newImage; + } + bsg_g_mach_headers_images_tail = newImage; + + bsg_mach_headers_cache_unlock(); + } } + } /** - * Called when a binary image is unloaded. + * To avoid a destructive operation that could lead thread safety problems, we maintain the + * image record, but mark it as 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); +void bsg_mach_headers_remove_image(const struct mach_header *header, intptr_t slide) { + BSG_Mach_Header_Info existingImage = { 0 }; + if (bsg_mach_headers_populate_info(header, slide, &existingImage)) { + for (BSG_Mach_Header_Info *img = bsg_g_mach_headers_images_head; img != NULL; img = img->next) { + if (img->imageVmAddr == existingImage.imageVmAddr) { + img->unloaded = true; + } + } + } +} + +BSG_Mach_Header_Info *bsg_mach_headers_image_named(const char *const imageName, bool exactMatch) { + + if (imageName != NULL) { + + for (BSG_Mach_Header_Info *img = bsg_g_mach_headers_images_head; img != NULL; img = img->next) { + if (img->name == NULL) { + continue; // name is null if the index is out of range per dyld(3) + } else if (img->unloaded == true) { + continue; // ignore unloaded libraries + } else if (exactMatch) { + if (strcmp(img->name, imageName) == 0) { + return img; + } + } else { + if (strstr(img->name, imageName) != NULL) { + return img; + } + } + } + } + + return NULL; +} + +BSG_Mach_Header_Info *bsg_mach_headers_image_at_address(const uintptr_t address) { + + for (BSG_Mach_Header_Info *img = bsg_g_mach_headers_images_head; img != NULL; img = img->next) { + if (img->unloaded == true) { + continue; + } + // Look for a segment command with this address within its range. + uintptr_t cmdPtr = bsg_mach_headers_first_cmd_after_header(img->header); + if (cmdPtr == 0) { + continue; + } + uintptr_t addressWSlide = address - img->slide; + for (uint32_t iCmd = 0; iCmd < img->header->ncmds; iCmd++) { + const struct load_command *loadCmd = + (struct load_command *)cmdPtr; + if (loadCmd->cmd == LC_SEGMENT) { + const struct segment_command *segCmd = + (struct segment_command *)cmdPtr; + if (addressWSlide >= segCmd->vmaddr && + addressWSlide < segCmd->vmaddr + segCmd->vmsize) { + return img; + } + } else if (loadCmd->cmd == LC_SEGMENT_64) { + const struct segment_command_64 *segCmd = + (struct segment_command_64 *)cmdPtr; + if (addressWSlide >= segCmd->vmaddr && + addressWSlide < segCmd->vmaddr + segCmd->vmsize) { + return img; + } + } + cmdPtr += loadCmd->cmdsize; + } + } + + return NULL; +} + +uintptr_t bsg_mach_headers_first_cmd_after_header(const struct mach_header *const header) { + if (header == NULL) { + return 0; + } + switch (header->magic) { + case MH_MAGIC: + case MH_CIGAM: + return (uintptr_t)(header + 1); + case MH_MAGIC_64: + case MH_CIGAM_64: + return (uintptr_t)(((struct mach_header_64 *)header) + 1); + default: + // Header is corrupt + return 0; + } +} + +uintptr_t bsg_mach_headers_image_at_base_of_image_index(const struct mach_header *const header) { + // Look for a segment command and return the file image address. + uintptr_t cmdPtr = bsg_mach_headers_first_cmd_after_header(header); + if (cmdPtr == 0) { + return 0; } + for (uint32_t i = 0; i < header->ncmds; i++) { + const struct load_command *loadCmd = (struct load_command *)cmdPtr; + if (loadCmd->cmd == LC_SEGMENT) { + const struct segment_command *segmentCmd = + (struct segment_command *)cmdPtr; + if (strcmp(segmentCmd->segname, SEG_LINKEDIT) == 0) { + return segmentCmd->vmaddr - segmentCmd->fileoff; + } + } else if (loadCmd->cmd == LC_SEGMENT_64) { + const struct segment_command_64 *segmentCmd = + (struct segment_command_64 *)cmdPtr; + if (strcmp(segmentCmd->segname, SEG_LINKEDIT) == 0) { + return (uintptr_t)(segmentCmd->vmaddr - segmentCmd->fileoff); + } + } + cmdPtr += loadCmd->cmdsize; + } + + return 0; } diff --git a/Tests/KSCrash/KSDynamicLinker_Tests.m b/Tests/KSCrash/KSDynamicLinker_Tests.m index 9f8864478..c68bbc877 100755 --- a/Tests/KSCrash/KSDynamicLinker_Tests.m +++ b/Tests/KSCrash/KSDynamicLinker_Tests.m @@ -28,6 +28,7 @@ #import #import "BSG_KSDynamicLinker.h" +#import "BSG_KSMachHeaders.h" @interface KSDynamicLinker_Tests : XCTestCase @end @@ -35,6 +36,9 @@ @implementation KSDynamicLinker_Tests - (void) testImageUUID { + bsg_mach_headers_initialize(); + _dyld_register_func_for_add_image(&bsg_mach_headers_add_image); + // Just abritrarily grab the name of the 4th image... const char* name = _dyld_get_image_name(4); const uint8_t* uuidBytes = bsg_ksdlimageUUID(name, true); @@ -43,27 +47,30 @@ - (void) testImageUUID - (void) testImageUUIDInvalidName { + bsg_mach_headers_initialize(); + _dyld_register_func_for_add_image(&bsg_mach_headers_add_image); + const uint8_t* uuidBytes = bsg_ksdlimageUUID("sdfgserghwerghwrh", true); XCTAssertTrue(uuidBytes == NULL, @""); } - (void) testImageUUIDNULLName { + bsg_mach_headers_initialize(); + _dyld_register_func_for_add_image(&bsg_mach_headers_add_image); + const uint8_t* uuidBytes = bsg_ksdlimageUUID(NULL, true); XCTAssertTrue(uuidBytes == NULL, @""); } - (void) testImageUUIDPartialMatch { + bsg_mach_headers_initialize(); + _dyld_register_func_for_add_image(&bsg_mach_headers_add_image); + const uint8_t* uuidBytes = bsg_ksdlimageUUID("libSystem", false); XCTAssertTrue(uuidBytes != NULL, @""); } -- (void) testGetImageNameNULL -{ - uint32_t imageIdx = bsg_ksdlimageNamed(NULL, false); - XCTAssertEqual(imageIdx, UINT32_MAX, @""); -} - @end diff --git a/Tests/KSCrash/KSMachHeader_Tests.m b/Tests/KSCrash/KSMachHeader_Tests.m index d4d355332..f09c3f9c1 100644 --- a/Tests/KSCrash/KSMachHeader_Tests.m +++ b/Tests/KSCrash/KSMachHeader_Tests.m @@ -10,164 +10,93 @@ #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); +void bsg_mach_headers_add_image(const struct mach_header *mh, intptr_t slide); -const struct mach_header mh = { - .magic = 1, - .cputype = 42, - .cpusubtype = 27, - .filetype = 1, + +const struct mach_header header1 = { + .magic = MH_MAGIC, + .cputype = 0, + .cpusubtype = 0, + .filetype = 0, .ncmds = 1, - .sizeofcmds = 1, - .flags = 1 + .sizeofcmds = 0, + .flags = 0 +}; +const struct segment_command command1 = { + LC_SEGMENT,0,SEG_TEXT,111,10,0,0,0,0,0,0 }; -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 }; +const struct mach_header header2 = { + .magic = MH_MAGIC, + .cputype = 0, + .cpusubtype = 0, + .filetype = 0, + .ncmds = 1, + .sizeofcmds = 0, + .flags = 0 +}; +const struct segment_command command2 = { + LC_SEGMENT,0,SEG_TEXT,222,10,0,0,0,0,0,0 +}; @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); +- (void)testAddRemoveHeaders { - // Add - bsg_add_mach_binary_image(info1); - XCTAssertEqual(images->size, 2); - XCTAssertEqual(bsg_dyld_image_count(), 1); + bsg_mach_headers_initialize(); - 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); + // Get to the end of the list of system images to check additions + BSG_Mach_Header_Info *listTail; + for (BSG_Mach_Header_Info *img = bsg_mach_headers_get_images(); img != NULL; img = img->next) { + listTail = img; + } - // 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); + bsg_mach_headers_add_image(&header1, 0); - // 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); + XCTAssertEqual(listTail->next->imageVmAddr, 111); + XCTAssert(listTail->next->unloaded == FALSE); - bsg_remove_mach_binary_image(34567); - XCTAssertEqual(images->size, 4); - XCTAssertEqual(bsg_dyld_image_count(), 0); + bsg_mach_headers_add_image(&header2, 0); - bsg_remove_mach_binary_image(34567); - XCTAssertEqual(images->size, 4); - XCTAssertEqual(bsg_dyld_image_count(), 0); + XCTAssertEqual(listTail->next->imageVmAddr, 111); + XCTAssert(listTail->next->unloaded == FALSE); + XCTAssertEqual(listTail->next->next->imageVmAddr, 222); + XCTAssert(listTail->next->next->unloaded == FALSE); - // 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_mach_headers_remove_image(&header1, 0); + XCTAssertEqual(listTail->next->imageVmAddr, 111); + XCTAssert(listTail->next->unloaded == TRUE); + XCTAssertEqual(listTail->next->next->imageVmAddr, 222); + XCTAssert(listTail->next->next->unloaded == FALSE); + + bsg_mach_headers_remove_image(&header2, 0); + XCTAssertEqual(listTail->next->imageVmAddr, 111); + XCTAssert(listTail->next->unloaded == TRUE); + XCTAssertEqual(listTail->next->next->imageVmAddr, 222); + XCTAssert(listTail->next->next->unloaded == TRUE); - 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); +- (void)testFindImageAtAddress { + bsg_mach_headers_initialize(); + bsg_mach_headers_add_image(&header1, 0); + bsg_mach_headers_add_image(&header2, 0); - bsg_add_mach_binary_image(info1); - bsg_add_mach_binary_image(info2); - XCTAssertEqual(images->size, 2); - XCTAssertEqual(bsg_dyld_image_count(), 2); + BSG_Mach_Header_Info *item; + item = bsg_mach_headers_image_at_address(111); + XCTAssertEqual(item->imageVmAddr, 111); - 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); + item = bsg_mach_headers_image_at_address(222); + XCTAssertEqual(item->imageVmAddr, 222); +} - 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); +- (void) testGetImageNameNULL +{ + BSG_Mach_Header_Info *img = bsg_mach_headers_image_named(NULL, false); + XCTAssertTrue(img == NULL); } @end