iOS高級進階系列之-項目開發基礎(下)Mach-O與鏈接器,Symbol。

前言

上篇文章多環境配置Mach-O與鏈接器,但是Symbol還沒又說道,這篇文章我們繼續上篇文章內容講下去

.xconnfig補充

上面文章在介紹多環境配置的時候講到了.xconnfig,說到了.xconnfig可以統一管理環境配置,這里可以根據不同的條件配置不同的設置,我們那Other Linker Flags來說明

上圖配置意思就是在Debug環境下,設備為模擬器,切架構為x86時添加framework "Man"

我們看到此時在arm64下編譯時成功的,因為Other Linker Flags沒有導入Man

這次我們看到在x86_64環境下,編譯時發現報錯,告訴我們找不到Man,這是因為這種環境下我們的Man被放入了項目環境中,所以才會提示找不到

作為一個開發者,有一個學習的氛圍跟一個交流圈子特別重要,這是一個我的iOS開發交流群:130 595 548,不管你是小白還是大牛都歡迎入駐 ,讓我們一起進步,共同發展!(群內會免費提供一些群主收藏的免費學習書籍資料以及整理好的幾百道面試題和答案文檔!)

Mach-O再講

上篇文章只是粗略的講了Mach-O,這里再補充一下

Mach-O結構

Mach-O的結構圖如下:

解釋如下:

  • 1.Mach Header告訴執行者自己包含哪些信息(也就是這個Mach-O的身份信息)
  • 2.Load Command就是配置文件最后三個才是我們的代碼編譯后的文件位置
  • 3.配置文件記錄一些必要的文件信息,以Load Command _TEXT為例,它里面記錄下面內容:
    • text代碼段的大小
    • text代碼段的起始位置
  • 記錄其它必要信息,比如:UUID標識符Version版本Dylinker連接器位置Linkdit動態庫信息Dylib引入哪些庫,指定入口為Main函數(你可以不制定Mian函數作為入口)

【注意】:每次讀都能保證讀完一個Load Command,這是因為這些信息的排列是按照結構體對齊的方式進行存儲排列的,所以按著約定好的字節數,就能正好讀完

Mach Header

Mach Header主要結構如下:

解釋幾個主要的:

  • cputype:架構
  • filetype:是可執行文件還是目標文件
  • sizeofcmds:大小

方便理解,我們來打印下mach header

__TEXT

我們通過命令來看下main.m在x86下編譯成text情況
  • 最左邊的是地址,我們看到main的起始地址為100003f20,結束地址為100003f5e。
  • 中間的是機器碼,給機器讀
  • 右邊匯編,給開發者讀

這個有點像查字典,提前約定好匯編機器碼對應關系,當讀機器碼55時,就對應匯編pushq %rbp,以此類推

Mach-O特性

上篇文章講了Mach-O可讀可寫的。可讀我們已經說了,可寫是什么意思?Mach-O之所以能被執行是因為有簽名,當我們修改Mach-O文件,需要重新簽名才能被蘋果系統所接受。這也是為什么破解軟解都需要重新簽名的原因。

鏈接器

生成目標文件過程

  • 1.鏈接器(llvm-ld)并沒有被執行
  • 2.目標文件不會包含Unix程序在被裝載和執行時所必須的包含信息

上面不是很好理解,我們直接通過代碼來解釋

代碼講解

我們在main.m文件中寫如下代碼:

我們看到.m中有定義的屬性了,我們再看看此時編譯為__TEXT是什么樣

我們和上面的相比較發現多了很多東西例如:NSLog此時變成了一個指令callq地址0x100003f60

也就是說在編譯的時候:

  • 1.把能變成匯編的先變成匯編機器碼
  • 2.把屬性轉成符號進行歸類 -> 放入重定位符號表(重定位符號表就是放.m/.o用到的API)
  • 3..o -> 鏈接器 -> 一張表 -> 可執行文件exec

之所以要放入重定位符號表,是因為已經放入符號表中,在生成.o文件時,其地址未虛擬化鏈接器進行連接的時候,會對重定位符號表進行合并

通過上面我們可知鏈接就是處理目標文件符號的過程

指令查看重定位符號表

命令:objdump --macho --reloc +.o文件,運行后,重定位符號表打印如下:

下面畫了個圖來大致說明一下:

未用到的將不會放到.o文件,通過這個特性,我們可以通過查看.o文件查看文件對某種API的使用情況

符號(Symbol)

我們通過指令查看下main.m的符號表
  • l:local布局的意思
  • g:global全局的意思
  • d:Debug的意思
  • o:Data的意思
  • F:Function的意思

我下面對這部分的字符說明進行了總結:

我們發現上面符號表有很多Debug模式下的輸出,下面我們用命令將這部分去掉。我們可以通過strip命令也可以通過鏈接器參數-S

就是鏈接不把調試符號放到最終生成可執行文件中。

  • 調試符號:當我們的文件通過匯編器生成一個DWARF格式調試文件,它會被放在Mach-O__DWARF段中,在連接的時候會把__DWARF段干掉同時__DWARF段變成符號,放到符號表中。

通過上面兩個圖可以知道全局變量是g(全局符號),而本地變量是l(局部符號),而將全局符號變為本地符號:1.加static 2.使用__attribute__關鍵字(第16行)

導入導出符號

我們知道NSLog是Foundation框架下的,寫在19行,相當于是導入NSLog符號,又因為Foundation導出了NSLog符號,讓其它地方使用(導出符號又是全局符號

下面我們看下main.m中有哪些導出符號,通過在.xcconfig中寫入命令,編譯

我們看到有4個導出符號,它正好對應上面打印符號表中的4個全局符號,這也就意味著當我們聲明全局符號時,會默認為導出符號,其它地方也可以使用

下面我們創建一個OC類,再查看符號

OC類都會默認為導出符號

間接符號表

我們知道動態庫是在運行的過程中加載,也就意味著它在編譯鏈接階段只需要提供符號就可以了,上篇文章我們在說符號表時提到:間接符號表保存這項目使用的其它動態庫符號,下面我們通過在.xcconfig中寫入命令,編譯來查看間接符號表

這里面我們就只認識最后的NSLog,這個是Foundation給我提供的導出符號

總結

  • 1.全局符號可以變成導出符號給外界使用
  • 2.間接符號表不能刪除,意味著動態庫中的全局符號不能刪除,也就說明在strip動態庫時,不能strip全局符號
  • 3.OC類編譯時都會默認導出符號,那么我們在用OC寫動態庫時,如果想盡可能讓動態庫包小些,我們可以在.xcconfig定義參數不導出符號
    • 進行編譯

    • 和上面的相比發現少了一個_OBJC_CLASS_$_LjOneObject,相同的方法可以讓_OBJC_METACLASS_$_LjOneObject也消失

補充

上面總結說了可以通過不導出符號來使動態庫體積減小,但是如果我們要寫的類太多了怎么辦,其實給了有方法:

  • 1.可以執行文件
  • 2.1中的文件的獲得可以通過查看當前文件使用類庫的信息

編譯后輸出:

紅框內是告訴開發者,生成了幾個目標文件,項目使用了哪些庫文件。通過map可以導出符號信息,鏈接信息

Weak Symbol

Weak Symbol具體分一下兩種

  • 1.Weak Reference Symbol:表示此未定義符號弱引用。如果動態鏈接器找不到該符號定義,則將其設置為0鏈接器會將此符號設置弱鏈接標志

  • 2.Weak defintion Symbol:表示此符號為弱定義符號。如果靜態鏈接器動態鏈接器為此符號找到另一個(非弱)定義,則弱定義將被忽略。只能將合并部分中的符號標記為弱定義

Weak Reference Symbol

Weak Reference Symbol(弱引用)寫法:

上面的解釋:也就是如果若引入符號未被定義(不想要實現),系統不會報錯

我們通過代碼來說明一些問題,在main函數寫如下代碼:

但是這么寫會報錯,原因:在說編譯鏈接原理的時候說過,符號怎么來查找的呢?我們在WeakImportSymbol.h寫了聲明,在main函數中使用,就是用的API,但是在連接的時候,我們需要知道符號具體的地址在什么地方,否則提示找不到

我們可以告訴編譯器,我這個符號時動態鏈接的,不要管它的具體位置即使它是弱引用的,到時候dyld運行起來,自己會查找

-U參數就是告訴編譯器這個沒有定義,需要動態查找

再次運行就會成功了,那么這個有什么用處呢?比如:我們可以判斷其它庫里有沒有這個符號這個符號我就調用沒有這個符號我就不調用。還有個用途就是在動態庫上,我們可以將整個動態庫文件聲明成一個弱引用,這個有什么好處呢?也就意味著如果你這個庫沒有導入的話,也不會報動態庫找不到的錯誤

Weak defintion Symbol

Weak defintion Symbol(弱定義)寫法:

上面講到弱定義符號:如果靜態鏈接器或動態鏈接器為此符號找到另一個(非弱)定義,則弱定義將被忽略,怎么理解這句話呢?我們通過代碼來理解

  • 1.在.h中我們弱定義了weak_function方法
  • 在.m中我們實現這個弱定義方法

方法實現聲明都是全局的,上面講了應該轉為導出符號,下面我們變一下,看下打印

當聲明為弱定義方法,并不影響作為導出符號導出

當我們在.m聲明相同的方法

如果正常情況下,由于方法名相同,運行應該會報錯,但是由于這個方法被弱定義,此時編譯是不會報錯的。

下面我們在main函數中調用這個方法

運行打印結果

我們看到執行了main函數的weak_function方法,并沒有執行WeakSymbol的weak_function,這也就是上面說的:如果靜態鏈接器或動態鏈接器為此符號找到另一個(非弱)定義,則弱定義將被忽略

如果我們把弱定義的符號聲明成一個隱藏符號,此時它應該是一個弱定義的本地符號

重新導出符號

當我們NSLogmain函數使用,當我想讓其它項目使用這個main.m時,也能夠使用NSLog,這就需要我們對NSLog進行重新導出(舉的事NSLog,其實在Foundation已經對NSLog做了重新導出否則外界是無法使用的

當我們重新導出NSLog,需要對間接符號的符號起別名

它會自動的將這個NSLog變成導出符號Lj_NSLog,編譯

我們發現存在了Lj_NSLog,但是這種形式不夠友好,所以我們需要換種打印方式,寫入命令

我們看到這個Lj_NSLog變成了NSLog的別名,我們再看下導出符號表符號

可以看到Lj_NSLog是被導出了,而且是重新導出的一個符號

作用:在我們的動態庫中鏈接另一個動態庫的時候,其中一個動態庫對你鏈接程序不可見的,我們就可以用這種重新導出方式讓這個動態庫可見,可以讓一個符號可見,也可以讓一個動態庫可見

總結

通過上面的符號可以知道一下幾點:

  • 1.間接符號表中的符號不能刪除,意味著動態庫中的全局符號不能刪除,也就說明在strip動態庫時,不能strip全局符號
  • 2.靜態庫.o文件合計以及重定位符號表,由于重定位符號不能刪除,所以只能strip.0文件中的調試符號

【問題】App加入動態庫體積和加入靜態庫體積誰的更大(只考慮符號)

答案:動態庫的體積更大

原因:

  • 【靜態庫】App在鏈接靜態庫時,會將.o文件以及重定位符號表放到App的符號表中,也就意味著它變成可能本地、全局、導出符號,根據我們上面說的脫離符號表規則,除了間接符號表中的符號,其它都可以脫
  • 【動態庫】App在鏈接動態庫時,符號都放到間接符號表中,導致在脫離符號表無法脫離間接符號表

拓展Strip Style(符號脫離)

  • 1.Debugging Symbols (.o 靜態庫 / 可執行文件 動態庫)
  • 2.All Symbols
  • 3.Non-Global Symbols

Strip Style過程

靜態庫

動態庫

All Symbols

Non-Global Symbols

寫到最后

文章寫的有些東西沒有細說,后面會介紹的!希望大家能夠多多交流,共同進步,最后貼出來上面說的指令:

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容