1.探索macho文件
1.1macho簡介
Mach-O(Mach Object)是macOS、iOS、iPadOS存儲(chǔ)程序和庫的文件格 式。對應(yīng)系統(tǒng)通過應(yīng)用二進(jìn)制接口(application binary interface,縮寫為 ABI)來運(yùn)行該格式的文件。
Mach-O格式用來替代BSD系統(tǒng)的a.out格式。Mach-O文件格式保存了在 編譯過程和鏈接過程中產(chǎn)生的機(jī)器代碼和數(shù)據(jù),從而為靜態(tài)鏈接和動(dòng)態(tài) 鏈接的代碼提供了單一文件格式。
可執(zhí)行文件調(diào)用過程
- 調(diào)用
fork函數(shù),創(chuàng)建一個(gè)process - 調(diào)用
execve或其衍生函數(shù),在該進(jìn)程上加載,執(zhí)行我們的Mach-O文件
當(dāng)我們調(diào)用時(shí)execve(程序加載器),內(nèi)核實(shí)際上在執(zhí)行以下操作: - 將文件加載到內(nèi)存
- 開始分析
Mach-O中的mach_header,以確認(rèn)它是有效的Mach-O文件
1.2查看macho的頭文件
objdump --macho --private-header macho文件
objdump --macho --private-header PayDemo
//輸出結(jié)果
Mach header
magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
MH_MAGIC_64 ARM64 ALL 0x00 EXECUTE 30 4120 NOUNDEFS DYLDLINK TWOLEVEL BINDS_TO_WEAK PIE
otool -h PayDemo //otool -h命令也可以讀出
macho文件的結(jié)構(gòu)

1.2macho中的代碼段
int main(int argc, char *argv[]) {
return 0;
}
通過命令
objdump --macho -d 查看
objdump --macho -d macho文件
100003fa0: 55 pushq %rbp
100003fa1: 48 89 e5 movq %rsp, %rbp
100003fa4: 31 c0 xorl %eax, %eax
100003fa6: c7 45 fc 00 00 00 00 movl $0, -4(%rbp)
100003fad: 89 7d f8 movl %edi, -8(%rbp)
100003fb0: 48 89 75 f0 movq %rsi, -16(%rbp)
100003fb4: 5d popq %rbp
100003fb5: c3 retq
2.符號表
2.1符號表Symbol Table
Symbol Table:就是用來保存符號。
String Table:就是用來保存符號的名稱。
Indirect Symbol Table:間接符號表。保存使用的外部符號。更準(zhǔn)確一點(diǎn)就是使 用的外部動(dòng)態(tài)庫的符號。是Symbol Table的子集。
2.2全局符號和本地符號
我們在main函數(shù)上定義一個(gè)全局變量和一個(gè)靜態(tài)變量
#import <Foundation/Foundation.h>
int global_init_value = 10;
static int static_init_value = 9;
int main(int argc, char *argv[]) {
return 0;
}
通過objdump命令查看macho的符號表
objdump --macho --syms
0000000100003fa0 l d *UND*
0000000100003fa0 l d *UND* _main
0000000000000016 l d *UND*
0000000000000016 l d *UND*
0000000000000000 l d *UND* _global_init_value
0000000000000000 l d *UND*
0000000100000000 g F __TEXT,__text __mh_execute_header
0000000100004008 g O __DATA,__data _global_init_value
0000000100003fa0 g F __TEXT,__text _main
可以看出全局變量是全局符號,靜態(tài)全局變量是本地符號
怎么把全局符號變成本地符號呢
//定義成全局變量
extern int hidden_y;
extern double default_y;
extern double protected_y;
int hidden_y __attribute__((visibility("hidden"))) = 99; //全局符號便成本地符號
double default_y __attribute__((visibility("default"))) = 100;//如果 //還是全局符號
visibility屬性,控制文件導(dǎo)出符號,限制符號可見性
-fvisibility:clang參數(shù)
default:用它定義的符號將被導(dǎo)出。
hidden:用它定義的符號將不被導(dǎo)出。
全局符號對整個(gè)項(xiàng)目可見
如果我們在一個(gè)庫的m文件中實(shí)現(xiàn)一個(gè)全局的global_object函數(shù),我們可以在項(xiàng)目中通過extern void global_object()之后正常使用,但是如果我們項(xiàng)目中也實(shí)現(xiàn)global_object函數(shù),代碼會(huì)優(yōu)先調(diào)用本項(xiàng)目中的global_object方法,這里涉及到命名空間
two_levelnamespace & flat_namespace:
二級命名空間與一級命名空間。鏈接器默認(rèn)采用二級命名空間,也就是除了會(huì)記錄符號 名稱,還會(huì)記錄符號屬于哪個(gè)Mach-O的,比如會(huì)記錄下來_NSLog來自Foundation。
2.3導(dǎo)出符號導(dǎo)出符號
我們在main中使用NSLog,可知NSLog對于Foundation是導(dǎo)出符號,NSLog對于我們的項(xiàng)目是導(dǎo)入符號
我們看一下我們自己的macho文件的導(dǎo)出符號
objdump --macho --exports-trie
//打印結(jié)果
Exports trie:
0x100000000 __mh_execute_header
0x100003F40 _global_object
0x100004048 _global_init_value
0x100003F60 _main
0x100004040 _default_y
我們可以看出我們的導(dǎo)出符號也就是我們的全局符號。
2.4間接符號表
間接符號表是我們使用的其他動(dòng)態(tài)庫的符號。
查看間接符號表
objdump --macho --indirect-symbols
//打印結(jié)果
Indirect symbols for (__TEXT,__stubs) 1 entries
address index name
0x0000000100003f86 23 _NSLog
Indirect symbols for (__DATA,__nl_symbol_ptr) 1 entries
address index name
0x0000000100004000 ABSOLUTE
Indirect symbols for (__DATA,__got) 1 entries
address index name
0x0000000100004008 25 dyld_stub_binder
Indirect symbols for (__DATA,__la_symbol_ptr) 1 entries
address index name
0x0000000100004010 23 _NSLog
我們strip的話,剝離本地符號,全局符號也就是導(dǎo)出符號不能被剝離。
2.5OC類是導(dǎo)出符號
我們創(chuàng)建OC類,查看OC類和方法是本地符號還是全局符號(導(dǎo)出符號)
objdump --macho --exports-trie

我們創(chuàng)建的oc類都是導(dǎo)出符號,如果我們不想讓我們的類作為導(dǎo)出符號被外界使用我們應(yīng)該怎么辦呢?
2.6修改link配置,隱藏導(dǎo)出符號
配置other link flag參數(shù)如下
OTHER_LDFLAGS=$(inherited) -Xlinker -unexported_symbol -Xlinker _OBJC_CLASS_$_LGOneObject
OTHER_LDFLAGS=$(inherited) -Xlinker -unexported_symbol -Xlinker _OBJC_METACLASS_$_LGOneObject
重新查看導(dǎo)出符號
objdump --macho --exports-trie
Exports trie:
0x100000000 __mh_execute_header
0x100003F10 _global_object
0x100004170 _global_init_value
0x100003F30 _main
0x100004168 _default_y
可以看出我們的隱藏了導(dǎo)出符號OBJC_METACLASS_LGOneObject
2.7重定位符號表
重定位符號就是我們在調(diào)用別人的api時(shí),在編譯的時(shí)候需要記錄一下放到重定位符號表中。
查看目標(biāo)文件o的重定位
objdump --macho --reloc test.o
//打印結(jié)果
test.o:
Relocation information (__TEXT,__text) 2 entries
address pcrel length extern type scattered symbolnum/value
0000001c True long True BRANCH False _NSLog
0000000b True long False SIGNED False 3 (__DATA,__cfstring)
Relocation information (__DATA,__cfstring) 2 entries
address pcrel length extern type scattered symbolnum/value
00000010 False quad False UNSIGND False 2 (__TEXT,__cstring)
00000000 False quad True UNSIGND False ___CFConstantStringClassReference
Relocation information (__LD,__compact_unwind) 1 entries
address pcrel length extern type scattered symbolnum/value
00000000 False quad False UNSIGND False 1 (__TEXT,__text)

怎么生成目標(biāo)文件可以在靜態(tài)庫查看
3.靜態(tài)鏈接
