1.前言
本篇文章旨在展示Clang的探究過程
探究Clang的意義
Clang在iOS當(dāng)中的發(fā)展史
編譯過程
學(xué)習(xí)Clang的實際應(yīng)用
2.意義
- 1.oc捎迫、swift都是編譯型語言,兩者都采用Clang作為前端編譯器,LLVM作為后端編譯器,學(xué)習(xí)Clang了解程序的編譯執(zhí)行過程
- 2.探究代碼的底層實現(xiàn)
- 3.了解mach-o,dylid,dSYM是什么
- 4.app瘦身料祠、啟動優(yōu)化利花、lldb調(diào)試技巧、Clang插件定制
在學(xué)習(xí)戴銘
的課程中也多次提到了Clang對于開發(fā)的幫助,有興趣的可以去學(xué)習(xí)下iOS高手開發(fā)課
3.Xcode中的編譯器發(fā)展史
- 1.xcode3以前:GCC
- 2.xcode3:增加llvm,gcc前端+llvm后端
- 3.xcode4.2:出現(xiàn)clang-llvm3.0成為默認(rèn)編譯器
- 4.xcode4.6llvm升級到4.2版本
- 5.code5:gcc被廢棄,新的編譯器是llvm5.0,從gcc過渡到clang-llvm時代
參考指令:clang -v
4.編譯過程
一次完整的編譯流程:clang -ccc-print-phases main.m
0: input, "main.m", objective-c
1: preprocessor, {0}, objective-c-cpp-output
2: compiler, {1}, ir
3: backend, {2}, assembler
4: assembler, {3}, object
5: linker, {4}, image
6: bind-arch, "x86_64", {5}, image
4.1預(yù)處理
xcrun clang -x objective-c -E -DDEBUG=1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.1.sdk main.m
在xocde中查看preprocess過程Product->Perform Action->Preprocess
4.2詞法分析
clang -fmodules -fsyntax-only -Xclang -dump-tokens -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.1.sdk main.m
4.3語法分析
clang -fmodules -fsyntax-only -Xclang -ast-dump -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.1.sdk main.m
4.4生成中間代碼
clang -O3 -S -fobjc-arc -emit-llvm -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.1.sdk main.m -o main.ll
O3
代碼優(yōu)化級別,optimization level
4.5生成目標(biāo)文件
clang -fmodules -c -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.1.sdk main.m -o main.o
4.6生成可執(zhí)行文件
clang -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.1.sdk main.o AppDelegate.o -o main
4.7Demo演示
新建一個C項目Demo ,演示生成可執(zhí)行文件的過程
clang -fmodules -c main.c -o main.o
clang main.o -o main
./main
4.8Xcode查看編譯過程
我們可以查看到編譯的整個過程信息
5.查看oc的c++實現(xiàn)
clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.1.sdk AppDelegate.m
6.Mach-O
編譯后的可執(zhí)行文件,包括exec(剛才生成的main)王滤、dylib(/usr/lib)
Mach-O 文件包含三個區(qū)域
1.Mach-O Header
:包含字節(jié)順序,magic弦追,cpu 類型裙椭,加載指令的數(shù)量等
2.Load Commands
:包含很多內(nèi)容的表躏哩,包括區(qū)域的位置,符號表揉燃,動態(tài)符號表等。每個加載指令包含一個元信息筋栋,比如指令類型炊汤,名稱,在二進(jìn)制中的位置等
3.Section
:最大的部分,包含了代碼抢腐,數(shù)據(jù)姑曙,比如符號表,動態(tài)符號表等迈倍。
輔助查看工具MachoView
7.LinkMap文件
Write Link Map File
設(shè)置為YES
,指定存儲目錄$(SRCROOT)/build/LinkMap.txt
,編譯后該文件列出了編譯后的每一個.o文件(包括靜態(tài)庫里的),以及每一個每一個目標(biāo)文件的代碼段,數(shù)據(jù)存儲詳情.默認(rèn):Build/Intermediates.noindex/ClangDemo.build/Debug-iphonesimulator/ClangDemo.build
__text
表示編譯后的程序執(zhí)行語句伤靠,__data
表示已初始化的全局變量和局部靜態(tài)變量,__bss
表示未初始化的全局變量和局部靜態(tài)變量啼染,__cstring表示代碼里的字符串常量宴合,等等。
Address Size File Name
0x100001380 0x00000040 [ 2] +[ViewController createSark]
偏移地址 4*16byte 所屬文件
8.學(xué)習(xí)Clang作用
8.1App瘦身方案
1.滴滴 基于clang插件的一種iOS包大小瘦身方案
https://mp.weixin.qq.com/s?__biz=MzUxMzcxMzE5Ng==&mid=2247488360&idx=1&sn=94fba30a87d0f9bc0b9ff94d3fed3386&source=41#wechat_redirect
2.微信 iOS微信安裝包瘦身
https://mp.weixin.qq.com/s?__biz=MzAwNDY1ODY2OQ==&mid=207986417&idx=1&sn=77ea7d8e4f8ab7b59111e78c86ccfe66&3rd=MzA3MDU4NTYzMw==&scene=6#rd
3.阿里 減小ipa體積之刪除frameWork中無用mach-O文件
https://www.oschina.net/question/2625381_2168321
可以看到以上方案都是基于編譯過程/結(jié)果進(jìn)行優(yōu)化,可見Clang在app開發(fā)過程中起到的強(qiáng)大作用
8.2App啟動優(yōu)化
啟動流程:
1.Load dylibs->rebase->bind->objc->initializers
讀取app的可執(zhí)行文件,Mach-o,在可執(zhí)行文件的Mach_header查找lc_load_dylib的加載指令,查找需要的動態(tài)庫dylib,
2.加載到內(nèi)存的可執(zhí)行文件都是不可用的,需要ASLR(進(jìn)程每次啟動,地址空間都會被簡單的隨機(jī)化,有PIE標(biāo)識,otool -hv ClangDemo),需要rabase,binding
rebase:因為初始地址和內(nèi)存地址不同,需要修正
binding:因為動態(tài)庫不編譯進(jìn)程序最終的二進(jìn)制文件中,而是在運行的時候動態(tài)的查找調(diào)用函數(shù)的地址
以上主要是__DATA中的指針數(shù)量
3.objc setup
libsystem中l(wèi)ibsystem_initializer初始化libdispatch,調(diào)用了os_object_init,最終調(diào)用了objc_init,objc_init中綁定了3個方法,map_2_images,load_images,unmap_images:
map_2_images:Binding操作結(jié)束之后,發(fā)出dyld_image_state_bound通知,調(diào)用map_2_images,主要做以下幾件事來完成objc setup:
讀取二進(jìn)制文件的DATA段內(nèi)容,找到與objc相關(guān)的信息.
注冊objc類
確保selector的唯一性
讀取protocol以及category的信息
load_images:函數(shù)作用就是調(diào)用objc的load方法,它監(jiān)聽dyld_image_state_dependents-initialize通知
unmap_image可以理解為map_2_images的逆向操作.
以上3步都是修改__DATA segment中的內(nèi)容.
4.靜態(tài)初始化工作,例如load函數(shù),c++的一些初始化構(gòu)造函數(shù)
5.執(zhí)行完上述的fix-ups之后,接著就會調(diào)用mian()
方法:
優(yōu)化__data__,即去除無用的類,方法,屬性,分類
9.DSYM是什么
從Mach-o文件中抽取調(diào)試信息(二進(jìn)制地址對,源碼文件,行號以及函數(shù)名字的對應(yīng)關(guān)系)而得到的文件目錄,實際用于保存調(diào)試信息的是.dSYM文件中的DWARF,可以手動生成.
查看.dSYM文件內(nèi)容:dwarfdump -v ClangDemo.app.dSYM
10.Xcode中的clang
警告提示:Incomplete objective-c protocols
等等
11.lldb調(diào)試
app運行斷點調(diào)試時,以下列出以下幾個比較有用的lldb調(diào)試方法
-
thread return
直接跳出方法: -
expression
修改值 ,比如expression name = @"張三"
-
bt 10
堆棧打印10條,也可以直接bt
-
thread return
跳出方法