簡(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