Mach-O 了解一下

Mach-O為Mach object文件格式的縮寫,它是一種用于可執(zhí)行文件、目標代碼、動態(tài)庫的文件格式,由多個源文件組成。作為a.out格式的替代,Mach-O提供了更強的擴展性。
常見文件.a,.dylib,Framework,可執(zhí)行文件,dyld,.dSYM(release)

這里值得注意的是nib雖然用file命令查看是data文件,但是仍被構建成動態(tài)鏈接共享庫的。其本質就是偽nib,是一個.dylib文件。

image.png

C文件的編譯鏈接實踐

1、創(chuàng)建C文件,并編寫相關代碼,然后進行編譯,生成test.o的目標文件。


查看目標文件,得到一個為Mach-Oobject文件。

file test.o

2、鏈接一下,生成a.out可執(zhí)行文件。

鏈接器或鏈接編輯器是計算機實用程序,它負責接收由編譯器生成的一個或多個目標文件,并將它們組合成單個可執(zhí)行文件,庫文件或者另一個"對象"文件。

Clang test.o


生成為test2的可執(zhí)行文件:

clang -o test2 test.o 

通過執(zhí)行可執(zhí)行文件可得到打印結:

./a.out
//%只是一個結束符。
test===test===test===test===test%

探尋系統(tǒng)的dylib

通過命令:find /usr/lib -name "*.dylib"查看系統(tǒng)的動態(tài)庫,下面列出常見的有:

#libobjc:objc和runtime
/usr/lib/libobjc.A.dylib
/usr/lib/libSystem.dylib

libSystem中常見的庫有:

  • libdispatch ( GCD )
  • libsystem_c ( C語言庫 )
  • libsystem_blocks ( Block )
  • libcommonCrypto ( 加密庫,比如常用的 md5 函數 )

通過命令file /usr/lib/libobjc.dylib查看動態(tài)庫信息:


通過cd /usr/lib進入dyld所在文件夾,file dyld查看其具體信息??芍?code>Mach-O文件,但不是可執(zhí)行文件,屬于dynamic linker,Mach-O中的獨立類型。

探尋dSYM文件。

將工程置于release模式下,Build。


顯示dSYM包內容,通過命令可知它也是屬于Mach-O文件

淺談可執(zhí)行文件架構

通用二進制文件(UNIVERSAL BINARY):

通用二進制代碼有兩種基本類型。一種類型就是簡單提供兩種獨立的二進制代碼,一個用來對應x86架構,一個用來對應PowerPC架構。但是對于不熟悉代碼的普通軟件使用者來說,在購買和使用的時候,可能搞不清二者區(qū)別。另外一種類型就是只編寫一個架構的代碼,當另外一種處理環(huán)境時讓系統(tǒng)自動調用模擬器運行。這會導致運行速度下降,一般是作為“通用二進制”或者“特別連編二進制”出現之前暫時使用的折衷辦法。(參見Rosetta
因為需要儲存多種代碼,通用二進制應用程序通常比單一平臺二進制的程序要大,但是由于兩種架構有共通的非執(zhí)行資源,所以并不會達到單一版本的兩倍之多。而且由于執(zhí)行中只調用一部分代碼,運行起來也不需要額外的內存。
目前,蘋果公司的Xcode是唯一一個可以編譯通用二進制代碼的GUI工具

$(ARCHS_STANDARD):代表armv7arm64兩種架構。

Xcode有關編譯架構的設置

lipo命令=>拆分可執(zhí)行文件


lipo命令=>合并可執(zhí)行文件
image.png

使用lipo -info 可以查看MachO文件包含的架構
$lipo -info MachO文件
使用lipo –thin 拆分某種架構
$lipo MachO文件 –thin 架構 –output 輸出文件路徑
使用lipo -create  合并多種架構
$lipo -create MachO1   MachO2  -output 輸出文件路徑

分析Mach-O的可執(zhí)行文件

分三大模塊:

  • Header

header包含該二進制文件的一般信息
字節(jié)順序、架構類型、加載指令的數量等。由系統(tǒng)內核負責讀取。
使得可以快速確認一些信息,比如當前文件用于32位還是64位,對應的處理器是什么、文件類型是什么

  • Load Commands

一張包含了很多內容的表,內容包括區(qū)域的位置、符號表、動態(tài)符號表等。

  • Data

通常是對象文件中最大的部分,包含了Segement的具體數據

通過otool查看可執(zhí)行文件的一些信息,根據提示命令獲取所需要的信息。

    -f print the fat headers
    -a print the archive header
    -h print the mach header
    -l print the load commands
    -L print shared libraries used
    -D print shared library id name
    -t print the text section (disassemble with -v)
    -p <routine name>  start dissassemble from routine name
    -s <segname> <sectname> print contents of se

查看Mach-O的源碼文件

  • 定義的Mach-O文件:
#define    MH_OBJECT    0x1        /* relocatable object file */
#define    MH_EXECUTE    0x2        /* demand paged executable file */
#define    MH_FVMLIB    0x3        /* fixed VM shared library file */
#define    MH_CORE        0x4        /* core file */
#define    MH_PRELOAD    0x5        /* preloaded executable file */
#define    MH_DYLIB    0x6        /* dynamically bound shared library */
#define    MH_DYLINKER    0x7        /* dynamic link editor */
#define    MH_BUNDLE    0x8        /* dynamically bound bundle file */
#define    MH_DYLIB_STUB    0x9        /* shared library stub for static */
/*  linking only, no section contents */
#define    MH_DSYM        0xa        /* companion file with only debug */
/*  sections */
#define    MH_KEXT_BUNDLE    0xb        /* x86_64 kexts */
  • 定義的Header
struct mach_header_64 {
    uint32_t    magic;        //魔數:快速定位屬于64位還是32位
    cpu_type_t    cputype;    //cpu 類型
    cpu_subtype_t    cpusubtype;    //cpu的具體類型
    uint32_t    filetype;    //文件類型,比如可執(zhí)行文件。
    uint32_t    ncmds;       //load commands 的數量
    uint32_t    sizeofcmds;   //load command 的大小
    uint32_t    flags;        //表示二進制文件所支持的一些功能,和系統(tǒng)加載有關系。描述文件在編譯、鏈接等過程中的信息,示例中的 MH_NOUNDEFS 表示文件中不存在未定義的符號,MH_DYLDLINK 表示文件要交由 DYLD 進一步處理,MH_TWOLEVEL 表示文件使用兩級命名空間,MH_PIE 表示啟用地址空間布局隨機化。
    uint32_t    reserved;    //比32位多一個保留字段。
};

通過MachOView查看文件
1、映射:告訴對方二進制文件會映射到手機內存中會占用多大,下一塊是誰。

segment組

_PAGEZERO:這個字段在文件中不存在的,在虛擬內存中是一塊區(qū)域,不具備訪問權限,專門來處理空指針。
VM Address:虛擬內存地址,4個G,根據CPU架構而來。
File Offset:文件偏移地址。

2、LC_DYLD_ INFO_ONLY:動態(tài)鏈接的相關信息。

它的 ONLY 后綴表明這是程序運行所必須的,如果鏈接器不支持,那么加載過程就會終止。

struct dyld_info_command {
    uint32_t   cmd;     /* LC_DYLD_INFO or LC_DYLD_INFO_ONLY */
    uint32_t   cmdsize;     /* sizeof(struct dyld_info_command) */
    uint32_t   rebase_off;  //重定向的偏移值。(ASLR)
    uint32_t   rebase_size; //重定向的大小。
    uint32_t   bind_off;    //綁定。可執(zhí)行文件讀到內存中會綁定一些數據。weak綁定,lazy綁定。
    uint32_t   bind_size;   /* size of binding info  */
    uint32_t   weak_bind_off; /* file offset to weak binding info   */
    uint32_t   weak_bind_size;  /* size of weak binding info  */
    uint32_t   lazy_bind_off; /* file offset to lazy binding info */
    uint32_t   lazy_bind_size;  /* size of lazy binding infs */
    uint32_t   export_off;  //對外開發(fā)的函數。
    uint32_t   export_size; /* size of lazy binding infs */
};

3、LC_SYMTABLC_DYSYMTAB,符號表地址和動態(tài)符號表地址。

記錄了程序的符號表以及字符串表的偏移量及大小,符號表中記錄了程序用到的函數以及全局變量的信息

/*
 * Format of a symbol table entry of a Mach-O file for 32-bit architectures.
 */
struct nlist {
    union {
#ifndef __LP64__
        char *n_name;   /* for use when in-core */
#endif
        uint32_t n_strx;    /* index into the string table */
    } n_un;
    uint8_t n_type;     /* type flag, see below */
    uint8_t n_sect;     /* section number or NO_SECT */
    int16_t n_desc;     /* see <mach-o/stab.h> */
    uint32_t n_value;   /* value of this symbol (or stab offset) */
};
/*
 * This is the symbol table entry structure for 64-bit architectures.
 */
struct nlist_64 {
    union {
        uint32_t  n_strx; /* index into the string table */
    } n_un;
    uint8_t n_type;        /* type flag, see below */
    uint8_t n_sect;        /* section number or NO_SECT */
    uint16_t n_desc;       /* see <mach-o/stab.h> */
    uint64_t n_value;      /* value of this symbol (or stab offset) */
};

數據結構中相關字段的含義都可以在 nlist.h 中找到,這里值得一說的是 n_un 字段,它用來記錄符號的名字,但為什么是 uint32_t 類型呢?又為什么在注釋中標明是 string table 的 索引呢?
這是因為在程序中,字符串的長度是不固定的,所以會將其放在 string table 中,然后存儲它在 string table 中的偏移。如果其他部分想要引用某個字符串,那么他首先需要找到 string table 的起始地址,然后根據偏移量找到相應字符串的起始位置并向后讀取字符,直到遇見 \0 才會停止讀取過程,最后返回讀到的字符串。
這也是 LC_SYMTAB 額外記錄 string table 地址的原因,string table 通常用于記錄 section 名、符號名等信息。

4、LC_LOAD_DYLINKER:動態(tài)連接器:dyld,在iPhone手機下的usr/lib/dyld來加載的。調用這個方法之前都是系統(tǒng)內核進行調用的。
5、LC_UUID:靜態(tài)鏈接器為其生成的文件所提供的唯一標識符
6、LC_MAIN:指定 main 函數的地址
7、LC_LOAD_DYLIB:加載一些依賴庫和三方庫的地址。

補充:

  • DATA段有懶加載表和非懶加載表。
  • symbol:用于綁定的時候會用到這里的信息。
  • Mach-O文件被內核加載,被DYLD讀取。

參考鏈接:

Mach-O文件格式參考
Mach-O文件格式分析

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容