最詳細(xì)的BSBacktraceLogger解析

簡(jiǎn)述

BSBacktraceLogger是一個(gè)輕量級(jí)的線程函數(shù)堆棧導(dǎo)出工具.

簡(jiǎn)單用法:

- (void)viewDidLoad {
    [super viewDidLoad];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        NSLog(@"%@",[BSBacktraceLogger bs_backtraceOfMainThread]);
    });
    [self foo];
}

- (void)foo {
    [self bar];
}

- (void)bar {
    while (true) {
        ;
    }
}

日志

2021-05-27 20:02:33.483299+0800 BacktraceLoggerTest[41034:5171140] Backtrace of Thread 259:
BacktraceLoggerTest             0x1069c64fc -[ViewController bar] + 12
BacktraceLoggerTest             0x1069c64e4 -[ViewController foo] + 36
BacktraceLoggerTest             0x1069c6590 -[ViewController viewDidLoad] + 128
UIKitCore                       0x7fff23f806a9 -[UIViewController _sendViewDidLoadWithAppearanceProxyObjectTaggingEnabled] + 88
UIKitCore                       0x7fff23f8504c -[UIViewController loadViewIfRequired] + 1084
UIKitCore                       0x7fff23f85436 -[UIViewController view] + 27
UIKitCore                       0x7fff246ffc73 -[UIWindow addRootViewControllerViewIfPossible] + 313
UIKitCore                       0x7fff246ff362 -[UIWindow _updateLayerOrderingAndSetLayerHidden:actionBlock:] + 219
UIKitCore                       0x7fff24700325 -[UIWindow _setHidden:forced:] + 362
UIKitCore                       0x7fff247133a6 -[UIWindow _mainQueue_makeKeyAndVisible] + 42
UIKitCore                       0x7fff24951c05 -[UIWindowScene _makeKeyAndVisibleIfNeeded] + 202
UIKitCore                       0x7fff23b0e80c +[UIScene _sceneForFBSScene:create:withSession:connectionOptions:] + 1671
UIKitCore                       0x7fff246c2df9 -[UIApplication _connectUISceneFromFBSScene:transitionContext:] + 1114
UIKitCore                       0x7fff246c3128 -[UIApplication workspace:didCreateScene:withTransitionContext:completion:] + 289
UIKitCore                       0x7fff241a4ab4 -[UIApplicationSceneClientAgent scene:didInitializeWithEvent:completion:] + 358
FrontBoardServices              0x7fff25a1b40b -[FBSScene _callOutQueue_agent_didCreateWithTransitionContext:completion:] + 398
FrontBoardServices              0x7fff25a43e55 __94-[FBSWorkspaceScenesClient createWithSceneID:groupID:parameters:transitionContext:completion:]_block_invoke.176 + 102
FrontBoardServices              0x7fff25a28f12 -[FBSWorkspace _calloutQueue_executeCalloutFromSource:withBlock:] + 209
FrontBoardServices              0x7fff25a43b28 __94-[FBSWorkspaceScenesClient createWithSceneID:groupID:parameters:transitionContext:completion:]_block_invoke + 352
libdispatch.dylib               0x106c4174e _dispatch_client_callout + 8
libdispatch.dylib               0x106c44656 _dispatch_block_invoke_direct + 295
FrontBoardServices              0x7fff25a695d0 __FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 30
FrontBoardServices              0x7fff25a692b6 -[FBSSerialQueue _targetQueue_performNextIfPossible] + 433
FrontBoardServices              0x7fff25a6977b -[FBSSerialQueue _performNextFromRunLoopSource] + 22
CoreFoundation                  0x7fff20390ede __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
CoreFoundation                  0x7fff20390dd6 __CFRunLoopDoSource0 + 180
CoreFoundation                  0x7fff20390300 __CFRunLoopDoSources0 + 340
CoreFoundation                  0x7fff2038a9f7 __CFRunLoopRun + 875
CoreFoundation                  0x7fff2038a1a7 CFRunLoopRunSpecific + 567
GraphicsServices                0x7fff2b874d85 GSEventRunModal + 139
UIKitCore                       0x7fff246c14df -[UIApplication _run] + 912
UIKitCore                       0x7fff246c639c UIApplicationMain + 101
BacktraceLoggerTest             0x1069c6912 main + 114
libdyld.dylib                   0x7fff2025abbd start + 1

源碼閱讀

?? 本文只討論 + (NSString *)bs_backtraceOfMainThread.
?? 本文以注釋形式解析代碼.本代碼可直接覆蓋GitHub原庫(kù).m文件.邊斷點(diǎn)調(diào)試邊閱讀注釋.
名詞:

  • 映像: Mach-O文件倒入到內(nèi)存之后.
  • 文件: Mach-O文件.
/*
    // 符號(hào)數(shù)據(jù)結(jié)構(gòu)
    struct BS_NLIST {
    union {
        uint32_t n_strx;   // index into the string table
    } n_un;
    uint8_t  n_type;       // type flag, see below
    uint8_t  n_sect;       // section number or NO_SECT 符號(hào)所在的 section index
    uint16_t n_desc;       // see <mach-o/stab.h>
    uint64_t n_value;      // value of this symbol (or stab offset) 符號(hào)的地址值
    };
 */
 
// 棧幀抽象結(jié)構(gòu)體,只關(guān)注有用的兩個(gè)指針
typedef struct BSStackFrameEntry{
    const struct BSStackFrameEntry *const previous; // FP: *FP 上一個(gè)函數(shù)起始
    const uintptr_t return_address;                 // LR: *(FP + 8) 此函數(shù)結(jié)束后返回的上一個(gè)函數(shù)的下一條指令的地址
} BSStackFrameEntry;

static mach_port_t main_thread_id;

@implementation BSBacktraceLogger

+ (void)load {
    main_thread_id = mach_thread_self();
}

#pragma -mark Implementation of interface

+ (NSString *)bs_backtraceOfNSThread:(NSThread *)thread {
    // NSThread 轉(zhuǎn) Mach thread.
    return _bs_backtraceOfThread(bs_machThreadFromNSThread(thread));
}

+ (NSString *)bs_backtraceOfCurrentThread {
    return [self bs_backtraceOfNSThread:[NSThread currentThread]];
}

+ (NSString *)bs_backtraceOfMainThread {
    // 傳入NSThread主線程
    return [self bs_backtraceOfNSThread:[NSThread mainThread]];
}

+ (NSString *)bs_backtraceOfAllThread {
    thread_act_array_t threads;
    mach_msg_type_number_t thread_count = 0;
    const task_t this_task = mach_task_self();
    
    kern_return_t kr = task_threads(this_task, &threads, &thread_count);
    if(kr != KERN_SUCCESS) {
        return @"Fail to get information of all threads";
    }
    
    NSMutableString *resultString = [NSMutableString stringWithFormat:@"Call Backtrace of %u threads:\n", thread_count];
    for(int i = 0; i < thread_count; i++) {
        [resultString appendString:_bs_backtraceOfThread(threads[i])];
    }
    return [resultString copy];
}

#pragma -mark Get call backtrace of a mach_thread

/// 導(dǎo)出堆棧符號(hào)字符串
NSString *_bs_backtraceOfThread(thread_t thread) {
    uintptr_t backtraceBuffer[50];  // 待符號(hào)化的堆棧地址數(shù)組
    int i = 0;
    NSMutableString *resultString = [[NSMutableString alloc] initWithFormat:@"Backtrace of Thread %u:\n", thread];
    
    /*
     * _STRUCT_MCONTEXT64
     * {
     *   _STRUCT_X86_EXCEPTION_STATE64   __es;
     *   _STRUCT_X86_THREAD_STATE64      __ss;
     *   _STRUCT_X86_FLOAT_STATE64       __fs;
     * };
     
     * _STRUCT_ARM_THREAD_STATE64
     * {
     *   __uint64_t __x[29];    // General purpose registers x0-x28
     *   __uint64_t __fp;       // Frame pointer x29
     *   __uint64_t __lr;       // Link register x30
     *   __uint64_t __sp;       // Stack pointer x31
     *   __uint64_t __pc;       // Program counter
     *   __uint32_t __cpsr;     // Current program status register
     *   __uint32_t __pad;      // Same size for 32-bit or 64-bit clients
     * };
     */
    
    _STRUCT_MCONTEXT machineContext;
    
    // 通過(guò)thread初始化machineContext,里面有__ss, __ss里面有LR帖鸦、FP、SP等寄存器.
    if(!bs_fillThreadStateIntoMachineContext(thread, &machineContext)) {
        return [NSString stringWithFormat:@"Fail to get information about thread: %u", thread];
    }
    
    // PC寄存器.即將要執(zhí)行的下一條指令.
    // ?為什么要先加入PC? 因?yàn)樵诜?hào)化的時(shí)候可以找到當(dāng)前函數(shù)地址.如果只有LR,當(dāng)前函數(shù)沒(méi)有指針指向了.
    const uintptr_t instructionAddress = bs_mach_instructionAddress(&machineContext);
    backtraceBuffer[i] = instructionAddress;
    ++i;
    
    // LR寄存器,函數(shù)返回地址.用于遞歸符號(hào)化堆棧
    uintptr_t linkRegister = bs_mach_linkRegister(&machineContext);
    if (linkRegister) {
        backtraceBuffer[i] = linkRegister;
        i++;
    }
    
    if(instructionAddress == 0) {
        return @"Fail to get instruction address";
    }
    
    BSStackFrameEntry frame = {0};  // 初始化一個(gè)空的棧幀模型
    const uintptr_t framePtr = bs_mach_framePointer(&machineContext); // 拿FP,棧幀指針. 指向函數(shù)起始地址, *FP保存上一個(gè)函數(shù)起始地址. 用于遞歸函數(shù)調(diào)用棧. machineContext->__ss.__fp
    
    // 用FP初始化首個(gè)棧幀模型BSStackFrameEntry.bs_mach_copyMem() 從framePtr和framePtr + 8的位置拿值,分別初始化 previous胚嘲、returnAddress
    if(framePtr == 0 || bs_mach_copyMem((void *)framePtr, &frame, sizeof(frame)) != KERN_SUCCESS) {
        return @"Fail to get frame pointer";
    }
    
    /*  例子:
        long * ptt = (long *)framePtr;              // 0d6163429232 -> 0x16f5e7770
        NSLog(@"%lx", *ptt);                        // *0x16f5e7770(FP) = 0x16f5e8c20 = frame.previous

        long * returnAdd = (long *)(framePtr + 8);  // 0d6163429240 -> 0x16f5e7778
        NSLog(@"%lx", *returnAdd);                  // *0x16f5e7778(FP + 8 = LR) = 0x1dd76d224 = frame.returnAddress
     */
    
    // i = 2
    for(; i < 50; i++) {
        backtraceBuffer[i] = frame.return_address;
        
        // 調(diào)用bs_mach_copyMem(), 遞歸: FP = *FP, LR = *(FP + 8)
        if(backtraceBuffer[i] == 0 || frame.previous == 0 || bs_mach_copyMem(frame.previous, &frame, sizeof(frame)) != KERN_SUCCESS) {
            break;
        }
    }
    
    int backtraceLength = i;
    Dl_info symbolicated[backtraceLength];
    bs_symbolicate(backtraceBuffer, symbolicated, backtraceLength, 0); // 對(duì)當(dāng)前的堆棧進(jìn)行符號(hào)化
    for (int i = 0; i < backtraceLength; ++i) {
        [resultString appendFormat:@"%@", bs_logBacktraceEntry(i, backtraceBuffer[i], &symbolicated[i])];
    }
    [resultString appendFormat:@"\n"];
    return [resultString copy];
}

#pragma -mark Convert NSThread to Mach thread
// NSThread 與 Mach thread 對(duì)應(yīng)
thread_t bs_machThreadFromNSThread(NSThread *nsthread) {
    char name[256];
    mach_msg_type_number_t count;
    thread_act_array_t list;
    task_threads(mach_task_self(), &list, &count);  // 將 mach_task_self() 進(jìn)程中的所有線程枚舉保存在list中
    
    // 因?yàn)镹SThread中 name 和 Mach thread 是一個(gè).所以用 name 匹配
    // 給NSThread設(shè)置一個(gè)名字,然后去pthread中查找同名的線程.
    // ?? 注意:可能有名稱(chēng)重復(fù)的問(wèn)題,所以用時(shí)間戳作為新名字.匹配完畢之后名字記得還原
    NSTimeInterval currentTimestamp = [[NSDate date] timeIntervalSince1970];
    NSString *originName = [nsthread name];
    [nsthread setName:[NSString stringWithFormat:@"%f", currentTimestamp]];
    
    if ([nsthread isMainThread]) {
        return (thread_t)main_thread_id;
    }
    
    for (int i = 0; i < count; ++i) {
        pthread_t pt = pthread_from_mach_thread_np(list[i]);
        if ([nsthread isMainThread]) {
            if (list[i] == main_thread_id) {
                return list[i];
            }
        }
        if (pt) {
            name[0] = '\0';
            pthread_getname_np(pt, name, sizeof name);
            if (!strcmp(name, [nsthread name].UTF8String)) {
                [nsthread setName:originName];
                return list[i];
            }
        }
    }
    
    [nsthread setName:originName];
    return mach_thread_self();
}

#pragma -mark GenerateBacbsrackEnrty
/// 組裝符號(hào)字符串
NSString* bs_logBacktraceEntry(const int entryNum,
                               const uintptr_t address,
                               const Dl_info* const dlInfo) {
    char faddrBuff[20];
    char saddrBuff[20];
    
    const char* fname = bs_lastPathEntry(dlInfo->dli_fname);
    if(fname == NULL) {
        sprintf(faddrBuff, POINTER_FMT, (uintptr_t)dlInfo->dli_fbase);
        fname = faddrBuff;
    }
    
    uintptr_t offset = address - (uintptr_t)dlInfo->dli_saddr;
    const char* sname = dlInfo->dli_sname;
    if(sname == NULL) {
        sprintf(saddrBuff, POINTER_SHORT_FMT, (uintptr_t)dlInfo->dli_fbase);
        sname = saddrBuff;
        offset = address - (uintptr_t)dlInfo->dli_fbase;
    }
    return [NSString stringWithFormat:@"%-30s  0x%08" PRIxPTR " %s + %lu\n" ,fname, (uintptr_t)address, sname, offset];
}

const char* bs_lastPathEntry(const char* const path) {
    if(path == NULL) {
        return NULL;
    }
    
    char* lastFile = strrchr(path, '/');
    return lastFile == NULL ? path : lastFile + 1;
}

#pragma -mark HandleMachineContext
bool bs_fillThreadStateIntoMachineContext(thread_t thread, _STRUCT_MCONTEXT *machineContext) {
    mach_msg_type_number_t state_count = BS_THREAD_STATE_COUNT;
    // 獲取線程所有信息,寫(xiě)入machineContext中, ss: Stack Segment 堆棧段寄存器.內(nèi)涵FP作儿、LR、SP馋劈、PC...等寄存器
    kern_return_t kr = thread_get_state(thread, BS_THREAD_STATE, (thread_state_t)&machineContext->__ss, &state_count);
    return (kr == KERN_SUCCESS);
}

uintptr_t bs_mach_framePointer(mcontext_t const machineContext){
    return machineContext->__ss.BS_FRAME_POINTER;
}

uintptr_t bs_mach_stackPointer(mcontext_t const machineContext){
    return machineContext->__ss.BS_STACK_POINTER;
}

uintptr_t bs_mach_instructionAddress(mcontext_t const machineContext){
    return machineContext->__ss.BS_INSTRUCTION_ADDRESS;
}

uintptr_t bs_mach_linkRegister(mcontext_t const machineContext){
#if defined(__i386__) || defined(__x86_64__)
    return 0;
#else
    return machineContext->__ss.__lr;
#endif
}

/// 拷貝FP到結(jié)構(gòu)體
/// @param src FP
/// @param dst BSStackFrameEntry
/// @param numBytes BSStackFrameEntry長(zhǎng)度
kern_return_t bs_mach_copyMem(const void *const src, void *const dst, const size_t numBytes){
    vm_size_t bytesCopied = 0;
    return vm_read_overwrite(mach_task_self(), (vm_address_t)src, (vm_size_t)numBytes, (vm_address_t)dst, &bytesCopied);
}

#pragma -mark Symbolicate

/// 地址轉(zhuǎn)符號(hào)字符串
/// @param backtraceBuffer 棧數(shù)據(jù)數(shù)組
/// @param symbolsBuffer 空數(shù)組
/// @param numEntries 棧數(shù)據(jù)長(zhǎng)度
/// @param skippedEntries = 0
void bs_symbolicate(const uintptr_t* const backtraceBuffer,
                    Dl_info* const symbolsBuffer,
                    const int numEntries,
                    const int skippedEntries){
    int i = 0;
    
    // 第一個(gè)存儲(chǔ)的是PC寄存器
    if(!skippedEntries && i < numEntries) {
        bs_dladdr(backtraceBuffer[i], &symbolsBuffer[i]);
        i++;
    }
    
    // 后面存儲(chǔ)的都是LR
    for(; i < numEntries; i++) {
        bs_dladdr(CALL_INSTRUCTION_FROM_RETURN_ADDRESS(backtraceBuffer[i]), &symbolsBuffer[i]);
    }
}

/// 找到LR指針最近的符號(hào), 放到 info 中
bool bs_dladdr(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 = bs_imageIndexContainingAddress(address);                       // 獲取address所在的image序號(hào)
    if(idx == UINT_MAX) {
        return false;
    }
    const struct mach_header* header = _dyld_get_image_header(idx);                     // 獲取映像的mach-o頭部信息結(jié)構(gòu)體指針, header對(duì)象存儲(chǔ)load command個(gè)數(shù)及大小
    const uintptr_t imageVMAddrSlide = (uintptr_t)_dyld_get_image_vmaddr_slide(idx);    // slide.
    const uintptr_t addressWithSlide = address - imageVMAddrSlide;                      // LR虛擬內(nèi)存地址
    const uintptr_t segmentBase = bs_segmentBaseOfImageIndex(idx) + imageVMAddrSlide;   // vmaddr - fileoff + slide
    if(segmentBase == 0) {
        return false;
    }
    
    info->dli_fname = _dyld_get_image_name(idx);
    info->dli_fbase = (void*)header;
    
    // Find symbol tables and get whichever symbol is closest to the address.
    const BS_NLIST* bestMatch = NULL;                  // 符號(hào)結(jié)構(gòu)體
    uintptr_t bestDistance = ULONG_MAX;
    uintptr_t cmdPtr = bs_firstCmdAfterHeader(header); // Load Commands
    if(cmdPtr == 0) {
        return false;
    }
    
    // 遍歷Load Commands,找到 LC_SYMTAB 段
    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;    // LC_SYMTAB,內(nèi)含符號(hào)表位于可執(zhí)行文件的偏移以及字符串表位于可執(zhí)行文件的偏移
            const BS_NLIST* symbolTable = (BS_NLIST*)(segmentBase + symtabCmd->symoff); // 符號(hào)表內(nèi)存真實(shí)地址, symoff 為符號(hào)表在 Mach-O 文件中的偏移
            const uintptr_t stringTable = segmentBase + symtabCmd->stroff;              // 字符串表內(nèi)存真實(shí)地址, stroff 為字符串表在 Mach-O 文件中的偏移
            
            // 遍歷所有符號(hào),找到與LR最近的那個(gè). symtabCmd->nsyms指示了符號(hào)表的條目
            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) {
                    uintptr_t symbolBase = symbolTable[iSym].n_value;                           // n_value 符號(hào)的虛擬內(nèi)存地址值
                    uintptr_t currentDistance = addressWithSlide - symbolBase;                  // LR與符號(hào)的距離
                    if((addressWithSlide >= symbolBase) && (currentDistance <= bestDistance)) { // 函數(shù)地址值在本符號(hào)之后 且 距離小于之前的最近距離
                        bestMatch = symbolTable + iSym;                                         // 最匹配的符號(hào) = 當(dāng)前符號(hào)表結(jié)構(gòu)體 + n個(gè)偏移
                        bestDistance = currentDistance;                                         // 最近距離 = 當(dāng)前距離
                    }
                }
            }
            if(bestMatch != NULL) {
                info->dli_saddr = (void*)(bestMatch->n_value + imageVMAddrSlide);                       // 符號(hào)真實(shí)地址 = n_value + slide
                info->dli_sname = (char*)((intptr_t)stringTable + (intptr_t)bestMatch->n_un.n_strx);    // 字符真實(shí)地址 = 字符串表地址 + 最接近的符號(hào)中的字符串表(數(shù)組)索引值 n_strx(輸出從此處到下一個(gè)null)
                if(*info->dli_sname == '_') {
                    info->dli_sname++;
                }
                // This happens if all symbols have been stripped.
                if(info->dli_saddr == info->dli_fbase && bestMatch->n_type == 3) {
                    info->dli_sname = NULL;
                }
                break;
            }
        }
        cmdPtr += loadCmd->cmdsize;
    }
    return true;
}

/// 找到Header下面第一個(gè)Command
uintptr_t bs_firstCmdAfterHeader(const struct mach_header* const header) {
    switch(header->magic) {
        case MH_MAGIC:
        case MH_CIGAM:
            return (uintptr_t)(header + 1); // 向下偏移1個(gè)mach_header長(zhǎng)度.
        case MH_MAGIC_64:
        case MH_CIGAM_64:
            return (uintptr_t)(((struct mach_header_64*)header) + 1); // 向下偏移1個(gè)mach_header_64長(zhǎng)度.mach_header_64比mach_header多4個(gè)字節(jié).
        default:
            return 0;  // Header is corrupt
    }
}

// 遍歷loadCommands攻锰,確認(rèn)adress是否落在當(dāng)前image的某個(gè)segment中. address: LR地址
uint32_t bs_imageIndexContainingAddress(const uintptr_t address) {
    const uint32_t imageCount = _dyld_image_count(); // 返回當(dāng)前進(jìn)程中加載的映像的數(shù)量
    const struct mach_header* header = 0;
    
    // 遍歷image
    for(uint32_t iImg = 0; iImg < imageCount; iImg++) {
        header = _dyld_get_image_header(iImg); // 得到image_header
        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);  // 得到減去ASLR之后的地址.在mach-o中真實(shí)地址
            uintptr_t cmdPtr = bs_firstCmdAfterHeader(header);                                  // 得到Header下面第一個(gè)loadCommands的地址
            if(cmdPtr == 0) {
                continue;
            }
            
            // ncmds: loadcommands的數(shù)量.
            for(uint32_t iCmd = 0; iCmd < header->ncmds; iCmd++) {
                const struct load_command* loadCmd = (struct load_command*)cmdPtr;
                if(loadCmd->cmd == LC_SEGMENT) { // command 類(lèi)型為 LC_SEGMENT, 使用結(jié)構(gòu)體segment_command
                    const struct segment_command* segCmd = (struct segment_command*)cmdPtr;
                    if(addressWSlide >= segCmd->vmaddr &&
                       addressWSlide < segCmd->vmaddr + segCmd->vmsize) {
                        return iImg; // 如果LR的地址落在這個(gè)模塊里,則返回映像索引號(hào)
                    }
                }
                else if(loadCmd->cmd == LC_SEGMENT_64) { // command 類(lèi)型為 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; // 如果LR的地址落在這個(gè)模塊里,則返回映像索引號(hào)
                    }
                }
                cmdPtr += loadCmd->cmdsize; // command地址是連續(xù)的,移動(dòng)到下一個(gè)command位置
            }
        }
    }
    return UINT_MAX;
}

/*
 *  sym_vmaddr(符號(hào)表虛擬地址) - vmaddr(LINKEDIT虛擬地址) = symoff(符號(hào)表文件地址) - fileoff(LINKEDIT文件地址)
 *  sym_vmaddr = vmaddr - fileoff + symoff
 *  因?yàn)?符號(hào)表內(nèi)存真實(shí)地址 = sym_vmaddr + slide
 *  所以 符號(hào)表真實(shí)內(nèi)存地址 = vmaddr - fileoff + symoff + slide
 *  此函數(shù)只為計(jì)算 vmaddr - fileoff
 */
uintptr_t bs_segmentBaseOfImageIndex(const uint32_t idx) {
    const struct mach_header* header = _dyld_get_image_header(idx);
    
    // Look for a segment command and return the file image address.
    uintptr_t cmdPtr = bs_firstCmdAfterHeader(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) { // __LINKEDIT是鏈接信息段,可以通過(guò)__LINKEDIT進(jìn)行符號(hào)地址計(jì)算
                return (uintptr_t)(segmentCmd->vmaddr - segmentCmd->fileoff);
            }
        }
        cmdPtr += loadCmd->cmdsize;
    }
    return 0;
}

@end

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末妓雾,一起剝皮案震驚了整個(gè)濱河市娶吞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌械姻,老刑警劉巖妒蛇,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異楷拳,居然都是意外死亡绣夺,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)欢揖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)陶耍,“玉大人,你說(shuō)我怎么就攤上這事浸颓。” “怎么了旺拉?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵产上,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我蛾狗,道長(zhǎng)晋涣,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任沉桌,我火速辦了婚禮谢鹊,結(jié)果婚禮上算吩,老公的妹妹穿的比我還像新娘。我一直安慰自己佃扼,他們只是感情好偎巢,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著兼耀,像睡著了一般压昼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瘤运,一...
    開(kāi)封第一講書(shū)人閱讀 51,125評(píng)論 1 297
  • 那天窍霞,我揣著相機(jī)與錄音,去河邊找鬼拯坟。 笑死但金,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的郁季。 我是一名探鬼主播冷溃,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼巩踏!你這毒婦竟也來(lái)了秃诵?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤塞琼,失蹤者是張志新(化名)和其女友劉穎菠净,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體彪杉,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡毅往,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了派近。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片攀唯。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖渴丸,靈堂內(nèi)的尸體忽然破棺而出侯嘀,到底是詐尸還是另有隱情,我是刑警寧澤谱轨,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布戒幔,位于F島的核電站,受9級(jí)特大地震影響土童,放射性物質(zhì)發(fā)生泄漏诗茎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一献汗、第九天 我趴在偏房一處隱蔽的房頂上張望敢订。 院中可真熱鬧王污,春花似錦、人聲如沸楚午。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)醒叁。三九已至司浪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間把沼,已是汗流浹背啊易。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留饮睬,地道東北人租谈。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像捆愁,于是被迫代替她去往敵國(guó)和親割去。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容