作者: 我是好寶寶

鏈接:

https://juejin.im/post/5df47aebe51d4558483d9717

一、MachO初探

1.定義

MachO 其實是 Mach Object 文件格式的縮寫,是mac以及iOS上可執行文件的格式,類似於Windows上的PE格式(Portable Executable)、Linux上的elf格式(Executable and Linking Format)

它是一種用於可執行文件、目標代碼、動態庫的文件格式,作爲.out格式的替代,MachO提供了更強的擴展性

2.常見的MachO文件

  • 目標文件.o

  • 庫文件

    • .a

    • .dylib

    • .Framework

  • 可執行文件

  • dyld(動態鏈接器)

  • .dsym(符號表:Relese環境運行生成

3.查看文件類型

$ file xxx.xx


二、關於架構

1.架構表

其實iPhone不同的型號對應的架構是不一樣的

2.生成多種架構

新建一個工程,真機運行,查看可執行文件僅僅是一個arm64架構的

將項目最低適配系統調爲iOS9.0,真機運行 Relese環境

爲什麼要改爲iOS9.0呢 ?是因爲iPhone5c等armv7、armv7s架構不支持iOS11.0

爲什麼要Relese環境運行呢 ?因爲Xcode默認Debug只生成單一架構

怎麼生成所有架構 ?Xcode10中只包含了v7和64,需要在 Architectures 中添加

三、通用二進制文件

1.定義

通用二進制文件(Universal binary)也被叫做 胖二進制(Fat binary)

  • 蘋果公司提出的一種程序代碼,能同時適用多種架構的二進制文件

  • 同一個程序包中同時爲多種架構提供最理想的性能

  • 因爲需要儲存多種代碼,通用二進制應用程序通常比單一平臺二進制的程序要大

  • 但是由於兩種架構有共通的非執行資源,所以並不會達到單一版本的兩倍之多

  • 而且由於執行中只調用一部分代碼,運行起來也不需要額外的內存

2.拆分/合併架構

架構拆分

合併架構

通用二進制 大小爲342kb,四個架構大小爲80+80+80+81=321kb

What!爲什麼不是單純的1+1=2?

因爲不同架構之間代碼部分是不共用的 (因爲代碼的二進制文件不同的組合在不同的 cpu 上可能會是不同的意義),而公共資源文件是公用的

利用上述方法可以給我們的app瘦身

結論:

胖二進制 拆分後再重組會得到原始 胖二進制

通用二進制 的大小可能大於子架構大小之和,也可能小於,也可能等於,取決於 公共資源文件 的多少

3.終端命令行

// 查看二進制文件

$ lipo -info xx

// 通用二進制文件

// 拆分二進制文件

lipo xxx -thin armv7 -output xxx

// 組合二進制文件

lipo -create x1 x2 x3 x4 -output xxx


四、MachO文件

1.整體結構

MachOView 打開會看到 通用二進制文件Fat Header四個可執行文件 組成

可執行文件 是由 Header Load commands Data 組成

我們可以這麼理解,把 通用二進制文件 看作四本翻譯語言不同的書,每本書有 標題(header) 目錄(load commands) 內容(data)

  • header:

  • load commands:

  • data:

另外我們也可以通過 otool 命令行查看MachO文件結構

$ otool -f universe


2.header

header 包含了該二進制文件的字節順序、架構類型、加載指令的數量等,使得可以快速確認一些信息,比如當前文件用於 32 位 還是 64 位 ,對應的處理器是什麼、文件類型是什麼

Xcode中 shift+command+O -> load.h ->如下信息

struct mach_header_64 {

uint32_t magic; /* 魔數,快速定位64位/32位 */

cpu_type_t cputype; /* cpu 類型 比如 ARM */

cpu_subtype_t cpusubtype; /* cpu 具體類型 比如arm64 , armv7 */

uint32_t filetype; /* 文件類型 例如可執行文件 .. */

uint32_t ncmds; /* load commands 加載命令條數 */

uint32_t sizeofcmds; /* load commands 加載命令大小*/

uint32_t flags; /* 標誌位標識二進制文件支持的功能 , 主要是和系統加載、鏈接有關*/

uint32_t reserved; /* reserved , 保留字段 */

};


mach_header_64(64位)對比mach_header(32位)只多了一個保留字段

3.load commands

load commands 是一張包括區域的位置、符號表、動態符號表等內容的表。它詳細保存着加載指令的內容,告訴鏈接器如何去加載這個 Mach-O 文件。通過查看內存地址我們發現,在內存中 load commands 是緊跟在 header 之後的

4.data

data 是MachO文件中最大的部分,其中 _TEXT段_DATA段 能給到很多信息

load commandsdata 之間還留有不少空間,給我們留下了注入代碼的衝破口

_TEXT段

_DATA段

五、dyld

dyld(the dynamic link editor)是蘋果的動態鏈接器,是蘋果操作系統的一個重要組成部分,在系統內容做好程序準備工作之後,交由dyld負責餘下的工作

系統庫的方法由於是公用的,存放在共享緩存中,那麼我們的MachO在調用系統方法時,dyld會將MachO裏調用存放在共享緩存中的方法進行符號綁定。這個符號在 release環境 是會被自動去掉的,這也是我們經常使用收集 bug 工具時需要恢復符號表的原因

如果感覺這篇文章不錯可以點擊在看:point_down:

相關文章