在 fishhook------原理分析 中,驗證了fishhook符號重綁定的實現原理,這裏主要分析一下fishhook的代碼實現.
準備
理論知識
Mach-O的文件佈局
Mach-O文件一共有三個部分:
- Header:header包含了文件的基本信息,實際上不會映射到虛擬內存空間上去;
- Load Commands:這部分確定了文件數據在虛擬內存上的佈局,包含各個部分的映射到虛擬空間上的虛擬地址,虛擬空間大小,實際文件在基地址上的偏移量,實際文件大小,包含section的數量等.
- Data: 這個段是真實的數據段,按照在Load Commands的定義被加載到虛擬內存中去.
符號表的查找
Mach-O的符號有兩部分,一部分是非懶加載符號表(__ln_symbol_ptr),指向非延時綁定的指針數組,這部分符號在動態庫加載時已經完成綁定,是非延時的;另一部分是懶加載符號表(__la_symbol_ptr),指向延時綁定的指針數組,這部分符號一般是在第一次調用時有_dyld_stub_binder去查找並填充(也有可能是啓動時進行綁定).
無論是那種方式符號表,都由統一的規則去尋找對應的符號:
- 在Load Commands段中,__DATA,__la_symbol_ptr(或者__ln_symbol_ptr)中存在存在一個偏移值reserved1,這個偏移值確定了對應的符號表(__ln_symbol_ptr或者__la_symbol_ptr)中的元素在重定向符號表(Indirect Symbols)中的起始索引位置startIndex;
- 在DATA段中,__la_symbol_ptr(或者__ln_symbol_ptr)指針數組中存儲了各個不同的符號信息.對於每一個符號信息在該數組中的索引relativeIndex,就是在重定向符號表中以startIndex爲起始位置,relativeIndex處的元素.即利用index=startIndex+relativeIndex,在重定向符號表中,獲取索引爲index位置對應的偏移值offsetInSymbols;
- 在符號表(Symbols)中,獲取到索引offsetInSymbols處的值,即爲該符號在符號在字符串表(String Table)中的位置偏移量offset;
- 根據字符串表的起始位置,加上偏移量offset即可得到該符號的值.
具體的查找過程在 fishhook------原理分析 中有詳細的描述.
源碼中的實現拆分
區分不同指令集對應架構對應的數據結構
Mach-O中可以包含多種架構,但是不同的架構在數據結構的定義上會有不同,所以爲了兼容不同的指令集架構,需要通過紅定義來區分不同架構下使用的數據結構,方便統一做處理:
#ifdef __LP64__
typedef struct mach_header_64 mach_header_t;
typedef struct segment_command_64 segment_command_t;
typedef struct section_64 section_t;
typedef struct nlist_64 nlist_t;
#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT_64
#else
typedef struct mach_header mach_header_t;
typedef struct segment_command segment_command_t;
typedef struct section section_t;
typedef struct nlist nlist_t;
#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT
#endif
Load Command中數據包含的數據結構
在Load Commands中,
- 最外層的數據主要使用數據結構
struct load_command {
uint32_t cmd; /* type of load command */
uint32_t cmdsize; /* total size of command in bytes */
};
來表示,其中cmd是一個特殊的整型類型,主要有以下類型:
/* Constants for the cmd field of all load commands, the type */
#define LC_SEGMENT 0x1 /* segment of this file to be mapped */
#define LC_SYMTAB 0x2 /* link-edit stab symbol table info */
#define LC_SYMSEG 0x3 /* link-edit gdb symbol table info (obsolete) */
#define LC_THREAD 0x4 /* thread */
#define LC_UNIXTHREAD 0x5 /* unix thread (includes a stack) */
#define LC_LOADFVMLIB 0x6 /* load a specified fixed VM shared library */
#define LC_IDFVMLIB 0x7 /* fixed VM shared library identification */
#define LC_IDENT 0x8 /* object identification info (obsolete) */
#define LC_FVMFILE 0x9 /* fixed VM file inclusion (internal use) */
#define LC_PREPAGE 0xa /* prepage command (internal use) */
#define LC_DYSYMTAB 0xb /* dynamic link-edit symbol table info */
#define LC_LOAD_DYLIB 0xc /* load a dynamically linked shared library */
#define LC_ID_DYLIB 0xd /* dynamically linked shared lib ident */
#define LC_LOAD_DYLINKER 0xe /* load a dynamic linker */
#define LC_ID_DYLINKER 0xf /* dynamic linker identification */
#define LC_PREBOUND_DYLIB 0x10 /* modules prebound for a dynamically */
/* linked shared library */
#define LC_ROUTINES 0x11 /* image routines */
#define LC_SUB_FRAMEWORK 0x12 /* sub framework */
#define LC_SUB_UMBRELLA 0x13 /* sub umbrella */
#define LC_SUB_CLIENT 0x14 /* sub client */
#define LC_SUB_LIBRARY 0x15 /* sub library */
#define LC_TWOLEVEL_HINTS 0x16 /* two-level namespace lookup hints */
#define LC_PREBIND_CKSUM 0x17 /* prebind checksum */
/*
* load a dynamically linked shared library that is allowed to be missing
* (all symbols are weak imported).
*/
#define LC_LOAD_WEAK_DYLIB (0x18 | LC_REQ_DYLD)
#define LC_SEGMENT_64 0x19 /* 64-bit segment of this file to be
mapped */
#define LC_ROUTINES_64 0x1a /* 64-bit image routines */
#define LC_UUID 0x1b /* the uuid */
#define LC_RPATH (0x1c | LC_REQ_DYLD) /* runpath additions */
#define LC_CODE_SIGNATURE 0x1d /* local of code signature */
#define LC_SEGMENT_SPLIT_INFO 0x1e /* local of info to split segments */
#define LC_REEXPORT_DYLIB (0x1f | LC_REQ_DYLD) /* load and re-export dylib */
#define LC_LAZY_LOAD_DYLIB 0x20 /* delay load of dylib until first use */
#define LC_ENCRYPTION_INFO 0x21 /* encrypted segment information */
#define LC_DYLD_INFO 0x22 /* compressed dyld information */
#define LC_DYLD_INFO_ONLY (0x22|LC_REQ_DYLD) /* compressed dyld information only */
#define LC_LOAD_UPWARD_DYLIB (0x23 | LC_REQ_DYLD) /* load upward dylib */
#define LC_VERSION_MIN_MACOSX 0x24 /* build for MacOSX min OS version */
#define LC_VERSION_MIN_IPHONEOS 0x25 /* build for iPhoneOS min OS version */
#define LC_FUNCTION_STARTS 0x26 /* compressed table of function start addresses */
#define LC_DYLD_ENVIRONMENT 0x27 /* string for dyld to treat
like environment variable */
#define LC_MAIN (0x28|LC_REQ_DYLD) /* replacement for LC_UNIXTHREAD */
#define LC_DATA_IN_CODE 0x29 /* table of non-instructions in __text */
#define LC_SOURCE_VERSION 0x2A /* source version used to build binary */
#define LC_DYLIB_CODE_SIGN_DRS 0x2B /* Code signing DRs copied from linked dylibs */
#define LC_ENCRYPTION_INFO_64 0x2C /* 64-bit encrypted segment information */
#define LC_LINKER_OPTION 0x2D /* linker options in MH_OBJECT files */
#define LC_LINKER_OPTIMIZATION_HINT 0x2E /* optimization hints in MH_OBJECT files */
#define LC_VERSION_MIN_TVOS 0x2F /* build for AppleTV min OS version */
#define LC_VERSION_MIN_WATCHOS 0x30 /* build for Watch min OS version */
#define LC_NOTE 0x31 /* arbitrary data included within a Mach-O file */
#define LC_BUILD_VERSION 0x32 /* build for platform min OS version */
#define LC_DYLD_EXPORTS_TRIE (0x33 | LC_REQ_DYLD) /* used with linkedit_data_command, payload is trie */
#define LC_DYLD_CHAINED_FIXUPS (0x34 | LC_REQ_DYLD) /* used with linkedit_data_command */
所以可以依據struct load_command中的cmd變量來確定對一個的Load Command命令屬於那種類型,然後再使用不同的類型來進行轉化.例如如果是LC_SEGEMENT或者LC_SEGMENT_64類型就可以使用數據結構
# 64位架構下
struct segment_command_64 { /* for 64-bit architectures */
uint32_t cmd; /* LC_SEGMENT_64 */
uint32_t cmdsize; /* includes sizeof section_64 structs */
char segname[16]; /* segment name */
uint64_t vmaddr; /* memory address of this segment */
uint64_t vmsize; /* memory size of this segment */
uint64_t fileoff; /* file offset of this segment */
uint64_t filesize; /* amount to map from the file */
vm_prot_t maxprot; /* maximum VM protection */
vm_prot_t initprot; /* initial VM protection */
uint32_t nsects; /* number of sections in segment */
uint32_t flags; /* flags */
};
# 32位架構下
struct segment_command { /* for 32-bit architectures */
uint32_t cmd; /* LC_SEGMENT */
uint32_t cmdsize; /* includes sizeof section structs */
char segname[16]; /* segment name */
uint32_t vmaddr; /* memory address of this segment */
uint32_t vmsize; /* memory size of this segment */
uint32_t fileoff; /* file offset of this segment */
uint32_t filesize; /* amount to map from the file */
vm_prot_t maxprot; /* maximum VM protection */
vm_prot_t initprot; /* initial VM protection */
uint32_t nsects; /* number of sections in segment */
uint32_t flags; /* flags */
};
進行類型轉化;如果是LC_SYMTAB類型,就可以使用數據結構
struct symtab_command {
uint32_t cmd; /* LC_SYMTAB */
uint32_t cmdsize; /* sizeof(struct symtab_command) */
uint32_t symoff; /* symbol table offset */
uint32_t nsyms; /* number of symbol table entries */
uint32_t stroff; /* string table offset */
uint32_t strsize; /* string table size in bytes */
};
進行轉化;如果LC_DYSYMTAB,就可使用數據結構
struct dysymtab_command {
uint32_t cmd; /* LC_DYSYMTAB */
uint32_t cmdsize; /* sizeof(struct dysymtab_command) */
uint32_t ilocalsym; /* index to local symbols */
uint32_t nlocalsym; /* number of local symbols */
uint32_t iextdefsym;/* index to externally defined symbols */
uint32_t nextdefsym;/* number of externally defined symbols */
uint32_t iundefsym; /* index to undefined symbols */
uint32_t nundefsym; /* number of undefined symbols */
uint32_t tocoff; /* file offset to table of contents */
uint32_t ntoc; /* number of entries in table of contents */
uint32_t modtaboff; /* file offset to module table */
uint32_t nmodtab; /* number of module table entries */
uint32_t extrefsymoff; /* offset to referenced symbol table */
uint32_t nextrefsyms; /* number of referenced symbol table entries */
uint32_t indirectsymoff; /* file offset to the indirect symbol table */
uint32_t nindirectsyms; /* number of indirect symbol table entries */
uint32_t extreloff; /* offset to external relocation entries */
uint32_t nextrel; /* number of external relocation entries */
uint32_t locreloff; /* offset to local relocation entries */
uint32_t nlocrel; /* number of local relocation entries */
};
進行轉化等等.
- 一個記載指令(load_command)可以包含多個Section,所以第二層的數據主要可以使用是數據結構segment_command_t來表示:
# 32位架構下
struct segment_command { /* for 32-bit architectures */
uint32_t cmd; /* LC_SEGMENT */
uint32_t cmdsize; /* includes sizeof section structs */
char segname[16]; /* segment name */
uint32_t vmaddr; /* memory address of this segment */
uint32_t vmsize; /* memory size of this segment */
uint32_t fileoff; /* file offset of this segment */
uint32_t filesize; /* amount to map from the file */
vm_prot_t maxprot; /* maximum VM protection */
vm_prot_t initprot; /* initial VM protection */
uint32_t nsects; /* number of sections in segment */
uint32_t flags; /* flags */
};
# 64位架構下
struct segment_command_64 { /* for 64-bit architectures */
uint32_t cmd; /* LC_SEGMENT_64 */
uint32_t cmdsize; /* includes sizeof section_64 structs */
char segname[16]; /* segment name */
uint64_t vmaddr; /* memory address of this segment */
uint64_t vmsize; /* memory size of this segment */
uint64_t fileoff; /* file offset of this segment */
uint64_t filesize; /* amount to map from the file */
vm_prot_t maxprot; /* maximum VM protection */
vm_prot_t initprot; /* initial VM protection */
uint32_t nsects; /* number of sections in segment */
uint32_t flags; /* flags */
};
- Symbols 中的元素對應了一個新的結構體:
struct nlist {
union {
#ifndef __LP64__
char *n_name; /* for use when in-core */
#endif
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 */
int16_t n_desc; /* see <mach-o/stab.h> */
uint32_t n_value; /* value of this symbol (or stab offset) */
};
/*
* This is the symbol table entry structure for 64-bit architectures.
*/
struct nlist_64 {
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 */
uint16_t n_desc; /* see <mach-o/stab.h> */
uint64_t n_value; /* value of this symbol (or stab offset) */
};
利用這個結構體可以獲取到對應索引處的偏移地址strtab_offset,假設string table的起始地址strtab,就可獲取到對應的符號.
開始擼代碼
- 獲取所有的image(target module)
在一個應用中會有多個image文件,也就是Mach-O文件.如果獲取應用中所有的target module文件呢?
// 包含在<mach-o/dyld.h>中
uint32_t countOfAllTargetModules = _dyld_image_count();
for(uint32_t i = 0; i < countOfAllTargetModules; i++) {
//獲取name
const char *imageName = _dyld_get_image_name(i);
//獲取偏移量(ASLR導致)
intptr_t silde = _dyld_get_image_vmaddr_slide(i);
//獲取mach_header
mach_header_t *header;
header = (mach_header_t *)_dyld_get_image_header(i);
Dl_info info;
dladdr(header, &info);
NSLog(@"dli_fname == %s", info.dli_fname);
NSLog(@"dli_sname == 0x%016lx", (uintptr_t)info.dli_fbase);
}
當然還有另一箇中方法.系統提供了一個加載image的回調,可以自定義回調處理函數來接收系統記載的image.
static void _rebind_symbols_for_image(const struct mach_header *header,
intptr_t slide) {
mach_header_t *mach_header = (mach_header_t *)header;
Dl_info info;
dladdr(mach_header, &info);
NSLog(@"path:%s, base address=0x%06lx", info.dli_fname, (uintptr_t)info.dli_fbase);
}
# 然後在需要的地方註冊回調函數.這個方法無論你在哪裏註冊都會回調,而不一定是要在image加載之前註冊
_dyld_register_func_for_add_image(&_rebind_symbols_for_image);
- 獲取Mach-O文件中的所有Load Command,並找出LC_SEGMENT(__LINKEDIT),LC_SYMTAB,LC_DYSYMTAB以及LC_UUID,並輸出UUID的值.
static void _rebind_symbols_for_image(const struct mach_header *header,
intptr_t slide) {
uintptr_t cur = (uintptr_t)header + sizeof(mach_header_t);
/* 或者使用
uintptr_t cur = (uintptr_t)((mach_header_t *)header + 1);
*/
struct load_command *cur_cmd;
segment_command_t *linkedit_segment = NULL;
struct symtab_command* symtab_cmd = NULL;
struct dysymtab_command* dysymtab_cmd = NULL;
struct uuid_command *uuid_command = NULL;
for(int i = 0; i < header->ncmds; i++, cur += cur_cmd->cmdsize) {
cur_cmd = (struct load_command *)cur;
if (cur_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) {
//當前load_command是一個Segment
segment_command_t *seg = (segment_command_t *)cur_cmd;
if (strcmp(seg->segname, SEG_LINKEDIT) == 0) {
//找到LC_SEGMENT(__LINKEDIT)
linkedit_segment = seg;
}
} else if (cur_cmd->cmd == LC_SYMTAB) {
symtab_cmd = (struct symtab_command*)cur_cmd;
} else if (cur_cmd->cmd == LC_DYSYMTAB) {
dysymtab_cmd = (struct dysymtab_command*)cur_cmd;
} else if (cur_cmd->cmd == LC_UUID) {
uuid_command = (struct uuid_command *)cur_cmd;
}
}
if (linkedit_segment != NULL) {
NSLog(@"find linkedit_segment");
}
if (symtab_cmd != NULL) {
NSLog(@"find symtab_cmd");
}
if (dysymtab_cmd != NULL) {
NSLog(@"find dysymtab_cmd");
}
if (uuid_command != NULL) {
uint8_t *command = uuid_command->uuid;
NSString *uuid = [NSString stringWithFormat:@"%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", command[0], command[1], command[2], command[3], command[4], command[5], command[6], command[7], command[8], command[9], command[10], command[11], command[12], command[13], command[14], command[15]];
NSLog(@"uuid == %@", uuid);
}
}
- 如何獲取在指定表內存中的地址的地址
在結構體struct segment_command(struct segment_command_64)中,有一個vmsize參數,這個參數就是在沒有ASLR技術的情況下對應結構被記載到內存中的地址.所以在有ASLR技術時,只需要加上對應target module的偏移量就slide就是對應的應用內存地址,只需要再減去文件的fileoff就是加載文件的起始地址.其他文件都是在這個地址基礎上加上fileoff就可以獲取到.
例如選擇__DATA,LC_SEGMENT(__LINKEDIT) 計算起始地址
//獲取起始地址
uintptr_t baseptr = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff;
//獲取符號表的地址
nlist_t *sym_tab = (nlist_t *)(baseptr + symbol_segment->symoff);
//獲取字符表地址
char *str_tab = (char *)(baseptr + symbol_segment->stroff);
//重定向表的地址
uint32_t *indirect_table = (uint32_t *)(baseptr + dysymbol_segement->indirectsymoff);
源碼解析
- 註冊交換符號實現的方法
//註冊重新綁定符號的回調時機
int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel) {
//將需要hook的結構體鏈接成鏈表結構(之所以使用鏈表是因爲在C語言中,處理這種預先不知道可能有多少個的集合來講,鏈表是比較好的選擇)
int retval = prepend_rebindings(&_rebindings_head, rebindings, rebindings_nel);
//此處的異常主要用來處理malloc開闢空間失敗
if (retval < 0) {
return retval;
}
// If this was the first call, register callback for image additions (which is also invoked for
// existing images, otherwise, just run on existing images
/*
如果是第一次調用重新綁定符號的方法,就通過註冊添加image(target module)的方法回調來重新綁定符號(這個方法在任何地方註冊都會執行加載image的回調);
如果不是第一次調用重新綁定符號的方法,就通過遍歷當前已經加載的image來循環綁定符號(如果在此操作執行之後,又重新加載了image,可以通過_dyld_register_func_for_add_image來監聽到);
*/
if (!_rebindings_head->next) {
_dyld_register_func_for_add_image(_rebind_symbols_for_image);
} else {
uint32_t c = _dyld_image_count();
for (uint32_t i = 0; i < c; i++) {
_rebind_symbols_for_image(_dyld_get_image_header(i), _dyld_get_image_vmaddr_slide(i));
}
}
return retval;
}
//在方法內部調用rebind_symbols_for_image實現
static void _rebind_symbols_for_image(const struct mach_header *header,
intptr_t slide) {
rebind_symbols_for_image(_rebindings_head, header, slide);
}
- 獲取鏈接表,符號表,動態符號表,重定向表在內存中的地址,獲取__la_symbol_ptr以及__ln_symbol_ptr對應的section;
static void rebind_symbols_for_image(struct rebindings_entry *rebindings,
const struct mach_header *header,
intptr_t slide) {
Dl_info info;
if (dladdr(header, &info) == 0) {
return;
}
segment_command_t *cur_seg_cmd;
segment_command_t *linkedit_segment = NULL;
struct symtab_command* symtab_cmd = NULL;
struct dysymtab_command* dysymtab_cmd = NULL;
//第一次遍歷load Commands主要是爲了找到鏈接表linkedit_segment,符號表(symtab_cmd),動態符號表(dysymtab_cmd)對應的load command
uintptr_t cur = (uintptr_t)header + sizeof(mach_header_t);//使用uintptr_t類型主要是爲了方便按照字節爲單位移動指針的位置
for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) {
cur_seg_cmd = (segment_command_t *)cur;
if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) {
if (strcmp(cur_seg_cmd->segname, SEG_LINKEDIT) == 0) {
linkedit_segment = cur_seg_cmd;
}
} else if (cur_seg_cmd->cmd == LC_SYMTAB) {
symtab_cmd = (struct symtab_command*)cur_seg_cmd;
} else if (cur_seg_cmd->cmd == LC_DYSYMTAB) {
dysymtab_cmd = (struct dysymtab_command*)cur_seg_cmd;
}
}
if (!symtab_cmd || !dysymtab_cmd || !linkedit_segment ||
!dysymtab_cmd->nindirectsyms) {
return;
}
// 通過使用__LINKEDIT的起始地址找到symbol table/string table在內存中的地址
uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff;
nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff);
char *strtab = (char *)(linkedit_base + symtab_cmd->stroff); //使用char *類型是因爲字符在內存中按照char爲單位進行存儲
// 獲取重定向表在內存中的位置
uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff); //使用uint32_t *是因爲重定向符號表中數據按照4字節爲單位進行存儲
cur = (uintptr_t)header + sizeof(mach_header_t);
for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) {
cur_seg_cmd = (segment_command_t *)cur;
if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) {
//找到DATA和DATA_CONST segment,並跳過
if (strcmp(cur_seg_cmd->segname, SEG_DATA) != 0 &&
strcmp(cur_seg_cmd->segname, SEG_DATA_CONST) != 0) {
continue;
}
////找到__nl_symbol_ptr和__la_symbol_ptr這兩個section
for (uint j = 0; j < cur_seg_cmd->nsects; j++) {
section_t *sect =
(section_t *)(cur + sizeof(segment_command_t)) + j;
if ((sect->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS) {
//遍歷懶加載符號表中的符號,與_rebindings_head進行比較,對匹配的符號進行重新綁定
perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab);
}
if ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) {
//遍歷非懶加載符號表中的符號,與_rebindings_head進行比較,對匹配的符號進行重新綁定
perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab);
}
}
}
}
}
- 查找並替換_rebindings_head中需要替換的符號
static void perform_rebinding_with_section(struct rebindings_entry *rebindings,
section_t *section,
intptr_t slide,
nlist_t *symtab,
char *strtab,
uint32_t *indirect_symtab) {
//section在indirect table中的索引從reserved1處開始
uint32_t *indirect_symbol_indices = indirect_symtab + section->reserved1;
//強制類型轉化z,主要是說明給地址處存儲的是還是地址(即指針處內存存儲的值依然是指針)
void **indirect_symbol_bindings = (void **)((uintptr_t)slide + section->addr);
for (uint i = 0; i < section->size / sizeof(void *); i++) {
//獲取indirext table中對應索引處的值,該值表示在symbole table中的索引
uint32_t symtab_index = indirect_symbol_indices[i];
//如果該索引表示的符號是未綁定前的符號或者本地符號,則不進行任何操作
if (symtab_index == INDIRECT_SYMBOL_ABS || symtab_index == INDIRECT_SYMBOL_LOCAL ||
symtab_index == (INDIRECT_SYMBOL_LOCAL | INDIRECT_SYMBOL_ABS)) {
continue;
}
//獲取在string table中偏移
uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx;
//獲取在string table中的地址
char *symbol_name = strtab + strtab_offset;
struct rebindings_entry *cur = rebindings;
//遍歷找到需要重新綁定的符號,進行重新綁定
while (cur) {
for (uint j = 0; j < cur->rebindings_nel; j++) {
if (strlen(symbol_name) > 1 &&
strcmp(&symbol_name[1], cur->rebindings[j].name) == 0) {
//防止重複綁定
if (cur->rebindings[j].replaced != NULL &&
indirect_symbol_bindings[i] != cur->rebindings[j].replacement) {
//如果符號對應的實現不是自定義實現,說明符號沒有還重新綁定,將符號對應的真實實現存入cur->rebindings[j].replaced對應位置
*(cur->rebindings[j].replaced) = indirect_symbol_bindings[i];
}
//將自定義實現,存入符號對應的位置
indirect_symbol_bindings[i] = cur->rebindings[j].replacement;
//非常古老的C語言跳轉指令,現在很少用
goto symbol_loop;
}
}
cur = cur->next;
}
symbol_loop:;
}
}