CDClassDump 這個(gè)文件是class-dump的一部分,用于檢查Mach-O文件的Objective-C segment
getopt_long()類似于getopt()都是解析命令行參數(shù)函數(shù),只是getopt用于處理單字母,而getopt_long用于處理長選項(xiàng)。解析出可執(zhí)行路徑之后設(shè)置到classDump.searchPathState.executablePath,然后將路徑文件轉(zhuǎn)成NSData,根據(jù)魔數(shù)判斷可執(zhí)行是否為fat文件,fat文件則需要根據(jù)CDDataCursor拆分出每一個(gè)CDFatArch架構(gòu)文件加入到arches數(shù)組。否則為單一架構(gòu)的CDMachOFile文件,根據(jù)魔數(shù)判斷是大端還是小端以便確定后續(xù)解析規(guī)則。根據(jù)Mach Header4個(gè)字節(jié)的偏移和大小端可以將ncmds,flags等解析出來,接下來就是解析loadCommand:
- (void)_readLoadCommands:(CDMachOFileDataCursor *)cursor count:(uint32_t)count {
for (uint32_t index = 0; index < count; index++) {
//根據(jù)游標(biāo)的首個(gè)32位確定coamnd的類型,每一個(gè)case對應(yīng)一個(gè)類,這個(gè)類統(tǒng)一繼承自CDLoadCommand
CDLoadCommand *loadCommand = [CDLoadCommand loadCommandWithDataCursor:cursor];
if (loadCommand != nil) {
[loadCommands addObject:loadCommand];
//收集部分屬性,如版本等
if (loadCommand.cmd == LC_VERSION_MIN_MACOSX) self.minVersionMacOSX = (CDLCVersionMinimum *)loadCommand;
if (loadCommand.cmd == LC_VERSION_MIN_IPHONEOS) self.minVersionIOS = (CDLCVersionMinimum *)loadCommand;
//XXX
//設(shè)置segments,符號表等
if ([loadCommand isKindOfClass:[CDLCSourceVersion class]]) self.sourceVersion = (CDLCSourceVersion *)loadCommand;
//XXX
else if ([loadCommand isKindOfClass:[CDLCRunPath class]]) {
[runPaths addObject:[(CDLCRunPath *)loadCommand resolvedRunPath]];
[runPathCommands addObject:loadCommand];
}
}
}
//可以自定義讀取的操作,如輸出dyld bind階段的cocode與立即數(shù):通過 byte & 0xF0 得到 opcode,byte & 0x0F 得到 immediate(立即數(shù)),根據(jù)操作數(shù)(opcode)進(jìn)行分支處理,具體含義如下:
for (CDLoadCommand *loadCommand in _loadCommands) {
[loadCommand machOFileDidReadLoadCommands:self];
}
}
0x00 REBASE_OPCODE_DONE
rebasing 結(jié)束標(biāo)志
0x10 REBASE_OPCODE_SET_TYPE_IMM
立即數(shù)(immediate)設(shè)置為 type,分為以下類型:
REBASE_TYPE_POINTER 1
REBASE_TYPE_TEXT_ABSOLUTE32 2
REBASE_TYPE_TEXT_PCREL32 3
0x20 REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
立即數(shù)(immediate)設(shè)置為當(dāng)前上下文的指向 segment 索引,從而計(jì)算出當(dāng)前 segment 首地址 segmentStartAddress
當(dāng)前 byte 后的數(shù)據(jù)為 ULEB128 字節(jié)流的值,解碼為相對 segmentStartAddress 的偏移,從而計(jì)算出操作地址 address
0x30 REBASE_OPCODE_ADD_ADDR_ULEB
操作地址 address 向后移動(dòng) ULEB128 數(shù)據(jù)對應(yīng)的值,即 address += read_uleb128(p, end);
0x40 REBASE_OPCODE_ADD_ADDR_IMM_SCALED
操作地址 address 向后移動(dòng)立即數(shù)(immediate)倍數(shù)的指針寬度,即 address += immediate*sizeof(uintptr_t);
0x50 REBASE_OPCODE_DO_REBASE_IMM_TIMES
將立即數(shù)(immediate)作為操作(循環(huán))次數(shù),依次將當(dāng)前操作地址 address 對應(yīng)的值進(jìn)行 rebasing,即,將內(nèi)部的值加上 slide 偏移
每次循環(huán)后操作地址 address 向后移動(dòng)指針寬度的字節(jié),進(jìn)入下一個(gè)需要 rebase 的地址
0x60 REBASE_OPCODE_DO_REBASE_ULEB_TIMES
與上一個(gè) 0x50 值相似,唯一不同點(diǎn)就是立即數(shù)的值替換為 ULEB128 的值進(jìn)行循環(huán)操作,這意為著需要 rebase 的地址超過了 4 位數(shù)能表示的最大值,即超過 16(0x0F)個(gè).
0x70 REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB
根據(jù)上下文數(shù)據(jù)執(zhí)行 rebase 操作
隨后操作地址 address 向后移動(dòng),偏移值為 ULEB128 加一個(gè)指針寬度的值,即 address += read_uleb128(p, end) + sizeof(uintptr_t);
0x80 REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB
連續(xù)讀取兩個(gè) ULEB128 值,依次為循環(huán)次數(shù) count 和跳過的字節(jié)數(shù) skip
執(zhí)行循環(huán),根據(jù)之前得出的上下文數(shù)據(jù)執(zhí)行 rebasing
操作地址 address 向后移動(dòng) skip 加指針寬度的偏移量,即 address += skip + sizeof(uintptr_t);
0x00 BIND_OPCODE_DONE
binding 結(jié)束標(biāo)志
0x10 BIND_OPCODE_SET_DYLIB_ORDINAL_IMM
立即數(shù)(immediate)設(shè)置為依賴庫索引 Ordinal,即 Load command 中的 LC_LOAD_DYLIB 按順序排列的庫,
0x20 BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB
將隨后的 ULEB128 值設(shè)置為依賴庫索引 Ordinal
0x30 BIND_OPCODE_SET_DYLIB_SPECIAL_IMM
根據(jù)立即數(shù)計(jì)算索引 Ordinal
0x0為self,0xf(-1)為main executable,0xe(-2)為flat lookup。
0x40 BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
從 byte 后獲取以 \0 結(jié)尾的符號名字符串
立即數(shù)作為符號的標(biāo)志(flag)
0x50 BIND_OPCODE_SET_TYPE_IMM
立即數(shù)(immediate)設(shè)置為 type,分為以下類型:
BIND_TYPE_POINTER 1
BIND_TYPE_TEXT_ABSOLUTE32 2
BIND_TYPE_TEXT_PCREL32 3
0x60 BIND_OPCODE_SET_ADDEND_SLEB
設(shè)置上下文的加數(shù)(addend)為隨后的 SLEB128 值
0x70 BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
立即數(shù)(immediate)設(shè)置為當(dāng)前上下文的 segment 索引,從而計(jì)算出當(dāng)前 segment 首地址 segmentStartAddress
將隨后的 ULEB128 字節(jié)流的值作為 segmentStartAddress 的偏移,從而計(jì)算出操作地址 address
0x80 BIND_OPCODE_ADD_ADDR_ULEB
操作地址 address 向后移動(dòng) ULEB128 數(shù)據(jù)對應(yīng)的值,即 address += read_uleb128(p, end);
0x90 BIND_OPCODE_DO_BIND
利用之前計(jì)算的上下文數(shù)據(jù)執(zhí)行 binding
操作地址 address 向后移動(dòng)一個(gè)指針寬度
0xA0 BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB
利用之前計(jì)算的上下文數(shù)據(jù)執(zhí)行 binding
操作地址 address 向后移動(dòng) ULEB128 的值加一個(gè)指針寬度
0xB0 BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB
利用之前計(jì)算的上下文數(shù)據(jù)執(zhí)行 binding
操作地址 address 向后移動(dòng)立即數(shù)倍數(shù)的指針寬度(immediate*sizeof(intptr_t))再加一個(gè)指針寬度
0xC0 BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB
連續(xù)讀取兩個(gè) ULEB128 值,先后為循環(huán)次數(shù) count 和跳過的字節(jié)數(shù) skip
執(zhí)行循環(huán),根據(jù)上下文數(shù)據(jù)執(zhí)行 binding 操作
操作地址 address 向后移動(dòng) skip 加指針寬度的偏移量,即 address += skip + sizeof(uintptr_t);
- (void)logBindOps:(const uint8_t *)start end:(const uint8_t *)end isLazy:(BOOL)isLazy;
{
//根據(jù)opcode的類型,如此處對binding的符號以符號地址作為key,符號名作為value,進(jìn)行收集以便后面對dyld符號進(jìn)行使用和設(shè)置
case BIND_OPCODE_DO_BIND:
if (debugBindOps) NSLog(@"BIND_OPCODE: DO_BIND");
[self bindAddress:address type:type symbolName:symbolName flags:symbolFlags addend:addend libraryOrdinal:libraryOrdinal];
address += _ptrSize;
bindCount++;
break;
}
- (void)bindAddress:(uint64_t)address type:(uint8_t)type symbolName:(const char *)symbolName flags:(uint8_t)flags
addend:(int64_t)addend libraryOrdinal:(int64_t)libraryOrdinal;
{
NSNumber *key = [NSNumber numberWithUnsignedInteger:address]; // I don't think 32-bit will dump 64-bit stuff.
NSString *str = [[NSString alloc] initWithUTF8String:symbolName];
_symbolNamesByAddress[key] = str;
}
section獲?。?/p>
在loadCommandWithDataCursor方法中通過segment類型得到對應(yīng)的子類實(shí)現(xiàn),如0x19對應(yīng)的LC_SEGMENT_64:CDLCSegment即為section header的獲?。?/p>
- (id)initWithDataCursor:(CDMachOFileDataCursor *)cursor;
{
//根據(jù)游標(biāo)位置獲取對應(yīng)section header所在的偏移,然后依次設(shè)置header信息
if ((self = [super initWithDataCursor:cursor])) {
_segmentCommand.cmd = [cursor readInt32];
_segmentCommand.cmdsize = [cursor readInt32];
_name = [cursor readStringOfLength:16 encoding:NSASCIIStringEncoding];
size_t nameLength = [_name lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
memcpy(_segmentCommand.segname, [_name UTF8String], MIN(sizeof(_segmentCommand.segname), nameLength));
_segmentCommand.vmaddr = [cursor readPtr];
_segmentCommand.vmsize = [cursor readPtr];
_segmentCommand.fileoff = [cursor readPtr];
_segmentCommand.filesize = [cursor readPtr];
_segmentCommand.maxprot = [cursor readInt32];
_segmentCommand.initprot = [cursor readInt32];
_segmentCommand.nsects = [cursor readInt32];
_segmentCommand.flags = [cursor readInt32];
//讀取number of sections個(gè)數(shù),依次設(shè)置__TEXT, __DATA等segment下的具體section header,原理相同,具體數(shù)據(jù)結(jié)構(gòu)參考header的addr,size,offset等長度進(jìn)行解析
NSMutableArray *sections = [[NSMutableArray alloc] init];
for (NSUInteger index = 0; index < _segmentCommand.nsects; index++) {
CDSection *section = [[CDSection alloc] initWithDataCursor:cursor segment:self];
[sections addObject:section];
}
//把segment的nsects對應(yīng)的section header解析出來之后,和當(dāng)前segment進(jìn)行一個(gè)綁定,但此時(shí)真正的section data為空
_sections = [sections copy];
}
return self;
}
經(jīng)過這一步結(jié)束,那一個(gè)CDMachOFile可執(zhí)行的結(jié)構(gòu)已經(jīng)搭建出來了,里面包含mach header對應(yīng)的數(shù)據(jù),load command數(shù)據(jù),segment和對應(yīng)section的頭等。
處理OC數(shù)據(jù):
- (void)processObjectiveCData;
{
for (CDMachOFile *machOFile in self.machOFiles) {
//processorClass首先會(huì)根據(jù)是否存在__objc_imageinfo section來決定使用不同的處理方式CDObjectiveC2Processor,__objc_imageinfo節(jié)可以看作是用于區(qū)分Objective-C 1.0與2.0,舊版是沒有這個(gè)節(jié)的
CDObjectiveCProcessor *processor = [[[machOFile processorClass] alloc] initWithMachOFile:machOFile];
[processor process];
[_objcProcessors addObject:processor];
}
}
- (void)process;
{
//根據(jù)是否存在CDLCEncryptionInfo加密段以及狀態(tài)進(jìn)行判斷能否導(dǎo)出
if (self.machOFile.isEncrypted == NO && self.machOFile.canDecryptAllSegments) {
[self.machOFile.symbolTable loadSymbols];
[self.machOFile.dynamicSymbolTable loadSymbols];
[self loadProtocols];
[self.protocolUniquer createUniquedProtocols];
// Load classes before categories, so we can get a dictionary of classes by address.
[self loadClasses];
[self loadCategories];
}
}
加載符號loadSymbols,分為靜態(tài)符號和動(dòng)態(tài)符號兩步,先看靜態(tài)符號表的處理:
- (void)loadSymbols;
{
for (CDLoadCommand *loadCommand in [self.machOFile loadCommands]) {
if ([loadCommand isKindOfClass:[CDLCSegment class]]) {
CDLCSegment *segment = (CDLCSegment *)loadCommand;
//每一個(gè) segment 的 VP (Virtual Page) 都根據(jù) initprot 進(jìn)行初始化,initprot 指定了如何通過讀/寫/執(zhí)行位初始化頁面的保護(hù)級別(4=r,2=w,1=x)
if (([segment initprot] & CD_VM_PROT_RW) == CD_VM_PROT_RW) {
//可讀可寫存在于__DATA段
//NSLog(@"segment... initprot = %08x, addr= %016lx *** r/w", [segment initprot], [segment vmaddr]);
_baseAddress = [segment vmaddr];
_flags.didFindBaseAddress = YES;
break;
}
}
}
NSMutableArray *symbols = [[NSMutableArray alloc] init];
NSMutableDictionary *classSymbols = [[NSMutableDictionary alloc] init];
NSMutableDictionary *externalClassSymbols = [[NSMutableDictionary alloc] init];
CDMachOFileDataCursor *cursor = [[CDMachOFileDataCursor alloc] initWithFile:self.machOFile offset:_symtabCommand.symoff];
//NSLog(@"offset= %lu", [cursor offset]);
//NSLog(@"stroff= %lu", symtabCommand.stroff);
//NSLog(@"strsize= %lu", symtabCommand.strsize);
//根據(jù)symtabCommand.stroff找到字符串表的起始位置([self.machOFile.data bytes] 即為可執(zhí)行的起始偏移對應(yīng)的指針地址)
const char *strtab = (char *)[self.machOFile.data bytes] + _symtabCommand.stroff;
void (^addSymbol)(NSString *, CDSymbol *) = ^(NSString *name, CDSymbol *symbol) {
[symbols addObject:symbol];
//根據(jù)_OBJC_CLASS_$_獲取對應(yīng)的className
NSString *className = [CDSymbol classNameFromSymbolName:symbol.name];
if (className != nil) {
//根據(jù)地址是否存在判斷符號是否為外部符號
if (symbol.value != 0)
classSymbols[className] = symbol;
else
externalClassSymbols[className] = symbol;
}
};
if (![self.machOFile uses64BitABI]) {
//XXX
} else {
/*
遍歷符號表讀取nlist,符號的數(shù)據(jù)結(jié)構(gòu)如下:
struct nlist_64 {
union {
uint32_t n_strx; /在 string table 中的索引/
} n_un;
uint8_t n_type; 符號類型,8bit的復(fù)合字段
uint8_t n_sect; 符號所在的 section index(內(nèi)部符號有效值從 1 開始,最大為 255)
uint16_t n_desc; 用來標(biāo)識重定義符的特性,比如是否lazy bind
uint64_t n_value; 符號的地址值(在鏈接過程中,會(huì)隨著其 section 發(fā)生變化)
};
**/
for (uint32_t index = 0; index < _symtabCommand.nsyms; index++) {
struct nlist_64 nlist;
nlist.n_un.n_strx = [cursor readInt32];
nlist.n_type = [cursor readByte];
nlist.n_sect = [cursor readByte];
nlist.n_desc = [cursor readInt16];
nlist.n_value = [cursor readInt64];
//nlist.n_un.n_strx即為字符串表的下標(biāo),那么拿到strtab加上所在的下標(biāo)即為符號名
const char *ptr = strtab + nlist.n_un.n_strx;
NSString *str = [[NSString alloc] initWithBytes:ptr length:strlen(ptr) encoding:NSASCIIStringEncoding];
CDSymbol *symbol = [[CDSymbol alloc] initWithName:str machOFile:self.machOFile nlist64:nlist];
//將字符串和符號對象進(jìn)行綁定,回到上一步的block實(shí)現(xiàn)
addSymbol(str, symbol);
}
//NSLog(@"Loaded %lu 64-bit symbols", [symbols count]);
}
// 最后加入到符號數(shù)組中
_symbols = [symbols copy];
_classSymbols = [classSymbols copy];
_externalClassSymbols = [externalClassSymbols copy];
//NSLog(@"symbols: %@", _symbols);
}
動(dòng)態(tài)符號表解析,先看下dysymtab的定義:
//動(dòng)態(tài)符號
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 */
}
1.ilocalsym、iextdefsym、iundefsym把符號表分為三個(gè)區(qū)域,ilocalsym 本地符號僅用于調(diào)試,iextdefsym可執(zhí)行文件定義的符號,iundefsym可執(zhí)行文件里沒有定義的
2.tocoff,目錄偏移,該內(nèi)容只有在動(dòng)態(tài)分享庫中存在。主要作用是把符號和定義它的模塊對應(yīng)起來。
3.modtaboff:為了支持“模塊”(整個(gè)對象文件)的動(dòng)態(tài)綁定,符號表必須知道文件創(chuàng)建的模塊。該內(nèi)容只有在動(dòng)態(tài)分享庫中存在。
4.extrefsymoff:為了支持動(dòng)態(tài)綁定模塊,每個(gè)模塊都有一個(gè)引用符號表,符號表里存放著每個(gè)模塊所引用的符號(定義的和沒有定義的)。該表指針動(dòng)態(tài)庫中存在。
5.indirectsymoff:如果 section 中有符號指針或者樁(stub),section中的reserved1存放該表的下標(biāo)。間接符號表,只是存放一些32位下標(biāo),這些下標(biāo)執(zhí)行符號表。
6.extreloff:每個(gè)模塊都有一個(gè)重定位外部符號表。僅在動(dòng)態(tài)庫中存在
7.locreloff:重定位本地符號表,由于只是在調(diào)試中用,所以不必增加模塊分組
//重定向?qū)嶓w:
struct relocation_info {
int32_t r_address; /* offset in the section to what is being
relocated */
uint32_t r_symbolnum:24, /* symbol index if r_extern == 1 or section
ordinal if r_extern == 0 */
r_pcrel:1, /* was relocated pc relative already */
r_length:2, /* 0=byte, 1=word, 2=long, 3=quad */
r_extern:1, /* does not include value of sym referenced */
r_type:4; /* if not 0, machine specific relocation type */
};
r_address和r_length字段描述了需要被 relocation 的字節(jié)范圍,其中r_address是相對于 section 的偏移量
r_pcrel表示地址值是 PC 相對地址值
r_extern標(biāo)記該符號是否是外部符號
r_symbolnum,index 值,對于外部符號,它描述了符號在 symbol table 中的位置;如果是內(nèi)部符號,它描述了符號所在的 section 的index
r_type,符號類型
- (void)loadSymbols;
{
NSMutableArray *externalRelocationEntries = [[NSMutableArray alloc] init];
//游標(biāo)置位到外部重定向表的偏移位置
CDMachOFileDataCursor *cursor = [[CDMachOFileDataCursor alloc] initWithFile:self.machOFile offset:_dysymtab.extreloff];
//遍歷外部重定向?qū)嶓w條數(shù)
for (uint32_t index = 0; index < _dysymtab.nextrel; index++) {
struct relocation_info rinfo;
//讀取重定向?qū)嶓w
rinfo.r_address = [cursor readInt32];
uint32_t val = [cursor readInt32];
rinfo.r_symbolnum = val & 0x00ffffff;
rinfo.r_pcrel = (val & 0x01000000) >> 24;
rinfo.r_length = (val & 0x06000000) >> 25;
rinfo.r_extern = (val & 0x08000000) >> 27;
rinfo.r_type = (val & 0xf0000000) >> 28;
CDRelocationInfo *ri = [[CDRelocationInfo alloc] initWithInfo:rinfo];
[externalRelocationEntries addObject:ri];
}
//收集重定向表
_externalRelocationEntries = [externalRelocationEntries copy];
}
加載協(xié)議loadProtocols,通過解析__objc_protolist section:
- (CDOCProtocol *)protocolAtAddress:(uint64_t)address;
{
if (address == 0)
return nil;
//根據(jù)地址從字典中取出CDOCProtocol對象
CDOCProtocol *protocol = [self.protocolUniquer protocolWithAddress:address];
if (protocol == nil) {
//不存在則設(shè)置進(jìn)
protocol = [[CDOCProtocol alloc] init];
[self.protocolUniquer setProtocol:protocol withAddress:address];
//從游標(biāo)位置開始讀取cd_objc2_protocol結(jié)構(gòu)記錄協(xié)議在mach-o中的信息
CDMachOFileDataCursor *cursor = [[CDMachOFileDataCursor alloc] initWithFile:self.machOFile address:address];
NSParameterAssert([cursor offset] != 0);
struct cd_objc2_protocol objc2Protocol;
objc2Protocol.isa = [cursor readPtr];
objc2Protocol.name = [cursor readPtr];
objc2Protocol.protocols = [cursor readPtr];
objc2Protocol.instanceMethods = [cursor readPtr];
objc2Protocol.classMethods = [cursor readPtr];
objc2Protocol.optionalInstanceMethods = [cursor readPtr];
objc2Protocol.optionalClassMethods = [cursor readPtr];
objc2Protocol.instanceProperties = [cursor readPtr];
objc2Protocol.size = [cursor readInt32];
objc2Protocol.flags = [cursor readInt32];
objc2Protocol.extendedMethodTypes = 0;
CDMachOFileDataCursor *extendedMethodTypesCursor = nil;
//8 * ptr + 2 * i32 即cd_objc2_protocol結(jié)構(gòu)體的前10個(gè)屬性大小,大于則表示extendedMethodTypes存在
BOOL hasExtendedMethodTypesField = objc2Protocol.size > 8 * [self.machOFile ptrSize] + 2 * sizeof(uint32_t);
if (hasExtendedMethodTypesField) {
objc2Protocol.extendedMethodTypes = [cursor readPtr];
if (objc2Protocol.extendedMethodTypes != 0) {
extendedMethodTypesCursor = [[CDMachOFileDataCursor alloc] initWithFile:self.machOFile address:objc2Protocol.extendedMethodTypes];
NSParameterAssert([extendedMethodTypesCursor offset] != 0);
}
}
//NSLog(@"----------------------------------------");
//NSLog(@"%016lx %016lx %016lx %016lx", objc2Protocol.isa, objc2Protocol.name, objc2Protocol.protocols, objc2Protocol.instanceMethods);
//NSLog(@"%016lx %016lx %016lx %016lx", objc2Protocol.classMethods, objc2Protocol.optionalInstanceMethods, objc2Protocol.optionalClassMethods, objc2Protocol.instanceProperties);
//根據(jù)所在偏移地址拿到內(nèi)容
NSString *str = [self.machOFile stringAtAddress:objc2Protocol.name];
[protocol setName:str];
if (objc2Protocol.protocols != 0) {
[cursor setAddress:objc2Protocol.protocols];
uint64_t count = [cursor readPtr];
for (uint64_t index = 0; index < count; index++) {
uint64_t val = [cursor readPtr];
CDOCProtocol *anotherProtocol = [self protocolAtAddress:val];
if (anotherProtocol != nil) {
[protocol addProtocol:anotherProtocol];
} else {
NSLog(@"Note: another protocol was nil.");
}
}
}
//instanceMethods,classMethods,optionalInstanceMethods,optionalClassMethods都是指向一個(gè)cd_objc2_list_header結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體的count如果大于0,則表示存在cd_objc2_method具體方法,而這個(gè)結(jié)構(gòu)體即方法name,type,imp的組合。然后根據(jù)地址拿到name,type在文件中的偏移創(chuàng)建CDOCMethod實(shí)例加入到數(shù)組倒序并返回
for (CDOCMethod *method in [self loadMethodsAtAddress:objc2Protocol.instanceMethods extendedMethodTypesCursor:extendedMethodTypesCursor])
[protocol addInstanceMethod:method];
for (CDOCMethod *method in [self loadMethodsAtAddress:objc2Protocol.classMethods extendedMethodTypesCursor:extendedMethodTypesCursor])
[protocol addClassMethod:method];
for (CDOCMethod *method in [self loadMethodsAtAddress:objc2Protocol.optionalInstanceMethods extendedMethodTypesCursor:extendedMethodTypesCursor])
[protocol addOptionalInstanceMethod:method];
for (CDOCMethod *method in [self loadMethodsAtAddress:objc2Protocol.optionalClassMethods extendedMethodTypesCursor:extendedMethodTypesCursor])
[protocol addOptionalClassMethod:method];
//屬性列表實(shí)現(xiàn)類似,不同的是根據(jù)cd_objc2_property結(jié)構(gòu)體name,attributes創(chuàng)建CDOCProperty實(shí)例數(shù)組
for (CDOCProperty *property in [self loadPropertiesAtAddress:objc2Protocol.instanceProperties])
[protocol addProperty:property];
}
return protocol;
}
然后通過createUniquedProtocols方法對字典進(jìn)行排序并更新到_uniqueProtocolsByAddress,并把協(xié)議中的實(shí)例方法和類方法以及屬性進(jìn)行抽取分類。
加載類,loadClasses方法通過讀取__objc_protolist節(jié):
- (void)loadClasses;
{
CDSection *section = [[self.machOFile dataConstSegment] sectionWithName:@"__objc_classlist"];
CDMachOFileDataCursor *cursor = [[CDMachOFileDataCursor alloc] initWithSection:section];
while ([cursor isAtEnd] == NO) {
uint64_t val = [cursor readPtr];
//循環(huán)讀取處理單個(gè)類,調(diào)用在下一個(gè)方法
CDOCClass *aClass = [self loadClassAtAddress:val];
if (aClass != nil) {
//緩存類信息
[self addClass:aClass withAddress:val];
}
}
}
- (CDOCClass *)loadClassAtAddress:(uint64_t)address;
{
if (address == 0)
return nil;
CDOCClass *class = [self classWithAddress:address];
if (class)
return class;
//NSLog(@"%s, address=%016lx", __cmd, address);
CDMachOFileDataCursor *cursor = [[CDMachOFileDataCursor alloc] initWithFile:self.machOFile address:address];
NSParameterAssert([cursor offset] != 0);
//讀取cd_objc2_class結(jié)構(gòu)體,data為指向class_ro_t的指針
struct cd_objc2_class objc2Class;
objc2Class.isa = [cursor readPtr];
objc2Class.superclass = [cursor readPtr];
objc2Class.cache = [cursor readPtr];
objc2Class.vtable = [cursor readPtr];
uint64_t value = [cursor readPtr];
class.isSwiftClass = (value & 0x1) != 0;
objc2Class.data = value & ~7;
objc2Class.reserved1 = [cursor readPtr];
objc2Class.reserved2 = [cursor readPtr];
objc2Class.reserved3 = [cursor readPtr];
//NSLog(@"%016lx %016lx %016lx %016lx", objc2Class.isa, objc2Class.superclass, objc2Class.cache, objc2Class.vtable);
//NSLog(@"%016lx %016lx %016lx %016lx", objc2Class.data, objc2Class.reserved1, objc2Class.reserved2, objc2Class.reserved3);
NSParameterAssert(objc2Class.data != 0);
//將游標(biāo)置為data在可執(zhí)行的偏移并構(gòu)造cd_objc2_class_ro_t,查看可執(zhí)行可得知具體的數(shù)據(jù)位于__objc_const節(jié),而這一個(gè)實(shí)體的條目前2個(gè)偏移數(shù)據(jù)是一個(gè)cd_objc2_list_header結(jié)構(gòu)體,內(nèi)部包含2個(gè)i32的屬性,表示size和count,通過其可以算出總共的方法數(shù)和占用的空間大小
[cursor setAddress:objc2Class.data];
struct cd_objc2_class_ro_t objc2ClassData;
objc2ClassData.flags = [cursor readInt32];
objc2ClassData.instanceStart = [cursor readInt32];
objc2ClassData.instanceSize = [cursor readInt32];
if ([self.machOFile uses64BitABI])
objc2ClassData.reserved = [cursor readInt32];
else
objc2ClassData.reserved = 0;
objc2ClassData.ivarLayout = [cursor readPtr];
objc2ClassData.name = [cursor readPtr];
objc2ClassData.baseMethods = [cursor readPtr];
objc2ClassData.baseProtocols = [cursor readPtr];
objc2ClassData.ivars = [cursor readPtr];
objc2ClassData.weakIvarLayout = [cursor readPtr];
objc2ClassData.baseProperties = [cursor readPtr];
//NSLog(@"%08x %08x %08x %08x", objc2ClassData.flags, objc2ClassData.instanceStart, objc2ClassData.instanceSize, objc2ClassData.reserved);
//NSLog(@"%016lx %016lx %016lx %016lx", objc2ClassData.ivarLayout, objc2ClassData.name, objc2ClassData.baseMethods, objc2ClassData.baseProtocols);
//NSLog(@"%016lx %016lx %016lx %016lx", objc2ClassData.ivars, objc2ClassData.weakIvarLayout, objc2ClassData.baseProperties);
NSString *str = [self.machOFile stringAtAddress:objc2ClassData.name];
//NSLog(@"name = %@", str);
CDOCClass *aClass = [[CDOCClass alloc] init];
[aClass setName:str];
//收集實(shí)例方法,通過cd_objc2_class_ro_t.baseMethods偏移取出cd_objc2_method創(chuàng)建CDOCMethod并加入集合
for (CDOCMethod *method in [self loadMethodsAtAddress:objc2ClassData.baseMethods])
[aClass addInstanceMethod:method];
//根據(jù)ivars的偏移取出cd_objc2_ivar構(gòu)造CDOCInstanceVariable并加入集合
aClass.instanceVariables = [self loadIvarsAtAddress:objc2ClassData.ivars];
{
//根據(jù)類名從之前構(gòu)造的符號映射表中取出符號進(jìn)行設(shè)置
CDSymbol *classSymbol = [[self.machOFile symbolTable] symbolForClassName:str];
if (classSymbol != nil)
aClass.isExported = [classSymbol isExternal];
}
{
uint64_t classNameAddress = address + [self.machOFile ptrSize];
NSString *superClassName = nil;
if ([self.machOFile hasRelocationEntryForAddress2:classNameAddress]) {
//根據(jù)地址從dyldInfo中取出superclassname
superClassName = [self.machOFile externalClassNameForAddress2:classNameAddress];
//NSLog(@"class: got external class name (2): %@", [aClass superClassName]);
} else if ([self.machOFile hasRelocationEntryForAddress:classNameAddress]) {
//否則根據(jù)地址從dynamicSymbolTable中取出superclassname
superClassName = [self.machOFile externalClassNameForAddress:classNameAddress];
//NSLog(@"class: got external class name (1): %@", [aClass superClassName]);
} else if (objc2Class.superclass != 0) {
//如果父類雀食存在而符號無法找到,則最后通過cd_objc2_class的superclass數(shù)據(jù)所在的偏移去重新檢索類關(guān)系
CDOCClass *sc = [self loadClassAtAddress:objc2Class.superclass];
aClass.superClassRef = [[CDOCClassReference alloc] initWithClassObject:sc];
}
if (superClassName) {
//如果父類存在,則找到其對應(yīng)的符號進(jìn)行引用關(guān)系綁定
CDSymbol *superClassSymbol = [[self.machOFile symbolTable] symbolForExternalClassName:superClassName];
if (superClassSymbol)
aClass.superClassRef = [[CDOCClassReference alloc] initWithClassSymbol:superClassSymbol];
else
aClass.superClassRef = [[CDOCClassReference alloc] initWithClassName:superClassName];
}
}
//收集類方法,規(guī)則是先通過isa收集到元類,然后cd_objc2_class.data --> cd_objc2_class_ro_t.baseMethods 最后拿到cd_objc2_list_header.count得到cd_objc2_method構(gòu)造出CDOCMethod數(shù)組
for (CDOCMethod *method in [self loadMethodsOfMetaClassAtAddress:objc2Class.isa])
[aClass addClassMethod:method];
// Process protocols 拿到cd_objc2_class_ro_t.baseProtocols在_uniqueProtocolsByAddress中收集到的協(xié)議
for (CDOCProtocol *protocol in [self.protocolUniquer uniqueProtocolsAtAddresses:[self protocolAddressListAtAddress:objc2ClassData.baseProtocols]])
[aClass addProtocol:protocol];
//cd_objc2_class_ro_t.baseProperties -> cd_objc2_list_header : cd_objc2_property 到屬性列表
for (CDOCProperty *property in [self loadPropertiesAtAddress:objc2ClassData.baseProperties])
[aClass addProperty:property];
return aClass;
}
loadCategories:幾乎與類方法的處理方式一致讀取__objc_catlist節(jié)然后進(jìn)行l(wèi)oadCategoryAtAddress處理,重點(diǎn)在于cd_objc2_category結(jié)構(gòu)體,按8字節(jié)解析之后可以獲取到instanceMethods,classMethods等的偏移,然后得到對應(yīng)的方法:
- (void)loadCategories;
{
CDSection *section = [[self.machOFile dataConstSegment] sectionWithName:@"__objc_catlist"];
CDMachOFileDataCursor *cursor = [[CDMachOFileDataCursor alloc] initWithSection:section];
while ([cursor isAtEnd] == NO) {
CDOCCategory *category = [self loadCategoryAtAddress:[cursor readPtr]];
[self addCategory:category];
}
}
struct cd_objc2_category {
uint64_t name;
uint64_t class;
uint64_t instanceMethods;
uint64_t classMethods;
uint64_t protocols;
uint64_t instanceProperties;
uint64_t v7;
uint64_t v8;
};
最后通過創(chuàng)建Visitor然后遍歷進(jìn)行字符串的拼接輸出到指定路徑:
CDMultiFileVisitor *multiFileVisitor = [[CDMultiFileVisitor alloc] init];
multiFileVisitor.classDump = classDump;
classDump.typeController.delegate = multiFileVisitor;
multiFileVisitor.outputPath = outputPath;
[classDump recursivelyVisit:multiFileVisitor];