Xcode的Link Map File

前言

我們編寫的源碼需要經(jīng)過編譯、鏈接惨奕,最終生成一個(gè)可執(zhí)行文件雪位。在編譯階段,每個(gè)類會生成對應(yīng)的 .o 文件(目標(biāo)文件)梨撞。在鏈接階段雹洗,會把 .o 文件和動(dòng)態(tài)庫鏈接在一起。Link Map File 就是這樣一個(gè)記錄鏈接相關(guān)信息的純文本文件卧波,里面記錄了可執(zhí)行文件的路徑队伟、CPU 架構(gòu)、目標(biāo)文件幽勒、符號等信息嗜侮。

為什么要理解 Link Map File ?

理解 Link Map File 啥容,可以幫助我們:

  • 理解鏈接過程
  • 理解 Crash 時(shí)锈颗,通過 Symbols 還原出源碼的機(jī)制
  • 理解內(nèi)存分段、分區(qū)
  • 分析可執(zhí)行文件中哪個(gè)類或庫占用比較大咪惠,進(jìn)行安裝包瘦身

Link Map File配置

點(diǎn)擊工程击吱,選擇 Build Setting 選項(xiàng),搜索 map 遥昧,可以看到如下界面覆醇。將 Write Link Map File 設(shè)置為 Yes 后,Build結(jié)束后炭臭,會在默認(rèn)路徑下生成一個(gè) Link Map File 文件永脓,該文件是 txt 格式的。點(diǎn)擊 Path to Link Map File 鞋仍,可以設(shè)置 DebugRelease 模式下的生成路徑常摧。

Path & Arch

# Path: /Users/shanggaolin/Library/Developer/Xcode/DerivedData/xxxxxx.app-hjsjojqpqxstlzceepeqbvxqzcrh/Build/Products/Debug-iphonesimulator/xxxxxx.app/xxxxxx
# Arch: x86_64

Path 是可執(zhí)行文件的路徑,Arch 是架構(gòu)類型威创。

Object files

# Object files:
[  0] linker synthesized
[  1] /Users/shanggaolin/Library/Developer/Xcode/DerivedData/NewMissFresh-hjsjojqpqxstlzceepeqbvxqzcrh/Build/Intermediates.noindex/NewMissFresh.build/Debug-iphonesimulator/NewMissFresh.build/NewMissFresh.app-Simulated.xcent
[  2] /Users/shanggaolin/Library/Developer/Xcode/DerivedData/NewMissFresh-hjsjojqpqxstlzceepeqbvxqzcrh/Build/Intermediates.noindex/NewMissFresh.build/Debug-iphonesimulator/NewMissFresh.build/Objects-normal/x86_64/UIButton+SSEdgeInsets.o
[  3] /Users/shanggaolin/Library/Developer/Xcode/DerivedData/NewMissFresh-hjsjojqpqxstlzceepeqbvxqzcrh/Build/Intermediates.noindex/NewMissFresh.build/Debug-iphonesimulator/NewMissFresh.build/Objects-normal/x86_64/MFRankListTableViewDelegate.o

在編譯成目標(biāo)文件后落午,通過鏈接器進(jìn)行鏈接,最終合成可執(zhí)行文件肚豺。這里展示的信息是鏈接時(shí)用到的文件溃斋,包括 .o 文件和 dylib 庫。第一列的序號是類的編號吸申,通過該編號可以對應(yīng)到具體的類梗劫。

在后面的 Symbols 部分寞奸,我們會用到類的編號。

Section區(qū)

# Sections:
# Address   Size        Segment Section
0x100002780 0x0129617D  __TEXT  __text
0x1012988FE 0x000015E4  __TEXT  __stubs
0x101299EE4 0x0000207C  __TEXT  __stub_helper
0x10129BF60 0x0002BE10  __TEXT  __const
0x1012C7D70 0x00097A6D  __TEXT  __objc_methname
0x10135F7DD 0x00010CD5  __TEXT  __objc_classname
0x1013704B2 0x00015F47  __TEXT  __objc_methtype
0x101386400 0x000B6CB2  __TEXT  __cstring
0x10143D0B2 0x00007D60  __TEXT  __ustring
0x101444E14 0x00054EA8  __TEXT  __gcc_except_tab
0x101499CBC 0x000002B0  __TEXT  __entitlements
0x101499F6C 0x00014CA8  __TEXT  __unwind_info
0x1014AEC18 0x0003B3E8  __TEXT  __eh_frame

0x1014EA000 0x00000010  __DATA  __nl_symbol_ptr
0x1014EA010 0x00000BD8  __DATA  __got
0x1014EABE8 0x00001D30  __DATA  __la_symbol_ptr
0x1014EC918 0x00000028  __DATA  __mod_init_func
0x1014EC940 0x00073BB0  __DATA  __const
0x1015604F0 0x00062700  __DATA  __cfstring
0x1015C2BF0 0x00004BD0  __DATA  __objc_classlist
0x1015C77C0 0x00000058  __DATA  __objc_nlclslist
0x1015C7818 0x00000640  __DATA  __objc_catlist
0x1015C7E58 0x00000088  __DATA  __objc_nlcatlist
0x1015C7EE0 0x00001018  __DATA  __objc_protolist
0x1015C8EF8 0x00000008  __DATA  __objc_imageinfo
0x1015C8F00 0x001EE0A0  __DATA  __objc_const
0x1017B6FA0 0x00024A08  __DATA  __objc_selrefs
0x1017DB9A8 0x000000B8  __DATA  __objc_protorefs
0x1017DBA60 0x00004AD8  __DATA  __objc_classrefs
0x1017E0538 0x00002F08  __DATA  __objc_superrefs
0x1017E3440 0x00015A38  __DATA  __objc_ivar
0x1017F8E78 0x0002F670  __DATA  __objc_data
0x1018284F0 0x00024D18  __DATA  __data
0x10184D210 0x00006C74  __DATA  __bss
0x101853E90 0x00000E90  __DATA  __common

第一列是起始位置在跳,第二列是 Section 占用內(nèi)存大小枪萄,第三列是 Segment 類型,第四列是 Section 類型猫妙。
為了理解上面的信息瓷翻,我們需要先補(bǔ)充一點(diǎn) Mach-O 知識。
Mach-O 文件中的虛擬地址最終會映射到物理地址上割坠。這些地址被分成不同的 Segement__TEXT 段齐帚、__DATA 段 和 __LINKEDIT 段。
(1)__TEXT 包含 Mach header 彼哼,被執(zhí)行的代碼和只讀常量(如 C 字符串)对妄,只讀可執(zhí)行(r-x)。
(2)__DATA 包含全局變量敢朱,靜態(tài)變量等剪菱,可讀寫(rw-)。
(3)__LINKEDIT 包含了加載程序的『元數(shù)據(jù)』拴签,比如函數(shù)的名稱和地址孝常,只讀(r–)。
Segement 劃分成了不同的 Section 蚓哩,不同的 Section 存儲著不同的信息构灸,下面是一些常用的 Section 的介紹。

//  __TEXT段中的一些Section
1. __text: 代碼節(jié)岸梨,存放機(jī)器編譯后的代碼
2. __stubs: 用于輔助做動(dòng)態(tài)鏈接代碼(dyld).
3. __stub_helper:用于輔助做動(dòng)態(tài)鏈接(dyld).
4. __objc_methname:objc的方法名稱
5. __cstring:代碼運(yùn)行中包含的字符串常量,比如代碼中定義`#define kGeTuiPushAESKey        @"DWE2#@e2!"`,那DWE2#@e2!會存在這個(gè)區(qū)里喜颁。
6. __objc_classname:objc類名
7. __objc_methtype:objc方法類型
8. __ustring:
9. __gcc_except_tab:
10. __const:存儲const修飾的常量
11. __dof_RACSignal:
12. __dof_RACCompou:
13. __unwind_info:

// __DATA段中的一些Section
1. __got:存儲引用符號的實(shí)際地址,類似于動(dòng)態(tài)符號表
2. __la_symbol_ptr:lazy symbol pointers曹阔。懶加載的函數(shù)指針地址半开。和__stubs和stub_helper配合使用。具體原理暫留次兆。
3. __mod_init_func:模塊初始化的方法稿茉。
4. __const:存儲constant常量的數(shù)據(jù)。比如使用extern導(dǎo)出的const修飾的常量芥炭。
5. __cfstring:使用Core Foundation字符串
6. __objc_classlist:objc類列表,保存類信息,映射了__objc_data的地址
7. __objc_nlclslist:Objective-C 的 +load 函數(shù)列表恃慧,比 __mod_init_func 更早執(zhí)行园蝠。
8. __objc_catlist: categories
9. __objc_nlcatlist:Objective-C 的categories的 +load函數(shù)列表。
10. __objc_protolist:objc協(xié)議列表
11. __objc_imageinfo:objc鏡像信息
12. __objc_const:objc常量痢士。保存objc_classdata結(jié)構(gòu)體數(shù)據(jù)彪薛。用于映射類相關(guān)數(shù)據(jù)的地址茂装,比如類名,方法名等善延。
13. __objc_selrefs:引用到的objc方法
14. __objc_protorefs:引用到的objc協(xié)議
15. __objc_classrefs:引用到的objc類
16. __objc_superrefs:objc超類引用
17. __objc_ivar:objc ivar指針,存儲屬性少态。
18. __objc_data:objc的數(shù)據(jù)。用于保存類需要的數(shù)據(jù)易遣。最主要的內(nèi)容是映射__objc_const地址彼妻,用于找到類的相關(guān)數(shù)據(jù)。
19. __data:暫時(shí)沒理解豆茫,從日志看存放了協(xié)議和一些固定了地址(已經(jīng)初始化)的靜態(tài)量侨歉。
20. __bss:存儲未初始化的靜態(tài)量。比如:`static NSThread *_networkRequestThread = nil;`其中這里面的size表示應(yīng)用運(yùn)行占用的內(nèi)存揩魂,不是實(shí)際的占用空間幽邓。所以計(jì)算大小的時(shí)候應(yīng)該去掉這部分?jǐn)?shù)據(jù)。
21. __common:存儲導(dǎo)出的全局的數(shù)據(jù)火脉。類似于static牵舵,但是沒有用static修飾。比如KSCrash里面`NSDictionary* g_registerOrders;`, g_registerOrders就存儲在__common里面

虛擬內(nèi)存最終會映射到物理內(nèi)存倦挂,通過上面的介紹棋枕,我們就可以知道代碼和數(shù)據(jù)在內(nèi)存中是如何存儲的。

Symbols

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

# Symbols:
// __text代碼區(qū)
# Address   Size        File  Name
0x100002780 0x00000450  [  2] -[UIButton(SSEdgeInsets) setImageUpTitleDownWithSpacing:]
0x100002BD0 0x00000070  [  2] _UIEdgeInsetsMake
0x100002C40 0x000004B0  [  2] -[UIButton(SSEdgeInsets) setImageRightTitleLeftWithSpacing:]
0x1000030F0 0x000001B0  [  2] -[UIButton(SSEdgeInsets) setDefaultImageTitleStyleWithSpacing:]
0x1000032A0 0x000006F0  [  2] -[UIButton(SSEdgeInsets) setEdgeInsetsWithType:marginType:margin:]
0x100003990 0x000000B0  [  3] -[MFRankListTableViewDelegate removeAllCellModels]
0x100003A40 0x000002A0  [  3] -[MFRankListTableViewDelegate appendCellModels:]
0x100003CE0 0x00000050  [  3] -[MFRankListTableViewDelegate numberOfSectionsInTableView:]
0x100003D30 0x000000C0  [  3] -[MFRankListTableViewDelegate tableView:numberOfRowsInSection:]

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

// __objc_methname方法名區(qū)
0x1012C7D70 0x0000000F  [  2] literal string: imageForState:
0x1012C7D7F 0x00000005  [  2] literal string: size
0x1012C7D84 0x00000014  [  2] literal string: setTitleEdgeInsets:
0x1012C7D98 0x0000000F  [  2] literal string: titleForState:
0x1012C7DA7 0x00000007  [  2] literal string: length
0x1012C7DAE 0x0000000B  [  2] literal string: titleLabel
0x1012C7DB9 0x00000005  [  2] literal string: font
0x1012C7DBE 0x00000025  [  2] literal string: dictionaryWithObjects:forKeys:count:
0x1012C7DE3 0x00000014  [  2] literal string: sizeWithAttributes:
0x1012C7DF7 0x00000014  [  2] literal string: setImageEdgeInsets:
0x1012C7E0B 0x00000019  [  2] literal string: attributedTitleForState:
0x1012C7E24 0x00000006  [  2] literal string: frame
0x1012C7E2A 0x00000020  [  2] literal string: setImageUpTitleDownWithSpacing:
0x1012C7E4A 0x00000023  [  2] literal string: setImageRightTitleLeftWithSpacing:
0x1012C7E6D 0x00000026  [  2] literal string: setDefaultImageTitleStyleWithSpacing:
0x1012C7E93 0x00000029  [  2] literal string: setEdgeInsetsWithType:marginType:margin:
0x1012C7EBC 0x0000000E  [  3] literal string: setIsLoading:

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

// __objc_classlist類列表區(qū)
0x1015C2BF0 0x00000008  [  3] anon
0x1015C2BF8 0x00000008  [  4] anon
0x1015C2C00 0x00000008  [  5] anon
0x1015C2C08 0x00000008  [  6] anon
0x1015C2C10 0x00000008  [  7] anon
1妒峦、__text代碼區(qū)

通過地址 0x100002780 重斑,可以知道它位于 __TEXT 段的 __text 區(qū),這段區(qū)域存儲著代碼肯骇,通過符號表窥浪,根據(jù)地址可以對應(yīng)出源代碼,如 -[UIButton(SSEdgeInsets) setImageUpTitleDownWithSpacing:] 笛丙。通過第三列的類編號漾脂,可以知道該代碼屬于 UIButton+SSEdgeInsets 分類。

2胚鸯、__objc_methname方法名區(qū)

通過地址 0x1012C7D70 骨稿,可以知道它位于 __TEXT 段的 __objc_methname 區(qū),這段區(qū)域存儲著方法名姜钳,通過符號表坦冠,根據(jù)地址可以對應(yīng)出具體的方法名,如 imageForState 哥桥。
由上面的信息辙浑,可以看出方法名越長,最終占用的內(nèi)存也越大拟糕。

3判呕、__objc_classlist類列表區(qū)

__objc_classlist區(qū)的 size 值都是 8 倦踢,區(qū)域里存儲的值都是一個(gè)指針,指向了類的虛擬地址(__objc_data 區(qū)中地址)侠草。
objc_class 的數(shù)據(jù)結(jié)構(gòu)為:

typedef struct objc_class {
        unsigned long long isa;
        unsigned long long wuperclass;
        unsigned long long cache;
        unsigned long long vtable;
        unsigned long long data;
        unsigned long long reserved1;
        unsigned long long reserved2;
        unsigned long long reserved3;
}objc_class;

上面的 data 字段保存了 _objc_const 區(qū)對應(yīng)的數(shù)據(jù)地址(objc_classdata 地址)辱挥。
objc_classdata 數(shù)據(jù)結(jié)構(gòu)為:

typedef struct objc_classdata {
    long long flags;
    long long instanceStart;
    long long instanceSize;
    long long reserved;
    unsigned long long ivarlayout;
    unsigned long long name;
    unsigned long long baseMethod;
    unsigned long long baseProtocol;
    unsigned long long ivars;
    unsigned long long weakIvarLayout;
    unsigned long long baseProperties;
}

objc_classdata 結(jié)構(gòu)體中保存了類名,方法名边涕,協(xié)議名晤碘,ivar 指針和屬性對應(yīng)的地址。通過地址奥吩,可以找到對應(yīng)的段和分區(qū)哼蛆,進(jìn)而找到對應(yīng)類的各種信息。

查找未使用的類和方法

__objc_classrefs 是引用到的類霞赫,_objc_classname 是所有類名腮介,通過分析兩者之間的差別,就可以知道哪些類沒有用到端衰。
同理叠洗,分析 __objc_selrefs_objc_methname 的差別,也可知道哪些方法沒用到旅东。
由于 OC 是動(dòng)態(tài)語言灭抑,中間可能會出現(xiàn)判斷失誤的情況。

分析大文件

Symbols 部分抵代,我們可以把類編號相同的 size 加起來腾节,算出每個(gè)類或庫占用的大小。在 Object files 部分根據(jù)類的編號可以查出對應(yīng)的類荤牍。分析的結(jié)果對 App 安裝包瘦身會有一些幫助案腺。github 上有很多實(shí)現(xiàn)了這樣功能的開源工具,實(shí)現(xiàn)原理很簡單康吵,如有需要可自行查找劈榨。

Author

如果你有什么建議,可以關(guān)注我晦嵌,直接留言同辣,留言必回。

參考

優(yōu)化 App 的啟動(dòng)時(shí)間
iOS微信安裝包瘦身

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末惭载,一起剝皮案震驚了整個(gè)濱河市旱函,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌棕兼,老刑警劉巖陡舅,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異伴挚,居然都是意外死亡靶衍,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進(jìn)店門茎芋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來颅眶,“玉大人,你說我怎么就攤上這事田弥√涡铮” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵偷厦,是天一觀的道長商叹。 經(jīng)常有香客問我,道長只泼,這世上最難降的妖魔是什么剖笙? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮请唱,結(jié)果婚禮上弥咪,老公的妹妹穿的比我還像新娘。我一直安慰自己十绑,他們只是感情好聚至,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著本橙,像睡著了一般扳躬。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上甚亭,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天贷币,我揣著相機(jī)與錄音,去河邊找鬼狂鞋。 笑死片择,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的骚揍。 我是一名探鬼主播字管,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼信不!你這毒婦竟也來了嘲叔?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤抽活,失蹤者是張志新(化名)和其女友劉穎硫戈,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體下硕,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡丁逝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年汁胆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片霜幼。...
    茶點(diǎn)故事閱讀 39,965評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡嫩码,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出罪既,到底是詐尸還是另有隱情铸题,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布琢感,位于F島的核電站丢间,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏驹针。R本人自食惡果不足惜烘挫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望牌捷。 院中可真熱鬧墙牌,春花似錦、人聲如沸暗甥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽撤防。三九已至虽风,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間寄月,已是汗流浹背辜膝。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留漾肮,地道東北人厂抖。 一個(gè)月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像克懊,于是被迫代替她去往敵國和親忱辅。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評論 2 355

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