iOS強(qiáng)化 : 熟悉Mach-O 文件

前言:

關(guān)于Mach-O 文件财边,在iOS App 加載流程知識中已經(jīng)提到過肌括。看到優(yōu)秀簡書進(jìn)行一波兒轉(zhuǎn)載酣难。本文轉(zhuǎn)載http://www.reibang.com/p/d641e8270108

整體結(jié)構(gòu)大致如下:


Mach-O定義

Mach-O(Mach Object)是macOS谍夭、iOS、iPadOS存儲程序和庫的文件格式憨募。對應(yīng)系統(tǒng)通過應(yīng)用二進(jìn)制接口(application binary interface紧索,縮寫為ABI) 來運(yùn)行該格式的文件。

Mach-O格式用來代替BSD系統(tǒng)的a.out格式菜谣。Mach-O文件格式保存了在編譯過程鏈接過程中產(chǎn)生的機(jī)器代碼和數(shù)據(jù)珠漂,從而為靜態(tài)鏈接動(dòng)態(tài)鏈接的代碼提供了單一文件格式。

Mach-O = 文件配置 + 二進(jìn)制文件

除了可執(zhí)行文件之外尾膊,還有一些文件也是Mach-O格式媳危,比如:

  • 目標(biāo)文件.o
  • 庫文件
    .a
    .dylib
    Framework
  • dyld(動(dòng)態(tài)鏈接器)
  • .dsym(符號表)

由此我們知道,可執(zhí)行文件只是Mach-O的一種冈敛,因此我們將Mach-O文件分為以下幾種:

名稱 注釋
Mach-O Object 目標(biāo)文件
Mach-O ececutable 可執(zhí)行文件
Mach-O dynamically 動(dòng)態(tài)庫文件
Mach-O dynamic linker 動(dòng)態(tài)鏈接器文件
Mach-O DSYM companion 符號表文件

通用二進(jìn)制文件(Universal binary)

支持多架構(gòu)的Mach-O ececutable(可執(zhí)行文件)被稱為:通用二進(jìn)制文件待笑,即多種架構(gòu)都可讀取運(yùn)行。

  • 通用二進(jìn)制文件具有以下特性:
    1抓谴、Apple 提出的一種程序代碼暮蹂,能夠同時(shí)適配多種架構(gòu)的二進(jìn)制文件寞缝。
    2、同一個(gè)程序包中仰泻,同時(shí)為多種架構(gòu)提供最理想的性能荆陆。
    3、通用二進(jìn)制應(yīng)用程序通常比單一平臺二進(jìn)制程序大集侯,因?yàn)樾枰鎯Χ喾N代碼被啼。
    4、由于多種架構(gòu)之間有共通的非執(zhí)行資源浅悉,所以并不會比單一架構(gòu)的兩倍大趟据。
    5、程序在執(zhí)行的時(shí)候只調(diào)用一部分代碼术健,運(yùn)行起來不需要額外的內(nèi)存汹碱。

  • 那么多種架構(gòu)是什么意思呢?下面我們通過file指令來看一下我們的可執(zhí)行文件:

    image

    通過上圖荞估,我們可以看到test可執(zhí)行文件的類型是Mach-O咳促;架構(gòu)是x86_64,這是我們用模擬器運(yùn)行的可執(zhí)行文件勘伺。
    我們再實(shí)際開發(fā)中遇到的設(shè)置arm64&armv7這些都是對應(yīng)的架構(gòu):

    名字 注釋
    arm64 真機(jī)64位處理器需要arm64架構(gòu)(iphone6,iphone6p以上的真機(jī))
    armv7s 真機(jī)32位處理器 ( ipnone5,iphone5s真機(jī)/armv7s)
    armv7 真機(jī)32位處理器 (iphone4真機(jī)/armv7)
    x86_64 模擬器64位處理器 (iphone6以上的模擬器)
    i386 模擬器32位處理器 (iphone5,iphone5s以下的模擬器)
  • Tips:
    在Xcode中設(shè)置Arhitectures跪腹,Debug屬性設(shè)置為NO的時(shí)候,會編譯支持所有架構(gòu)的版本飞醉,編譯的速度會變慢冲茸,設(shè)置為yes的時(shí)候,只編譯當(dāng)前的Arhitectures版本缅帘,編譯速度快轴术。

    image

通用二進(jìn)制文件的拆分 與 組合

  • MachOView中,通用二進(jìn)制文件也被叫做Fat binary钦无。
    這種二進(jìn)制文件是可以拆分逗栽、或者重新組合
    ?? 注意這里我采用的是真機(jī)測試,Scheme對應(yīng)的Build Configuration選用Release模式失暂。(關(guān)于Xcode環(huán)境的配置彼宠,有不清楚的同學(xué)可以看這里:Xcode 多環(huán)境的配置)
    ?? 這里還有一點(diǎn)要注意:測試的時(shí)候,如果工程只包含一種架構(gòu)弟塞,此時(shí)要手動(dòng)添加其他架構(gòu)凭峡。

  • 我們可以通過file指令,也可以通過lipo -info指令查看二進(jìn)制文件支持的架構(gòu):

    image
    image

可以看到决记,目前test可執(zhí)行程序支持arm64arm_v7兩種架構(gòu)摧冀。
那么下面我們先進(jìn)行文件拆分:

拆分Fat binary
linpo mach-o文件名 -thin 要拆分的架構(gòu)名 -output 拆分出來的文件名

拆分前的ipa包內(nèi)容:

image

拆分后的ipa包內(nèi)容:

image

?? 拆分后源文件并不會發(fā)生改變,類似于從源文件中copy出來一個(gè)架構(gòu)單一的二進(jìn)制文件,注意這里不是單獨(dú)的分離架構(gòu)按价。

image
合并 Fat binary
lipo -create macho_arm64 macho_armv7 -output newTest

合并之后的文件與原文件并無差異,我們可以通過哈希值也看一下:

image

Mach-O 文件結(jié)構(gòu)

  • Mach-O文件主要由 3 部分組成:
名字 注釋
Mach Header (Mach-O頭) 描述了Mach-OCPU架構(gòu)笙瑟、文件類型以及加載命令等信息
Load Commands (加載命令) 描述了文件中數(shù)據(jù)的具體組織結(jié)構(gòu)楼镐,不同的數(shù)據(jù)類型用不同的加載命令表示
Data (數(shù)據(jù)區(qū)) Data中每一個(gè)段 (Segment) 的數(shù)據(jù)都保存在這里,都擁有一個(gè)或多個(gè)Section往枷,用來存儲數(shù)據(jù)和代碼
image

?? 既然Mach-O是二進(jìn)制文件框产,那么它又是怎么知道哪一塊內(nèi)容是Load commands,哪一塊又是Header的呢错洁?
其實(shí)這里涉及到一個(gè)概念叫做結(jié)構(gòu)體對齊秉宿,簡單的講就是:按照一定的規(guī)則組合到一起,再按照既定的規(guī)則拆分就可以了屯碴。

Mach Header
image

可以看到Mach Header里面有很多的Description(描述)那么對應(yīng)的都是什么意思呢描睦?
我們可以在工程中搜索一下,使用快捷鍵(command + shift + o) 搜索load.h文件导而,打開該文件忱叭,由于是當(dāng)前是64位的,所以找到:

/*
 * The 64-bit mach header appears at the very beginning of object files for
 * 64-bit architectures.
 */
struct mach_header_64 {
    uint32_t    magic;      /* mach magic number identifier */
    cpu_type_t  cputype;    /* cpu specifier */
    cpu_subtype_t   cpusubtype; /* machine specifier */
    uint32_t    filetype;   /* type of file */
    uint32_t    ncmds;      /* number of load commands */
    uint32_t    sizeofcmds; /* the size of all the load commands */
    uint32_t    flags;      /* flags */
    uint32_t    reserved;   /* reserved */
};

load.h文件中mach_header_64mach_header(32位的頭文件)多了一個(gè)保留字段

uint32_t    reserved;   /* reserved */

mach_header是鏈接器加載的時(shí)候最先讀取的內(nèi)容今艺,它決定了一些基礎(chǔ)架構(gòu)韵丑,系統(tǒng)類型,指令條數(shù)等信息虚缎。

Load Commands

Load Commands 詳細(xì)保存著加載指令的內(nèi)容撵彻,告訴鏈接器如何去加載當(dāng)前的Mach-O文件。
那么每一條Load Command對應(yīng)的又是什么意思呢实牡?
同樣的我們也可以在load.h搜索的到陌僵,我們以LC_SEGMENT_64為例:

image

下面我們列舉一些常見的:

名字 注釋
LC_SEGMENT_64 將文件中的段映射到進(jìn)程地址空間中
LC_DYLD_INFO_ONLY 加載動(dòng)態(tài)鏈接庫信息(重定向地址、弱引用綁定铲掐、懶加載綁定拾弃、開放函數(shù)等的偏移值信息)
LC_SYMTAB 載入符號表地址
LC_DYSYMTAB 載入動(dòng)態(tài)符號表地址
LC_LOAD_DYLINKER 加載動(dòng)態(tài)鏈接器
LC_UUID 唯一標(biāo)識,crash解析中也會用到摆霉,檢查dysm文件和crash文件是否匹配
LC_VERSION_MIN_MACOSX / LC_VERSION_MIN_IPHONEOS 二進(jìn)制文件支持的最底操作系統(tǒng)版本
LC_SOURCE_VERSION 構(gòu)建二進(jìn)制文件使用的源代碼版本
LC_MAIN 設(shè)置程序主線程的入口地址和棧大小(這也就是為什么我們的程序每次運(yùn)行都是從main()進(jìn)來的原因)
LC_ENCRYPTION_INFO_64 獲取加密信息
LC_LOAD_DYLIB 加載額外的動(dòng)態(tài)庫
LC_FUNCTION_STARTS 函數(shù)起始地址表
LC_DATA_IN_CODE 定義在代碼段(__text)內(nèi)的非指令表
LC_CODE_SIGNATURE 應(yīng)用的簽名信息
Data

Data段又分為:__TEXT段 和 __DATA

  • __TEXT
    代碼的讀取是從__TEXT段開始讀取的豪椿,其中不同的__TEXT代表的意思如下:
名字 注釋
__text 主程序代碼
__cstring C 語言字符串
__const const關(guān)鍵字修飾的常量
__stubs 用于Stub的占位代碼,很多地方稱之為樁代碼
__stubs_helper 當(dāng)Stub無法找到真正的符號地址后的最終指向
__objc_methname OC 方法名
__objc_methtype OC 方法類型
__objc_classname OC 類名
  • __DATA
    __DATA段在內(nèi)存中緊跟在__TEXT段之后
名字 注釋
__got 全局偏移表
__la_symbol_ptr lazy binding的指針表携栋,表中的指針一開始都指向__stub_helper
__cfstring 工程中使用的Core Foundation字符串(CFStringRefs)
__objc_classlist OC 類列表
__objc_protolist OC protocol列表
__objc_imginfo OC 鏡像信息
__const 沒有初始化過的常量
__objc_selfrefs OC 引用的SEL列表
__objc_protorefs OC 引用的protocol列表
__objc_superrefs OC 引用的父類列表
__objc_ivar OC ivar信息
__objc_data class信息
__bss BSS搭盾,存放 未初始化的全局變量,就是常說的靜態(tài)內(nèi)存分配
__data 初始化的可變數(shù)據(jù)

?? 這里有一點(diǎn)大家需要注意婉支,系統(tǒng)庫的方法在我們自己的Mach-O文件里面是找不到的鸯隅,它存放在共享緩存區(qū)。那么我們自己的Mach-O文件又怎么去調(diào)用這些系統(tǒng)方法實(shí)現(xiàn)呢?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蝌以,一起剝皮案震驚了整個(gè)濱河市炕舵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌跟畅,老刑警劉巖咽筋,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異徊件,居然都是意外死亡奸攻,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門虱痕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來睹耐,“玉大人,你說我怎么就攤上這事部翘∠跹担” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵略就,是天一觀的道長捎迫。 經(jīng)常有香客問我,道長表牢,這世上最難降的妖魔是什么窄绒? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮崔兴,結(jié)果婚禮上彰导,老公的妹妹穿的比我還像新娘。我一直安慰自己敲茄,他們只是感情好位谋,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著堰燎,像睡著了一般掏父。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上秆剪,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天赊淑,我揣著相機(jī)與錄音,去河邊找鬼仅讽。 笑死陶缺,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的洁灵。 我是一名探鬼主播饱岸,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了苫费?” 一聲冷哼從身側(cè)響起汤锨,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎百框,沒想到半個(gè)月后泥畅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡琅翻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了柑贞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片方椎。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖钧嘶,靈堂內(nèi)的尸體忽然破棺而出棠众,到底是詐尸還是另有隱情,我是刑警寧澤有决,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布闸拿,位于F島的核電站,受9級特大地震影響书幕,放射性物質(zhì)發(fā)生泄漏新荤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一台汇、第九天 我趴在偏房一處隱蔽的房頂上張望苛骨。 院中可真熱鬧,春花似錦苟呐、人聲如沸痒芝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春病游,著一層夾襖步出監(jiān)牢的瞬間侨拦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工腰奋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留单起,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓劣坊,卻偏偏與公主長得像嘀倒,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

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