Clang探究及應用

1.前言

本篇文章旨在展示Clang的探究過程

  • 探究Clang的意義
  • Clang在iOS當中的發(fā)展史
  • 編譯過程
  • 學習Clang的實際應用

2.意義

  • 1.oc、swift都是編譯型語言,兩者都采用Clang作為前端編譯器,LLVM作為后端編譯器,學習Clang了解程序的編譯執(zhí)行過程
  • 2.探究代碼的底層實現(xiàn)
  • 3.了解mach-o,dylid,dSYM是什么
  • 4.app瘦身、啟動優(yōu)化、lldb調試技巧、Clang插件定制
    在學習戴銘的課程中也多次提到了Clang對于開發(fā)的幫助,有興趣的可以去學習下iOS高手開發(fā)課

3.Xcode中的編譯器發(fā)展史

  • 1.xcode3以前:GCC
  • 2.xcode3:增加llvm,gcc前端+llvm后端
  • 3.xcode4.2:出現(xiàn)clang-llvm3.0成為默認編譯器
  • 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預處理

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生成目標文件

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í)行文件的過程


image.png
clang -fmodules -c main.c -o main.o
clang main.o -o main
./main

4.8Xcode查看編譯過程

我們可以查看到編譯的整個過程信息


image.png

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:包含很多內容的表,包括區(qū)域的位置,符號表,動態(tài)符號表等。每個加載指令包含一個元信息,比如指令類型,名稱,在二進制中的位置等
3.Section:最大的部分,包含了代碼,數(shù)據,比如符號表,動態(tài)符號表等。
輔助查看工具MachoView

image.png

7.LinkMap文件

Write Link Map File設置為YES,指定存儲目錄$(SRCROOT)/build/LinkMap.txt,編譯后該文件列出了編譯后的每一個.o文件(包括靜態(tài)庫里的),以及每一個每一個目標文件的代碼段,數(shù)據存儲詳情.默認: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.學習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

可以看到以上方案都是基于編譯過程/結果進行優(yōu)化,可見Clang在app開發(fā)過程中起到的強大作用

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.加載到內存的可執(zhí)行文件都是不可用的,需要ASLR(進程每次啟動,地址空間都會被簡單的隨機化,有PIE標識,otool -hv ClangDemo),需要rabase,binding
        rebase:因為初始地址和內存地址不同,需要修正
        binding:因為動態(tài)庫不編譯進程序最終的二進制文件中,而是在運行的時候動態(tài)的查找調用函數(shù)的地址
    以上主要是__DATA中的指針數(shù)量
    3.objc setup        
    libsystem中l(wèi)ibsystem_initializer初始化libdispatch,調用了os_object_init,最終調用了objc_init,objc_init中綁定了3個方法,map_2_images,load_images,unmap_images:
    map_2_images:Binding操作結束之后,發(fā)出dyld_image_state_bound通知,調用map_2_images,主要做以下幾件事來完成objc setup:
    讀取二進制文件的DATA段內容,找到與objc相關的信息.
    注冊objc類
    確保selector的唯一性
    讀取protocol以及category的信息
    load_images:函數(shù)作用就是調用objc的load方法,它監(jiān)聽dyld_image_state_dependents-initialize通知
    unmap_image可以理解為map_2_images的逆向操作.
    以上3步都是修改__DATA segment中的內容.

    4.靜態(tài)初始化工作,例如load函數(shù),c++的一些初始化構造函數(shù)
    5.執(zhí)行完上述的fix-ups之后,接著就會調用mian()
    
方法:
    優(yōu)化__data__,即去除無用的類,方法,屬性,分類 

9.DSYM是什么

從Mach-o文件中抽取調試信息(二進制地址對,源碼文件,行號以及函數(shù)名字的對應關系)而得到的文件目錄,實際用于保存調試信息的是.dSYM文件中的DWARF,可以手動生成.
查看.dSYM文件內容:dwarfdump -v ClangDemo.app.dSYM

10.Xcode中的clang

警告提示:Incomplete objective-c protocols等等

11.lldb調試

app運行斷點調試時,以下列出以下幾個比較有用的lldb調試方法

  • thread return直接跳出方法:
  • expression修改值 ,比如 expression name = @"張三"
  • bt 10堆棧打印10條,也可以直接bt
  • thread return 跳出方法
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容