概述
??本文檔描述了Mach-O文件格式的結(jié)構(gòu)谅辣,它被用來存儲程序和庫到硬盤中柏副,作為Mac OS X程序的二進制接口(ABI).想要了解xcode工具和Mach-O文件怎么工作以及底層任務(wù)調(diào)試的搓扯,需要了解這些信息
??Mach-O文件格式提供中間物(構(gòu)建過程中產(chǎn)生的)和最終存儲有機器碼和數(shù)據(jù)的文件锨推。它被設(shè)計成一個靈活的BSD a.out的替代品换可。這種文件被編譯器和靜態(tài)鏈接器使用,并在運行時包含靜態(tài)鏈接的可執(zhí)行代碼译荞。隨著Mac OS X目標的發(fā)展吞歼,動態(tài)鏈接的特性也被添加進來篙骡,從而為靜態(tài)鏈接和動態(tài)鏈接的代碼形成了單一的文件格式尿褪。
基本結(jié)構(gòu)
??一個Mach-O文件包含三個主要區(qū)域(如圖1所示)
??在每個Mach-O文件的開始部分都有一個Header結(jié)構(gòu)杖玲,這個Header標志這個文件是Mach-O文件。這個頭部也包含了其他基礎(chǔ)文件類型信息今膊,標明目標體系結(jié)構(gòu)斑唬,包含指定選項的標志,這些標志會影響對文件剩余部分的解釋抒倚。
??在header之后是一系列大小可變的加載命令含蓉,它們指定文件的布局和鏈接特征馅扣。在其他信息中,load命令可以指定:
- 文件在虛擬內(nèi)存中的初始化布局
- 符號表的位置(被用來動態(tài)鏈接使用)
- 程序主線程的初始執(zhí)行狀態(tài)
- 包含主可執(zhí)行文件導入符號定義的共享庫的名稱
??在加載指令之后蓄喇,所有的Mach-O文件都包含一個或多個段的數(shù)據(jù)刃鳄。每個段有零個或多個section铲汪,段的每個section都包含特定類型的代碼或數(shù)據(jù)。每個段定義了虛擬內(nèi)存區(qū)域张吉,被連接器用來映射到進程的地址空間中,段和section的確切數(shù)量和布局由load命令和文件類型指定伦忠。
??在用戶級完全鏈接的Mach-O文件中昆码,最后一個段是鏈接編輯段。此段包含鏈接編輯信息的表脓匿,如符號表陪毡、字符串表等毡琉,動態(tài)加載程序使用這些信息將可執(zhí)行文件或Mach-O包鏈接到其附屬庫。
?? Mach-O文件中的各種表按編號引用section(節(jié))虱歪。節(jié)編號從1(不是0)開始师枣,并跨越段邊界践美。因此陨倡,在文件中第一個段可能包含1、2節(jié)杂曲,第二個段可能包含3、4節(jié)棚饵。
??當使用Stabs調(diào)試格式時蟹地,符號表還包含調(diào)試信息。使用DWARF時分别,調(diào)試信息存儲在圖像對應的dSYM文件中,該文件由uuid_command(第20頁)結(jié)構(gòu)指定括授。
?? 注釋: stabs取名于symbol table strings薛夜,因為開始的時候梯澜,調(diào)試信息是以字符串的形式存儲在Unix的a.out目標文件的符號表中。 stabs以字符串的形式編碼程序的信息咆疗。最開始的時候民傻,stabs很簡單,但是后來變得越來越復雜漏隐,難解,而且不一致脖隶。此外,stabs沒有形成標準构蹬,文檔也不夠詳細庄敛。Sun Microsystem基于stabs作了大量擴展;GCC在對SUn的擴展進行反向工程的過程中之众,作了其它的擴展棺禾。stabs仍然被廣泛使用。
??DWARF已經(jīng)被廣泛使用悬襟,包括GCC和LLVM。DWARF也是基于嵌套結(jié)構(gòu)存儲調(diào)試信息割捅。
?? DWARF源于Unix System V Release 4中的C編譯器以及sdb調(diào)試器亿驾。1989年的文檔形成了DWARF 1。1900發(fā)布了DWARF 2的一個draft標準。隨后召锈,因為Motorola一個項目的失敗规求,支持團隊被解散。隨后丛塌,DWARF 2的擴展泛濫印衔,就有了各種各樣的實現(xiàn),沒能形成最終標準与帆。直到2006年發(fā)布的最終標準DWARF 3. 2010年發(fā)布了DWARF 4.
頭部結(jié)構(gòu)和加載命令
??一個Mach-O文件包含了一個架構(gòu)的代碼和數(shù)據(jù)墨榄。Mach-O文件的頭結(jié)構(gòu)指定了目標架構(gòu)玄糟,這使得內(nèi)核能夠確保,例如袄秩,為基于powerpc的Macintosh計算機設(shè)計的代碼不會在基于intel的Macintosh計算機上執(zhí)行阵翎。
??你可以將多個Mach-O文件組合成一個二進制文件,用 “Universal Binaries and 32-bit/64-bit PowerPC Binaries” 相應格式描述即可。
??包含多個體系結(jié)構(gòu)的目標文件的二進制文件不是Mach-O文件。他們存檔一個或多個Mach-O文件疆前。
??段和節(jié)通常按名稱訪問米辐。按照慣例狸页,段的命名是使用所有大寫字母加上兩個下劃線(例如,用字母組合);section的命名應該使用所有小寫字母加上兩個下劃線(例如,用字母組合)乒省。這個命名約定是標準的蛆封,盡管對于工具的正確操作不是必需的。
段(segment)
??段定義了Mach-O文件中的一個字節(jié)范圍平绩,以及地址和內(nèi)存保護屬性酗钞,當動態(tài)鏈接器加載應用程序時,這些字節(jié)被映射到虛擬內(nèi)存中。因此少孝,段總是與虛擬內(nèi)存頁對齊粱胜。一個段包含零個或多個節(jié)涯曲。
??運行時比構(gòu)建時需要更多內(nèi)存的段可以指定比實際磁盤上更大的內(nèi)存大小。PowerPC可執(zhí)行文件的鏈接器生成的__PAGEZERO段的虛擬內(nèi)存大小為一頁态兴,而在磁盤上的大小為0。它不需要占用可執(zhí)行文件中的任何空間章贞。
note: 段的末尾必須用大小為0的節(jié)填充败京,否則標準工具將沒辦法成功操作Mach-O文件车荔。
??為了緊湊性,中間對象文件只包含一個段。這段沒有名字蜓谋,在最終的對象文件中观堂,它包含了不同段所定義的sections福扬。定義了一個section(page 23)的數(shù)據(jù)結(jié)構(gòu)包含了段中節(jié)的名稱莉御,靜態(tài)鏈接器將每個節(jié)放在最終的目標文件中。
??為了更好的性能步氏,段應該和虛擬內(nèi)存頁的邊界對齊榛瓮,對于PowerPC和x86處理器每個虛擬內(nèi)存頁大寫是4096b。累加每個section的大小删掀,然后對結(jié)果取整作為下個虛擬內(nèi)存頁的分界線(4096b或者4kb).用這種算法艾少,一個段最小是4kb毯辅,接下來會以4kb作為增量描焰。
??出于分頁的目的瓤介,頭部和加載命令會作為第一個段的一部分。在一個可執(zhí)行文件中吩蔑,通常這個意思是頭部和加載命令__TEXT開始的位置,因為這是第一個包含數(shù)據(jù)的段鹏浅。__PAGEZERO段在硬盤上面沒有數(shù)據(jù)胖眷,所以一般會忽略它劳淆。
??這些是標準的Mac OS X開發(fā)工具(包含在Xcode工具CD中)可能包含在Mac OS X可執(zhí)行文件中的片段:
- 靜態(tài)連接器會創(chuàng)建一個
__PAGEZERO
段作為可執(zhí)行文件的第一個段。這個段位于虛擬內(nèi)存的0位置论泛,而且沒有分配任何保護權(quán)限湿颅,它們的組合會導致對NULL
的訪問沙合,這是一種常見的C編程錯誤,會立即崩潰蜜葱。__PAGEZERO
段對于現(xiàn)在的架構(gòu)就是一頁虛擬內(nèi)存頁的大信依妗(對于基于Intel和Power-PC內(nèi)核的Mac計算機揩懒,一般是4096字節(jié)或者十六進制0x1000).因為__PAGEZERO
段沒有數(shù)據(jù)亦鳞,在文件中沒有占用任何空間(段命令中的文件大小是0) -
__TEXT
段包含了可執(zhí)行代碼和只讀數(shù)據(jù)。允許內(nèi)核直接從可執(zhí)行文件映射到共享內(nèi)存中胆描,靜態(tài)連接器設(shè)置這個段虛擬內(nèi)存的權(quán)限為不允許寫入目尖。當段被映射到內(nèi)存中時,它可以在所有對其內(nèi)容感興趣的進程之間共享。(這個主要用在frameworks,bundles搞乏,和共享庫蛤奥,但是可以在Mac OS X中運行同一可執(zhí)行文件的多個副本小泉,這也適用于這種情況被济。)只讀屬性也意味著這些內(nèi)存也組成的__TEXT
段是不可以會寫到磁盤中的。當內(nèi)核需要釋放物理內(nèi)存的時候灶壶,他可以放棄一個或多個__TEXT
绞灼,如果下次有需要,就從磁盤重新讀取他們 -
__DATA
段包含的是可寫數(shù)據(jù)。靜態(tài)連接器設(shè)置這段虛擬內(nèi)存的權(quán)限為可讀可寫。因為是可寫的,因為它是可寫的棒拂,所以框架或其他共享庫的數(shù)據(jù)段在邏輯上被復制到與該庫鏈接的每個進程伞梯。當組成__DATA
段的內(nèi)存頁可讀可寫時,內(nèi)核將它們標記為“寫時復制”;因此當一個進程寫入這些頁的其中一頁時帚屉,該進程會收到當前進程私有的當前頁備份谜诫。 -
__OBJC
段包含了OC語言runtime支持庫所用到的數(shù)據(jù)。 -
__IMPORT
段包含了符號樁和指向可執(zhí)行文件中未定義的符號的非懶加載指針攻旦。這個段僅在IA-32架構(gòu)的目標可執(zhí)行文件中生成喻旷。 -
__LINKEDIT
段包含了動態(tài)連接器所用到的原始數(shù)據(jù),例如符號牢屋、字符串且预、和重定位表記錄
Sections(節(jié))
__TEXT
和__DATA
段包含了許多標準section槽袄,列于表1、表2锋谐、和表3遍尺。__OBJC
段包含了許多OC編譯器私有section。請注意涮拗,靜態(tài)鏈接器和文件分析工具使用節(jié)類型和屬性(而不是節(jié)名稱)來確定它們應該如何處理節(jié)乾戏。section名稱、類型和屬性將在section數(shù)據(jù)類型的描述(第23頁)中進一步說明三热。
表1??__TEXT
段的節(jié)
段?和?節(jié)名稱 | 內(nèi)容 |
---|---|
__TEXT, __text | 可執(zhí)行機器碼鼓择。編譯器通常只放可執(zhí)行代碼,沒有其他類型的表和數(shù)據(jù) |
__TEXT, __cstring | C字符串常量康铭。c字符串就是一個以'\0'結(jié)尾的非空的字節(jié)序列惯退。靜態(tài)連接器在構(gòu)建最終結(jié)果時會合并c常量字符串值,移除重復的 |
__TEXT,__picsymbol_stub | 位置無關(guān)的間接符號樁从藤。有關(guān)更多信息领舰,請參閱Mach-O編程主題中的“動態(tài)代碼生成”疾层。 |
__TEXT,__symbol_stub | 簡介符號樁,有關(guān)更多信息,請參閱Mach-O編程主題中的“動態(tài)代碼生成” |
__TEXT, __const | 初始化的常量變量据块。編譯器會在這個section中放置用const 修飾的不可重定位數(shù)據(jù) |
__TEXT, __litera14 | 4字節(jié)的字面量。編譯器在本節(jié)中放置單精度浮點常量灾常。在構(gòu)建最終文件時俺叭,靜態(tài)鏈接器合并這些值,刪除重復項妒貌。對于某些架構(gòu)通危,編譯器使用立即加載指令比添加到本節(jié)更有效。 |
__TEXT, __litera18 | 8字節(jié)的字面量灌曙。編譯器在本節(jié)中放置雙精度浮點常量菊碟。在構(gòu)建最終文件時,靜態(tài)鏈接器合并這些值在刺,刪除重復項逆害。對于某些架構(gòu),編譯器使用立即加載指令比添加到本節(jié)更有效蚣驼。 |
表2 ?? __DATA
段中的節(jié)
段?和?節(jié)名稱 | 內(nèi)容 |
---|---|
__DATA, __data | 初始化的可變變量魄幕,例如可寫的c字符串和數(shù)據(jù)數(shù)組 |
__DATA, __la_symbol_ptr | 懶加載符號指針,它間接引用了來自不同文件的重要函數(shù)颖杏。了解更多信息纯陨,參閱Mach-O編程主題中的“生成動態(tài)代碼” |
__DATA, __nl_symbol_ptr | 非懶加載符號指針,它間接引用了來自不同文件的數(shù)據(jù)項 。了解更多信息队丝,參閱Mach-O編程主題中的“生成動態(tài)代碼”靡馁。 |
__DATA, __dyld | 動態(tài)連接器使用的占位符部分 |
__DATA, __const | 初始化的可重定位的常量變量 |
__DATA, __mod_init_func | 模塊初始化函數(shù)。C++編譯器放置靜態(tài)構(gòu)造函數(shù)的地方 |
__DATA, __mod_term_func | 模塊終止函數(shù) |
__DATA, __bss | 未初始化的靜態(tài)變量的數(shù)據(jù) (比如机久,static int i; ) |
__DATA, __common | 導入的未初始化符號定義(比如:int i; )位于全局范圍內(nèi)的(聲明在函數(shù)之外的) |
表3 ?? __IMPORT
段的節(jié)
段?和?節(jié)名稱 | 內(nèi)容 |
---|---|
__IMPORT, __jump_table | 動態(tài)庫中函數(shù)調(diào)用的存根臭墨。 |
__IMPORT,__pointers | 非懶加載符號指針,它直接引用從不同文件導入的函數(shù)膘盖。 |
注意:編譯器或任何創(chuàng)建Mach-O文件的工具都可以定義額外的節(jié)名胧弛。這些額外的名稱沒有出現(xiàn)在表1中。
數(shù)據(jù)類型
這里介紹的是構(gòu)成Mach-O文件的數(shù)據(jù)類型侠畔。除了fat_header(第56頁)和fat_arch(第56頁)以外结缚,所有Mach-O數(shù)據(jù)結(jié)構(gòu)中的整數(shù)類型的值都是使用主機CPU的字節(jié)排序方案寫入的,它們都是按大端字節(jié)順序?qū)懭氲摹?/p>
頭部數(shù)據(jù)結(jié)構(gòu)
mach_header
指定文件的一般屬性软棺。出現(xiàn)在以32位體系結(jié)構(gòu)為目標的目標文件的開頭红竭。聲明在/usr/include/mach-o/loader.h
,也可以看mach_header_64
(第14頁)
struct mach_header
{
uint32_t magic;
cpu_type_t cputype;
cpu_subtype_t cpusubtype;
uint32_t filetype;
uint32_t ncmds;
uint32_t sizeofcmds;
uint32_t flags;
};
字段
magic
:包含了一個整數(shù)值喘落,標志這個文件是32位的Mach-O的文件茵宪。如果文件打算在與運行編譯器的計算機的字節(jié)序相同的CPU上使用,請使用常量MH_MAGIC瘦棋。 當目標計算機的字節(jié)排序方案與主機CPU相反時稀火,可以使用常量MH_CIGAM。
cputype
:這個整數(shù)值指示了你準備在什么架構(gòu)上運行此文件赌朋。適當?shù)闹涤幸韵聨追N:
-
CPU_TYPE_POWERPC
目標是基于PowerPC的Mac電腦 -
CPU_TYPE_I386
目標是基于Intel的Mac電腦
cpusubtype
指定CPU的精確模型的整數(shù)凰狞。在Mac OS X內(nèi)核支持的所有PowerPC或x86處理器上運行,這個應該設(shè)置為CPU_SUBTYPE_POWERPC_ALL
或者CPU_SUBTYPE_I386_ALL
filetype
該整數(shù)值標明了文件的用途和對齊方式沛慢。這個字段有效的值包括:
-
MH_OBJECT
文件類型是用于中間目標文件的格式赡若。它包含了一個段中所有的節(jié)格式很緊湊。編譯器和匯編器通常會為每個源碼文件創(chuàng)建一個MH_OBJECT
文件团甲。按照慣例斩熊,這個文件名字的后綴格式是.o
。 -
MH_EXECUTE
文件類型是標準可執(zhí)行程序使用的格式伐庭。 -
MH_BUNDLE
文件類型通常由運行時加載的代碼使用(通常稱為bundle或插件)。按照慣例分冈,這種格式的文件擴展名是.bundle
圾另。 -
MH_DYLIB
文件類型用于動態(tài)共享庫。它包含一些額外的表來支持多個模塊雕沉。按照慣例集乔,這種格式的文件擴展名是dylib
,除了框架的主共享庫外,它通常是不會有文件擴展名的扰路。 -
HM_PRELOAD
文件類型是一種可執(zhí)行格式尤溜,用于Mac OS X內(nèi)核沒有加載的特殊用途程序,例如刻錄到可編程ROM芯片中的程序汗唱。不要將此文件類型與MH_PREBOUND
標志混淆宫莱,MH_PREBOUND
標志是靜態(tài)鏈接器在頭結(jié)構(gòu)中設(shè)置的標志,用于標記預綁定圖像哩罪。 -
MH_CORE
文件類型用于儲存核心文件授霸,這通常是在程序崩潰時創(chuàng)建的。核心文件存儲進程崩潰時的整個地址空間际插。稍后碘耳,您可以在核心文件上運行g(shù)db來找出為什么會發(fā)生崩潰。 -
MH_DYLINKER
文件類型用于動態(tài)連接器共享庫框弛。這是dyld
文件類型辛辨。 -
MH_DSYM
文件類型指定存儲對應二進制文件的符號信息的文件。
ncmds
:在頭部結(jié)構(gòu)后面指示了加載命令的數(shù)量
sizeofcmds
:在header結(jié)構(gòu)后面指示這加載命令所占用的字節(jié)數(shù)量
flags
:包含了一組位標志瑟枫,指示著Mach-O文件格式某些可選特性的狀態(tài)斗搞。這些是你可以用來操作該字段的掩碼:
-
MH_NOUNDEFS
--該對象文件在構(gòu)建時不包含未定義的引用。 -
MH_INCRLINK
--該對象文件是相對于基類文件增量連接的輸出而且不能被重復連接力奋。 -
MH_DYLDLINK
--該對象文件是動態(tài)連接器的輸入榜旦,不能被再次靜態(tài)連接。 -
MH_TWOLEVEL
--該鏡像用二級命名空間綁定景殷。 -
MH_BINDATLOAD
--當文件被加載時溅呢,動態(tài)攔截器應該綁定未定義的引用。 -
MH_PREBOUND
--文件的未定義引用是預綁定的猿挚。 -
MH_PREBINDABLE
--文件未定義的引用需要預綁定 -
MH_NOFIXPREBINDING
--動態(tài)鏈接器不會將此可執(zhí)行文件通知預綁定代理咐旧。 -
MH_ALLMODSBOUND
--指示此二進制文件綁定到其附屬庫的所有兩級名稱空間模塊。僅當MH_PREBINDABLE和MH_TWOLEVEL被設(shè)置時使用绩蜻。 -
MH_CANONICAL
--通過從文件中取消預綁定-清除預綁定信息铣墨,此文件已被規(guī)范化。有關(guān)詳細信息办绝,請參閱redo_prebinding手冊頁伊约。 -
MH_SPLIT_SEGS
--該文件將只讀段和只寫段分開 -
MH_FORCE_FLAT
--可執(zhí)行文件強制所有映像使用平面名稱空間綁定。 -
MH_SUBSECTIONS_VIA_SYMBOLS
--對象文件的各個部分可以劃分為單獨的塊孕蝉。如果其他代碼不使用這些塊屡律,那么它們就是死區(qū)。有關(guān)詳細信息降淮,請參閱Xcode用戶指南中的“鏈接”超埋。 -
MH_NOMULTIDEFS
--這把傘保證在它的子圖像中沒有符號的多重定義。因此,總是可以使用兩級名稱空間提示霍殴。
加載命令數(shù)據(jù)結(jié)構(gòu)
加載命令位于對象文件中頭部后面媒惕,它指定了文件的邏輯結(jié)構(gòu)以及在虛擬內(nèi)存中的布局。每個加載命令都以一個指定命令類型和命令數(shù)據(jù)大小的字段開始来庭。
load_command
包含所有加載命令通用的字段妒蔚。
struct load_command
{
uint32_t cmd;
uint32_t cmdsize;
};
字段
cmd
:一個整數(shù),代表加載命令的類型巾腕。表4列出了有效的加載命令類型面睛。
cmdsize
:這個字段指定了記載命令數(shù)據(jù)結(jié)構(gòu)總共的字節(jié)大小。根據(jù)load命令不同的類型尊搬,每個load命令結(jié)構(gòu)包含一組不同的數(shù)據(jù)叁鉴,所以每組數(shù)據(jù)大小可能不同。在32位架構(gòu)中佛寿,這組數(shù)據(jù)的大小總是4的倍數(shù)幌墓,在64位架構(gòu)中是8的倍數(shù)。如果load命令數(shù)據(jù)不能被4或8整除(這取決于目標體系結(jié)構(gòu)是32位還是64位)冀泻,則在末尾添加包含0的字節(jié)常侣,直到它被整除為止。
討論
表四列出了有效的加載命令類型弹渔,以及每種類型完整數(shù)據(jù)的連接胳施。
表四 Mach-O加載命令
命令 | 數(shù)據(jù)結(jié)構(gòu) | 用途 |
---|---|---|
LC_UUID | uuid_command(page 20) | 指定圖像或其對應的dSYM文件的128位UUID |
LC_SEGMENT | segment_command | 加載此文件時,定義映射到進程地址空間中所需的文件段肢专。而且每個段中包含了所有的節(jié) |
LC_SYMTAB | symtab_command | 指定了文件的符號表舞肆。靜態(tài)鏈接器和動態(tài)連接器連接文件的時候都需要用到這些信息,還可以通過調(diào)試器將符號映射到生成符號的原始源代碼文件博杖。 |
LC_DYSYMTAB | dysymtab_command | 指定了動態(tài)連接器用到的附帶符號表信息 |
LC_THREAD LC_UNIXTHREAD | thread_command | 對于可執(zhí)行文件椿胯,LC_UNIXTHREAD 命令定義了進程主線程的線程狀態(tài)。LC_THREAD 和LC_UNIXTHREAD 一樣剃根,但是LC_THREAD 不會引起內(nèi)核分配堆棧 |
LC_LOAD_DYLIB | dylib_command | 定義此文件鏈接的動態(tài)共享庫的名稱哩盲。 |
LC_ID_DYLIB | dylib_command | 定義了動態(tài)共享庫安裝名稱 |
LC_PREBOUND_DYLIB | prebound_dylib_command | 對于此可執(zhí)行文件鏈接預綁定的共享庫,指定使用的共享庫中的模塊狈醉。 |
LC_LOAD_DYLINKER | dylinker_command | 指定內(nèi)核執(zhí)行加載文件所需的動態(tài)連接器 |
LC_ID_DYLINKER | dylinker_command | 標志這個文件可以作為動態(tài)連接器 |
LC_ROUTINES | routines_command | 包含共享庫初始化例行程序的地址(由鏈接器的-init 選項指定)廉油。 |
LC_ROUTINES_64 | routines_command_64 | 包含共享庫64位初始化例行程序的地址(由鏈接器的-init 選項指定)。 |
LC_TWOLEVEL_HINTS | twolevel_hints_command | 包含兩級命名空間查詢提示表苗傅。 |
LC_SUB_FRAMEWORK | sub_framework_command | 將此文件標識為傘形框架的子框架的實現(xiàn)抒线。傘形框架的名稱存儲在字符串參數(shù)中。(傘形框架可以包含多個子框架金吗,蘋果不推薦這樣使用) |
LC_SUB_UMBRELLA | sub_umbrella_command | 指定此文件作為傘框架的子傘 |
LC_SUB_LIBRARY | sub_library_command | 標志這個文件可以作為傘框架的一個字庫的實現(xiàn)。請注意,Apple尚未為子庫定義受支持的位置摇庙。 |
LC_SUB_CLIENT | sub_client_command | 子框架可以明確地允許另一個框架或包鏈接到它旱物,方法是包含一個LC_SUB_CLIENT load命令,該命令包含框架的名稱或包的客戶端名稱卫袒。 |
uuid_command
指定鏡像或者與之相匹配的dSYM文件的128位通用唯一標識符
struct uuid_command
{
uint32_t cmd;
uint32_t cmdsize;
uint8_t uuid[16];
};
字段
cmd
為此結(jié)構(gòu)設(shè)置LC_UUID宵呛。
cmdsize
設(shè)置sizeof(uuid_command)
uuid
128位唯一標識符
segment_command
指定組成段的32位Mach-O文件中的字節(jié)范圍。這些字節(jié)通過加載器映射到程序的地址空間中夕凝。聲明在/usr/include/mach-o/loader.h
宝穗。
struct segment_command
{
uint32_t cmd;
uint32_t cmdsize;
char segname[16];
uint32_t vmaddr;
uint32_t vmsize;
uint32_t fileoff;
uint32_t filesize;
vm_prot_t maxprot;
vm_prot_t initprot;
uint32_t nsects;
uint32_t flags;
};
字段
??cmd
所有加載命令的結(jié)構(gòu)中都有此字段。設(shè)置LC_SEGMENT
對于次結(jié)構(gòu)體码秉。
??cmdsize
所有加載命令結(jié)構(gòu)體中都有此字段逮矛。對于這個結(jié)構(gòu),將這個字段設(shè)置為sizeof(segment_command)
加上后面所有section數(shù)據(jù)結(jié)構(gòu)的大小(sizeof(segment_command + (sizeof(section) * segment->nsect))
转砖。
??segname
一個用C字符串作為名字的段须鼎。這個字段值可以是任意順序的ASCII字符,Apple定義的段名以兩個下劃線開頭府蔗,并由大寫字母組成(如在__TEXT
和__DATA
中)晋控。該字段的長度固定為16字節(jié)。
??vmaddr
指向段在虛擬內(nèi)存地址中開始的位置姓赤。
??vmsize
指示此段占用的虛擬內(nèi)存字節(jié)數(shù)赡译。參見下面的filesize
大小說明。
??fileoff
指示數(shù)據(jù)文件被映射到虛擬內(nèi)存中不铆,相對于開始位置的偏移量蝌焚。
??filesize
指定了段在磁盤上面占用的字節(jié)數(shù)量。對于這些段狂男,它們在運行時要求的內(nèi)存要比構(gòu)建時多综看,vmsize
要比filesize
大。比如岖食,對于MH_EXECUTABLE
文件連接器生成的__PAGEZERO
段分配的虛擬內(nèi)存大小是0x1000但是文件大小是0红碑。因為__PAGEZERO
段中沒有數(shù)據(jù),這里是不會為它分配任何內(nèi)存的泡垃,直到運行時析珊。而且,靜態(tài)連接器經(jīng)常分配未初始化的數(shù)據(jù)在__DATA
段后面蔑穴;通過這些例子忠寻,vmsize
要大于filesize
。加載程序保證這種類型的任何內(nèi)存都是用零初始化的存和。
??maxprot
表示此段被允許的且受保護的最大虛擬內(nèi)存奕剃。
??initprot
表示此段受保護的初始化虛擬內(nèi)存衷旅。
??nsects
表示此加載命令后面節(jié)數(shù)據(jù)結(jié)構(gòu)的數(shù)量。
??flags
定義了一組標志纵朋,此段的加載會受到這組標志的影響:
-
SG_HIGHVM
--該段的文件內(nèi)容為虛擬內(nèi)存空間的高位部分;較低的部分是零填充(用于核心文件中的堆棧); -
SG_NORELOC
--這一段沒有任何東西被移動到它里面也沒有任何東西被移動到它里面柿顶。它可以安全更換,無需搬遷操软。
segment_command_64
表示由段組成的Mach-O文件的字節(jié)范圍嘁锯。這些字節(jié)被映射到加載器加載程序的地址空間。
struct segment_command_64
{
uint32_t cmd;
uint32_t cmdsize;
char segname[16];
uint64_t vmaddr;
uint64_t vmsize;
uint64_t fileoff;
uint64_t filesize;
vm_prot_t maxprot;
vm_prot_t initprot;
uint32_t nsects;
uint32_t flags;
};
section_64
定義64位節(jié)使用的元素聂薪。直接跟在一個segment_command_64
數(shù)據(jù)結(jié)構(gòu)后面的一組section_64
數(shù)據(jù)結(jié)構(gòu),segment_command_64
結(jié)構(gòu)中家乘,nsects
是這組數(shù)據(jù)的準確數(shù)量。
struct section_64
{
char sectname[16];
char segname[16];
uint64_t addr;
uint64_t size;
uint32_t offset;
uint32_t align;
uint32_t reloff;
uint32_t nreloc;
uint32_t flags;
uint32_t reserved1;
uint32_t reserved2;
};
字段
??secname
一個字符串藏澳,指定了這個節(jié)的名字仁锯。這個字段的值可以是任意序列的ASCII字符,蘋果定義的名字由兩個下劃線開始笆载,并且由小寫字母組成(比如__text
和__data
)扑馁。這個字段的長度固定為16字節(jié)。
??segname
表示包含此節(jié)的段的字符串名字凉驻。為了緊湊性腻要,中間對象文件--類型是MH_OBJECT
只包含一個段,并且把所有的節(jié)都放在這個一個段中涝登。靜態(tài)連接器在構(gòu)建最終產(chǎn)品時雄家,會將每個節(jié)放到指定的段中(任意類型文件不僅是MH_OBJECT
)
??addr
一個整數(shù)表示了節(jié)的虛擬內(nèi)存地址。
??size
一個整數(shù)標志此節(jié)占用的虛擬內(nèi)存的字節(jié)大小
??offset
一個整數(shù)表示此節(jié)在文件中的偏移胀滚。
??align
一個整數(shù)表示節(jié)的字節(jié)對齊方式趟济。這個整數(shù)是2的冪數(shù);比如咽笼,具有8字節(jié)對齊的節(jié)的對齊值為3
??reloff
指定此部分的第一個重定位項的文件偏移量的整數(shù)顷编。
??nreloc
一個整數(shù),用于指定位于本節(jié)的重定位項的數(shù)目剑刑。
??flags
這個整數(shù)被分為兩部分媳纬。最低有效8位包含節(jié)類型,最多的有效24位包含了一組標記施掏,這些標記表示節(jié)的其他屬性钮惠。這些類型和標記主要是靜態(tài)連接器和文件分析工具在使用,比如otool
七芭,決定了要怎么修改和顯示這些節(jié)素挽。下面是有效類型:
-
S_REGULAR
--這個節(jié)沒有特殊的類型。標準工具會創(chuàng)建這個類型的__TEXT
的__text
節(jié)狸驳。 -
S_ZEROFILL
--按需填充零的節(jié)--當這個section第一次讀取或者寫入的會后预明,每頁都會自動的被包含0的字節(jié)填充缩赛。 -
S_CSTRING_LITERALS
--這個節(jié)僅包含C的常量字符串。標準工具會創(chuàng)建此類型的__TEXT
段__cstring
section撰糠。 -
S_4BYTE_LITEBALS
--這個節(jié)僅包含4字節(jié)長的常量值峦筒。標準工具會創(chuàng)建此類型的__TEXT
的__literal4
節(jié)。 -
S_8BYTE_LITERALS
--這個節(jié)僅包含8字節(jié)長的常量值窗慎。標準工具會創(chuàng)建此類型的__TEXT
的__literal8
節(jié)。 -
S_LITERAL_POINTERS
--這個section僅包含常量值的指針卤材。 -
S_NON_LASY_SYMBOL_POINTERS
--這個section僅包含非懶加載符號指針遮斥。標準工具會創(chuàng)建此類型的__DATA
段的__nl_symbol_ptrs
節(jié) -
S_SYMBOL_STUBS
--這個section包含了符號樁(存根),標準工具會創(chuàng)建此類型的__TEXT
的__symbol_stub
節(jié)和__TEXT
的__picsymbol_stub
節(jié)扇丛。詳情請看Mach-O編程主題中的“動態(tài)代碼生成”术吗。 -
S_MOD_INIT_FUNC_POINTERS
--這個section包含了模塊初始化函數(shù)的指針。標準工具會此類型的__DATA
的__mod_init_func
節(jié)帆精。 -
S_MOD_TERM_FUNC_POINTERS
--這個section包含了模塊終止函數(shù)的指針较屿。標準工具會創(chuàng)建此類型的__DATA
的__mod_term_func
節(jié)。 -
S_COALESCED
--本節(jié)包含由靜態(tài)鏈接器(可能還有動態(tài)鏈接器)合并的符號卓练。多個文件可以包含同一符號的合并定義隘蝎,而不會導致多個定義符號錯誤。 -
S_GB_ZEROFILL
--這是一個零填充隨需應變的section襟企。他可能大于4GB嘱么。這個section一定要被放在僅包含零填充section的段中。如果你將一個零填充section放到一個非零填充的段中顽悼,會導致那些section用31位偏移訪問不到曼振。這個結(jié)果源于一個事實,即一個零填充的部分的大小可以大于4 GB(在32位地址空間中)蔚龙。正如結(jié)果一樣冰评,靜態(tài)連接器是不能構(gòu)建輸出文件的。
討論
在Mach-O文件中的每個section都包含類型和一組屬性標記木羹。在中間對象文件中甲雅,這個類型和屬性決定了靜態(tài)連接器怎么將section拷貝到最終產(chǎn)品中。對象文件分析工具(例如otool)用類型和屬性決定怎么讀取和現(xiàn)實這些section汇跨。有些section類型和屬性是動態(tài)連接器用到的务荆。
這些是符號類型和屬性的重要靜態(tài)鏈接變體:
- Regular sections.在常規(guī)部分中,中間對象文件中只能存在外部符號的一個定義穷遂。如果發(fā)現(xiàn)任何重復的外部符號定義函匕,靜態(tài)鏈接器將返回一個錯誤。
- Coalesced sections.在最終產(chǎn)品中蚪黑,靜態(tài)連接器在每個合并section符號定義中只有一個實例盅惜。為了支持復雜的語言(比如C++的vtables和RTTI)編譯器會為每個中間對象文件創(chuàng)建一個特別的符號定義中剩。然后,靜態(tài)連接器和動態(tài)連接器將多個定義減少到程序用的單個定義抒寂。
- Coalesced sections with weak definitions弱引用符號定義可能僅顯示在合并section中结啼。當用靜態(tài)連接器查找一個符號的多個定義時,它將忽略一些合并符號定義中被設(shè)置為弱定義屈芜。如果這里沒有非弱定義郊愧,第一個弱引用定義將被替換。這樣設(shè)計是為了支持C++模板井佑;它允許一個明確的模板實例去復寫模糊的另一個模板属铁。C++編譯器會將明確的定義放到工整的section中,而且會把模糊的定義放到合并section中躬翁,標記為若定義焦蘑。用弱定義構(gòu)建的中間目標文件(以及靜態(tài)存檔庫)只能在Mac OS X v10.2及更高版本的靜態(tài)鏈接器中使用。如果最終產(chǎn)品不會被用到macOS更好版本中盒发,就不會包含若定義例嘱。
section_64
定義了64位section用到的元素。是一個section_64
數(shù)據(jù)結(jié)構(gòu)的數(shù)組宁舰,直接放在segment_command_64
數(shù)據(jù)結(jié)構(gòu)后面拼卵,這個數(shù)組的數(shù)量由segment_command_64
機構(gòu)中的nsects
字段決定。
struct section_64
{
char sectname[16];
char segname[16];
uint64_t addr;
uint64_t size;
uint32_t offset;
uint32_t align;
uint32_t reloff;
uint32_t nreloc;
uint32_t flags;
uint32_t reserved1;
uint32_t reserved2;
};
字段
sectname
一個字符串指定了section的名稱蛮艰。這個字段值可以是任意序列的ASCII字符间学,然而蘋果規(guī)定section名字的開始是兩個下劃線,后面由小寫字母組成印荔。這個字段最多16個字節(jié)低葫。
segname
一個字符串指定了最終包含此section的段的名字。為了緊湊性仍律,類型為MH_OBJECT
的中間對象文件僅包含一個段嘿悬,并將所有的section放到里面。靜態(tài)連接器水泉。在靜態(tài)連接器構(gòu)建最終產(chǎn)品時善涨,回見每個section放到指定的段中。(任意文件不僅限于類型是MH_OBJECT
)草则。
addr
整數(shù)钢拧,指定了section在虛擬內(nèi)存中的地址。
size
整數(shù)炕横,指定了section在虛擬內(nèi)存中所占大小源内。
offset
整數(shù),指定了section在文件中的偏移份殿。
align
整數(shù)膜钓,指定了section的字節(jié)對齊方式嗽交。這個整數(shù)是2的冪次方;比如颂斜,一個八字節(jié)對齊方式的section的align值是3.
reloff
整數(shù)夫壁,指定了此section第一個重定位入口的文件偏移。
nreloc
整數(shù)沃疮,指定了此section重定位入口的數(shù)量盒让,位于reloff。
??flags
這個整數(shù)被分為兩部分司蔬。最低有效8位包含節(jié)類型糯彬,最多的有效24位包含了一組標記,這些標記表示節(jié)的其他屬性葱她。這些類型和標記主要是靜態(tài)連接器和文件分析工具在使用,比如otool
似扔,決定了要怎么修改和顯示這些節(jié)吨些。下面是有效類型:
-
S_REGULAR
--這個節(jié)沒有特殊的類型。標準工具會創(chuàng)建這個類型的__TEXT
的__text
節(jié)炒辉。 -
S_ZEROFILL
--按需填充零的節(jié)--當這個section第一次讀取或者寫入的會后豪墅,每頁都會自動的被包含0的字節(jié)填充。 -
S_CSTRING_LITERALS
--這個節(jié)僅包含C的常量字符串黔寇。標準工具會創(chuàng)建此類型的__TEXT
段__cstring
section偶器。 -
S_4BYTE_LITEBALS
--這個節(jié)僅包含4字節(jié)長的常量值。標準工具會創(chuàng)建此類型的__TEXT
的__literal4
節(jié)缝裤。 -
S_8BYTE_LITERALS
--這個節(jié)僅包含8字節(jié)長的常量值屏轰。標準工具會創(chuàng)建此類型的__TEXT
的__literal8
節(jié)。 -
S_LITERAL_POINTERS
--這個section僅包含常量值的指針憋飞。 -
S_NON_LASY_SYMBOL_POINTERS
--這個section僅包含非懶加載符號指針霎苗。標準工具會創(chuàng)建此類型的__DATA
段的__nl_symbol_ptrs
節(jié) -
S_SYMBOL_STUBS
--這個section包含了符號樁(存根),標準工具會創(chuàng)建此類型的__TEXT
的__symbol_stub
節(jié)和__TEXT
的__picsymbol_stub
節(jié)榛做。詳情請看Mach-O編程主題中的“動態(tài)代碼生成”唁盏。 -
S_MOD_INIT_FUNC_POINTERS
--這個section包含了模塊初始化函數(shù)的指針。標準工具會此類型的__DATA
的__mod_init_func
節(jié)检眯。 -
S_MOD_TERM_FUNC_POINTERS
--這個section包含了模塊終止函數(shù)的指針厘擂。標準工具會創(chuàng)建此類型的__DATA
的__mod_term_func
節(jié)。 -
S_COALESCED
--本節(jié)包含由靜態(tài)鏈接器(可能還有動態(tài)鏈接器)合并的符號锰瘸。多個文件可以包含同一符號的合并定義刽严,而不會導致多個定義符號錯誤。 -
S_GB_ZEROFILL
--這是一個零填充隨需應變的section避凝。他可能大于4GB港庄。這個section一定要被放在僅包含零填充section的段中倔既。如果你將一個零填充section放到一個非零填充的段中,會導致那些section用31位偏移訪問不到鹏氧。這個結(jié)果源于一個事實渤涌,即一個零填充的部分的大小可以大于4 GB(在32位地址空間中)。正如結(jié)果一樣把还,靜態(tài)連接器是不能構(gòu)建輸出文件的实蓬。
以下是一個部分可能的屬性:
-
S_ATTR_PURE_INSTRUCTIONS
--這個section僅包含可執(zhí)行機器指令。標準工具會為__TEXT
的__text
吊履、__TEXT
的__symbol_stub
和__TEXT
的__picsymbol_stub
設(shè)置此flag安皱。 -
S_ATTR_SOME_INSTRUCTIONS
--這個section包含了可執(zhí)行機器指令。 -
S_ATTR_NO_TOC
--section包含了合并符號艇炎,而且它是不會被放到靜態(tài)歸檔庫內(nèi)容的表中的酌伊。 -
S_ATTR_EXT_RELOC
--section包含了一定被重定位的引用。這些引用涉及到了其他文件存在的數(shù)據(jù)(未定義的符號)。為了支持重定位擴展泉唁,段中最大受保護的虛擬內(nèi)存包含的section必須允許被讀寫裙椭。 -
S_ATTR_LOC_RELOC
--section包含了一定被重定位的引用。這些引用涉及到了文件中的數(shù)據(jù)奏候。 -
S_ATTR_STRIP_STATIC_SYMS
--如果鏡像mach_header
頭部中的結(jié)構(gòu)設(shè)置了MH_DYLDLINK
標志,那么這個section的靜態(tài)符號是可以被去掉的唇敞。 -
S_ATTR_NO_DEAD_STRIP
--這部分不能拆裝蔗草。詳情可以參考Xcode用戶指南中的"Linking"。 -
S_ATTR_LIVE_SUPPORT
--如果引用的代碼是活躍的疆柔,但引用是不可檢測的咒精,則此部分不能被拆裝。
討論
在Mach-O文件中的每個section都包含類型和一組屬性標記旷档。在中間對象文件中狠轻,這個類型和屬性決定了靜態(tài)連接器怎么將section拷貝到最終產(chǎn)品中。對象文件分析工具(例如otool)用類型和屬性決定怎么讀取和現(xiàn)實這些section彬犯。有些section類型和屬性是動態(tài)連接器用到的向楼。
這些是符號類型和屬性的重要靜態(tài)鏈接變體:
- Regular sections.在常規(guī)部分中,中間對象文件中只能存在外部符號的一個定義谐区。如果發(fā)現(xiàn)任何重復的外部符號定義湖蜕,靜態(tài)鏈接器將返回一個錯誤。
- Coalesced sections.在最終產(chǎn)品中宋列,靜態(tài)連接器在每個合并section符號定義中只有一個實例昭抒。為了支持復雜的語言(比如C++的vtables和RTTI)編譯器會為每個中間對象文件創(chuàng)建一個特別的符號定義。然后,靜態(tài)連接器和動態(tài)連接器將多個定義減少到程序用的單個定義灭返。
- Coalesced sections with weak definitions弱引用符號定義可能僅顯示在合并section中盗迟。當用靜態(tài)連接器查找一個符號的多個定義時,它將忽略一些合并符號定義中被設(shè)置為弱定義熙含。如果這里沒有非弱定義罚缕,第一個弱引用定義將被替換。這樣設(shè)計是為了支持C++模板怎静;它允許一個明確的模板實例去復寫模糊的另一個模板邮弹。C++編譯器會將明確的定義放到工整的section中,而且會把模糊的定義放到合并section中蚓聘,標記為若定義腌乡。用弱定義構(gòu)建的中間目標文件(以及靜態(tài)存檔庫)只能在Mac OS X v10.2及更高版本的靜態(tài)鏈接器中使用。如果最終產(chǎn)品不會被用到macOS更好版本中与纽,就不會包含若定義。
twolevel_hints_command
定義了LC_TWOLEVEL_HINTS
加載命令的一些屬性。
struct twolevel_hints_command
{
uint32_t cmd;
uint32_t cmdsize;
uint32_t offset;
uint32_t nhints;
};
字段
cmd 所有加載命令的結(jié)構(gòu)都有裂七,次結(jié)構(gòu)設(shè)置為LC_TWOLEVEL_HINTS
仓坞。
cmdsize 所有下載命令的結(jié)構(gòu)都有。次結(jié)構(gòu),設(shè)置為sizeof(twoevel_hints_command)
。
offset 表示從文件開始到twolevel_hint
數(shù)據(jù)結(jié)構(gòu)的數(shù)組的偏移量蒿赢,稱為二級命名空間提示表羡棵。
nhints 位于偏移處二級命名數(shù)據(jù)結(jié)構(gòu)的數(shù)量觉至。
討論
當靜態(tài)連接器構(gòu)建一個二級命名空間鏡像時语御,會增加LC_TWOLEVEL_HINTS
加載命令和二級命名空間提示表在輸出文件中。
特殊注意事項
默認耿导,在HM_BUNDLE
文件中ld
是不包含LC_TWOLEVEL_HINTS
命令或者耳機命名空間提示表的殖氏,因為這樣命令的存在會導致Mac OS X v10.0裝載動態(tài)連接器時會崩潰婚瓜。如果你的代碼僅運行在Mac OS X v10.1及之后版本宝鼓,明確二級命名空間提示表可以被使用。
twolevel_hint
指定二級命名空間提示變的一個入口
struct twolevel_hint {
uint32_t isub_image:8,
itoc:24;
};
字段
isub_image 定義符號中的子鏡像巴刻。它是構(gòu)成傘形鏡像的鏡像列表的索引愚铡。如果該字段為0,則符號在傘鏡像本身中胡陪。如果鏡像不是傘形框架或庫沥寥,則此字段為0。
itoc 將符號索引放入由isub_image字段指定的鏡像內(nèi)容表中柠座。
討論
兩級名稱空間提示表為動態(tài)鏈接器提供了建議的位置邑雅,以便開始在當前鏡像所鏈接的庫中搜索符號。
在兩級名稱空間映像中妈经,每個未定義的符號(即N_UNDF或N_PBUD類型的每個符號)在同一索引處的兩級提示表中都有相應的條目淮野。
當構(gòu)建二級命名空間鏡像時,靜態(tài)連接器會添加LC_TWOLEVEL_HINTS
加載命令和二級命名空間提示表到輸出文件中吹泡。
默認情況下骤星,連接器是不會將LC_TWOLEVEL_HINTS
加載命令或者二級命名空間提示表加到MH_BUNDLE
文件中的,因為這個load命令的存在會導致Mac OS X v10.0附帶的動態(tài)鏈接器版本崩潰爆哑。
lc_str
定義一個可變長度字符串洞难。
union lc_str
{
uint32_t offset;
#ifndef __LP64__
char *ptr;
#endif
};
字段
offset 長整型。從包含此字符串的加載命令開始到字符串數(shù)據(jù)的開始的字節(jié)偏移揭朝。
ptr 指向字節(jié)數(shù)組的指針队贱。在運行時,這個指針包含了字符串數(shù)據(jù)的虛擬內(nèi)存地址萝勤。在Mach-O文件中不適用ptr
字段露筒。
討論
加載命令存儲了可變長度的數(shù)據(jù)呐伞,比如使用lc_str
數(shù)據(jù)結(jié)構(gòu)的庫名稱敌卓。除非另外指定,數(shù)據(jù)由一個C字符串組成伶氢。
指向的數(shù)據(jù)存儲在load命令之后趟径,而且大小是加到加載命令大小上的。這個字符串應該以null終止癣防;任何額外的字節(jié)都應該是null蜗巧。通過減少加載命令數(shù)據(jù)結(jié)構(gòu)中的字段cmdsize
來決定字符串的大小。
dylib
定義動態(tài)鏈接器使用的數(shù)據(jù)蕾盯,以便將共享庫與鏈接到共享庫的文件進行匹配幕屹。專用于dylib_command(第32頁)數(shù)據(jù)結(jié)構(gòu)。
struct dylib {
union lc_str name;
uint_32 timestamp;
uint_32 current_version;
uint_32 compatibility_version;
};
字段
name 類型為lc_str
的數(shù)據(jù)結(jié)構(gòu)。表示共享庫的名稱望拖。
timetamp 構(gòu)建共享庫數(shù)據(jù)的時間
current_version 共享庫當前版本號
compatibility_version 共享庫的兼容版本號
dylib_command
定義了LC_LOAD_DYLIB
和LC_ID_DYLIB
加載命令的屬性
struct dylib_command {
uint_32 cmd;
uint_32 cmdsize;
struct dylib dylib;
};
字段
cmd 所有加載命令結(jié)構(gòu)都有的字段渺尘。次結(jié)構(gòu)中,設(shè)置為LC_LOAD_DYLIB
,LC_LOAD_WEAK_DYLIB
或者LC_ID_DYLIB
说敏。
cmdsize 所有加載命令結(jié)構(gòu)都有的字段鸥跟。此結(jié)構(gòu)中,設(shè)置為sizeof(dylib_command)加上由dylib字段的name字段指向的數(shù)據(jù)的大小盔沫。
dylib 類型為dylib
的數(shù)據(jù)結(jié)構(gòu)医咨。表示共享庫的屬性。
討論
文件鏈接到的每個共享庫架诞,靜態(tài)鏈接器創(chuàng)建一個LC_LOAD_DYLIB
命令拟淮,并將其dylib字段設(shè)置為目標庫的LC_ID_DYLD
裝載命令的dylib字段的值。所有的LC_LOAD_DYLIB
命令一起組成一個列表侈贷,根據(jù)文件中的位置排序惩歉,最早的LC_LOAD_DYLIB
命令放在第一個。對于二級命名空間文件俏蛮,符號表中未定義的符號項通過索引引用到此列表中的父共享庫撑蚌。這個索引被稱為library ordinal
,并且它被存儲在nlist
數(shù)據(jù)結(jié)構(gòu)中的n_desc
字段中搏屑。
在運行時争涌,動態(tài)連接器用LC_LOAD_DYLIB
命令dyld
字段中的name字段定位共享庫。如果找到共享庫辣恋,動態(tài)連接器拿LC_LOAD_DYLIB
加載命令中版本信息和動態(tài)庫的做比較亮垫。要使動態(tài)鏈接器成功鏈接共享庫,共享庫的兼容版本必須小于或等于LC_LOAD_DYLIB
命令中的兼容版本伟骨。
動態(tài)連接器用時間戳決定是否能被預綁定信息饮潦。函數(shù)NSVersionOfRunTimeLibrary
返回的當前版本信息,由你來決定你程序用到庫的版本携狭。
dylinker_command
定義了LC_LOAD_DYILNKER
和LC_ID_DYLINKER
加載命令的屬性继蜡。
struct dylinker_command {
uint32_t cmd;
uint32_t cmdsize;
union lc_str name;
};
字段
cmd 所有加載命令結(jié)構(gòu)都有。此結(jié)構(gòu)中逛腿,設(shè)置為LC_ID_DYLINKER
或者LC_LOAD_DYLINKER
稀并。
cmdsize 所有加載命令結(jié)構(gòu)都有。此結(jié)構(gòu)中单默,設(shè)置為sizeof(dylinker_command)
加上name
字段指向的數(shù)據(jù)大小碘举。
name 一個類型為lc_str
的數(shù)據(jù)結(jié)構(gòu)。表示動態(tài)連接器的名稱搁廓。
討論
每個動態(tài)鏈接的可執(zhí)行文件都包含一個指定動態(tài)連接器名稱的LC_LOAD_DYLINKER
指令引颈,這個名稱是內(nèi)核為了加載可執(zhí)行文件所必需的耕皮。動態(tài)連接器本身的名稱用LC_ID_DYLINKER
加載指令表示。
prebound_dylib_command
定義了LC_PREBOUND_DYLIB
加載指令的屬性蝙场。對于預綁定的可執(zhí)行文件鏈接到的每個庫明场,靜態(tài)鏈接器添加一個LC_PREBOUND_DYLIB
指令。
struct prebound_dylib_command {
uint32_t cmd;
uint32_t cmdsize;
union lc_str name;
uint32_t nmodules;
union lc_str linked_modules;
};
字段
cmd 每個加載指令結(jié)構(gòu)中都有的字段李丰。對于此結(jié)構(gòu)苦锨,設(shè)置為LC_PERBOUND_DYLIB
。
cmdsize 每個加載指令結(jié)構(gòu)中都有的字段趴泌。對于此結(jié)構(gòu)舟舒,設(shè)置為sizeof(prebound_dylib_command)
加上name
和linked_moudules
字段指向數(shù)據(jù)的大小。
name 一個類型為lc_str
的數(shù)據(jù)結(jié)構(gòu)嗜憔。表示預綁定共享庫的名稱秃励。
nmodules 一個整數(shù)。表示預綁定共享庫包含模塊的數(shù)量吉捶。linked_modules
字符串的大小是(modules / 8) + (nmodules % 8)
夺鲜。
linked_modules 一個類型為lc_str
的數(shù)據(jù)結(jié)構(gòu)。通常呐舔,這個數(shù)據(jù)結(jié)構(gòu)定義了一個C字符串的偏移币励;這種用法中,它是一個變長位集珊拼,包含了每個module的位食呻。沒個位代表了與之對應的module是否被連接到當前文件當中的module,1為yes澎现,0位no仅胞。第一個module的位是第一個字節(jié)的低位。
thread_command
定義了LC_THREAD
和LC_UNIXTHREAD
加載指令的屬性剑辫。這個指令的數(shù)據(jù)對于每個架構(gòu)都是特殊的干旧,在thread_status.h
中顯示,位于架構(gòu)目錄/usr/include/mach
中妹蔽。
struct thread_command {
uint32_t cmd;
uint32_t cmdsize;
/* uint32_t flavor;*/
/* uint32_t count; */
/* struct cpu_thread_state state;*/
};
字段
cmd 所有加載指令結(jié)構(gòu)都有的字段椎眯。對于此結(jié)構(gòu),設(shè)置為LC_THREAD
或者LC_UNIXTHREAD
讹开。
cmdsize 設(shè)置為sizeof(thread_command)
加上flavor
和count
字段的大小再加上特定CPU線程狀態(tài)數(shù)據(jù)結(jié)構(gòu)的大小盅视。
flavor 指定線程狀態(tài)數(shù)據(jù)結(jié)構(gòu)的特定樣式的整數(shù)捐名〉┩颍可以參考你目標架構(gòu)中的thread_status.h
文件。
count 線程狀態(tài)數(shù)據(jù)的大小镶蹋,以32位整數(shù)的數(shù)量為單位成艘。線程狀態(tài)數(shù)據(jù)結(jié)構(gòu)必須填充到32位對齊方式赏半。
routines_command_64
定義了LC_ROUTINES_64
加載指令的屬性,被用在64位架構(gòu)體系中淆两。描述了共享庫初始化函數(shù)的位置断箫,這是動態(tài)鏈接器在允許調(diào)用庫中的任何例行程序之前調(diào)用的函數(shù)。
struct routines_command_64 {
uint32_t cmd;
uint32_t cmdsize;
uint64_t init_address;
uint64_t init_module;
uint64_t reserved1;
uint64_t reserved2;
uint64_t reserved3;
uint64_t reserved4;
uint64_t reserved5;
uint64_t reserved6;
};
字段
cmd 所有指令結(jié)構(gòu)都有的字段秋冰。此結(jié)構(gòu)中仲义,設(shè)置為LC_ROUTINES_64
cmdsize 所有的指令結(jié)構(gòu)都有的字段。設(shè)置為sizeof(routines_command_64)
init_address 指定初始化函數(shù)的虛擬內(nèi)存地址剑勾。
init_module 表示包含初始化函數(shù)模型的模型表中的索引值埃撵。
Symbol Table and Related Data Structure
兩個加載指令LC_SYMTAB
和LC_DYSYMTAB
,描述了符號表的大小和位置以及其他元數(shù)據(jù)。本節(jié)中列出的其他數(shù)據(jù)結(jié)構(gòu)表示符號表本身虽另。
symtab_command
定義了LC_SYMTAB
加載指令的屬性暂刘。描述了符號表數(shù)據(jù)結(jié)構(gòu)的大小和位置。
struct symtab_command {
uint_32 cmd;
uint_32 cmdsize;
uint_32 symoff;
uint_32 nsyms;
uint_32 stroff;
uint_32 strsize;
};
字段
cmd 設(shè)置為LC_SYMTAB
cmdsize 設(shè)置為sizeof(symtab_command)
symoff 從文件開始到符號表入口位置的偏移量捂刺。符號表是nlist
數(shù)據(jù)結(jié)構(gòu)的數(shù)組谣拣。
nsyms 符號表入口數(shù)量的值
stroff 從鏡像開始處到字符串表位置的偏移量。
strsize 字符串表的大凶逭埂(用字節(jié)表示)
討論
LC_SYMTAB
應該同時存在于靜態(tài)鏈接和動態(tài)鏈接的文件類型中森缠。
nlist_64
描述了64位架構(gòu)的符號表中的入口
struct nlist_64 {
union {
uint32_t n_strx;
} n_un;
uint8_t n_type;
uint8_t n_sect;
uint16_t n_desc;
uint64_t n_value;
};
字段
n_un 將索引保存到字符串表中的聯(lián)合,用0表示空字符串("")
n_type 由使用四位掩碼訪問的數(shù)據(jù)組成的字節(jié)值:
-
N_STAB
(0xe0)--如果設(shè)置了這三位的任意一位仪缸,這個符號就代表符號調(diào)試表(stab)的條目辅鲸。這個例子中,整個n_type
字段被解釋為stab
值腹殿。