了解Mach-O文件

Mach-O文件

想要一個應用程序運行起來,那么它的可執行文件格式一定要被操作系統所理解。在Windows系統的可執行文件是PE,而在OS XiOS 中的可執行文件是Mach-O

那么Mach-O是怎么生成的呢?蘋果公司目前使用的編譯器是LLVM,在程序編譯時,編譯器會對每個文件進行編譯,然后生成Mach-O文件,而后鏈接器會將項目中的多個 Mach-O 文件合并成一個,最終的這個就是我們的可執行Mach-O文件.

那么Mach-O 文件里面有哪些內容呢?其實主要還是數據和代碼,其中數據是一些初始值的定義,代碼就是一些是函數的定義。下面我們一起了解下Mach-O文件。

Mach-O文件簡介

Mach-O 是Mach Object文件格式的縮寫,是運用于mac以及iOS上;它是一種用于可執行文件、目標代碼、動態庫的文件格式;

Mach-O文件類型

  • Executable:應用可執行的二進制文件
  • Dylib Library:動態鏈接庫
  • Static Library:靜態鏈接庫
  • Bundle:不能被鏈接 Dylib,只能在運行使用dlopen()加載
  • Relocatable Object File:可重定向文件

Mach-O文件結構

Mach-O文件主要由三部分組成:HeaderLoad commandsData

Header部分描述當前Mach-O文件的基本信息 (架構,是否Fat二進制文件,CUP類型等等);

Load commands部分主要描述:1.Mach-O文件中在虛擬內存中空間是如何分配,從哪個內存地址開始到哪個內存地址結束。 2.不同段在Mach-O文件中的位置,大小分布。

Data部分是描述內存如何被分配的內容。包括__TEXT, __DATA等

Header

類型:區分32位、64位

結構

// 32位
struct mach_header {
    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 */
};
// 64位
 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 */
};

注釋
magic:確定Mach-O文件運行框架,如64位/32位
cputype:CPU類型,如arm
cpusubtype:對應CPU類型的具體型號
filetype:文件類型
ncmds:加載命令條數
sizeofcmds:所有加載命令的大小
flags:保留字段
reserved:標志位

LoadCommand

  • cmd:指令類型
  • cmdsize: 指令長度

以下是load_command的結構:

 struct load_command {
    uint32_t cmd;       /* type of load command */
    uint32_t cmdsize;   /* total size of command in bytes */
};

command 指令類型說明

  • LC_SEGMENT/LC_SEGMENNT_64 :將對應段中的數據加載并映射到進程的內存空間

  • LC_SEGMENT_TEXT :代碼段,其中_stub_helper用于關聯函數bind/rebind

  • LC_SEGMENT_DATA :可讀/可寫的數據段,函數指針,其中_la_symbol_ptr動態函數個數,及相對動態符號表的偏移量

  • LC_SEGMENT_LINKEDIT : :動態鏈接加載指令,支持動態鏈接dyld,該段長度覆蓋符號表等數據(計算鏈接時程序的基址),符號表,動態符號表,字符串表段中定義的offset偏移量都是基于_LINKEDIT的vm_add

  • LC_SYMTAB :符號表信息,解析函數名

  • LC_DYSYMTAB :動態符號表信息,地址值為動態函數相對符號表的索引,_la_symbol_ptr對應的cmd可以換算出第一個動態函數對應動態符號表的初始地址,其次存儲是連續,結構長度固定的,可以通過遍歷獲取所有動態函數的對應的符號表索引

Data

Data中就是由Segment組成的,每一個Segment定義了一些Mach-O文件的數據、地址和內存保護屬性,這些數據在動態鏈接器加載程序時被映射到了虛擬內存中。每個段都有不同的功能。

Segment一般包含下列功能:

  1. __PAGEZERO: 空指針陷阱段,映射到虛擬內存空間的第一頁,用于捕捉對NULL指針的引用;
  2. __TEXT: 包含了執行代碼以及其他只讀數據。 為了讓內核將它 直接從可執行文件映射到共享內存, 靜態連接器設置該段的虛擬內存權限為不允許寫。當這個段被映射到內存后,可以被所有進程共享。(這主要用在frameworks, bundles和共享庫等程序中,也可以為同一個可執行文件的多個進程拷貝使用)
  3. __DATA: 包含了程序數據,該段可寫;
  4. __LINKEDIT: 含有為動態鏈接庫使用的原始數據,比如符號,字符串,重定位表條目等等。

一般Segment又會按不同的功能劃分為幾個區(Section),即段所有字母大小,加兩個下橫線作為前綴,而區則為小寫,同樣加兩個下橫線作為前綴,

Segment結構體:

struct segment_command { /* for 32-bit architectures */
    uint32_t    cmd;        /* LC_SEGMENT */
    uint32_t    cmdsize;    /* includes sizeof section structs */
    char        segname[16];    /* segment name */
    uint32_t    vmaddr;     /* memory address of this segment */
    uint32_t    vmsize;     /* memory size of this segment */
    uint32_t    fileoff;    /* file offset of this segment */
    uint32_t    filesize;   /* amount to map from the file */
    vm_prot_t   maxprot;    /* maximum VM protection */
    vm_prot_t   initprot;   /* initial VM protection */
    uint32_t    nsects;     /* number of sections in segment */
    uint32_t    flags;      /* flags */
};


struct segment_command_64 { /* for 64-bit architectures */
    uint32_t    cmd;        /* LC_SEGMENT_64 */
    uint32_t    cmdsize;    /* includes sizeof section_64 structs */
    char        segname[16];    /* segment name */
    uint64_t    vmaddr;     /* memory address of this segment */
    uint64_t    vmsize;     /* memory size of this segment */
    uint64_t    fileoff;    /* file offset of this segment */
    uint64_t    filesize;   /* amount to map from the file */
    vm_prot_t   maxprot;    /* maximum VM protection */
    vm_prot_t   initprot;   /* initial VM protection */
    uint32_t    nsects;     /* number of sections in segment */
    uint32_t    flags;      /* flags */
};

Section結構體:

struct section { /* for 32-bit architectures */
    char        sectname[16];   /* name of this section */
    char        segname[16];    /* segment this section goes in */
    uint32_t    addr;       /* memory address of this section */
    uint32_t    size;       /* size in bytes of this section */
    uint32_t    offset;     /* file offset of this section */
    uint32_t    align;      /* section alignment (power of 2) */
    uint32_t    reloff;     /* file offset of relocation entries */
    uint32_t    nreloc;     /* number of relocation entries */
    uint32_t    flags;      /* flags (section type and attributes)*/
    uint32_t    reserved1;  /* reserved (for offset or index) */
    uint32_t    reserved2;  /* reserved (for count or sizeof) */
};

struct section_64 { /* for 64-bit architectures */
    char        sectname[16];   /* name of this section */
    char        segname[16];    /* segment this section goes in */
    uint64_t    addr;       /* memory address of this section */
    uint64_t    size;       /* size in bytes of this section */
    uint32_t    offset;     /* file offset of this section */
    uint32_t    align;      /* section alignment (power of 2) */
    uint32_t    reloff;     /* file offset of relocation entries */
    uint32_t    nreloc;     /* number of relocation entries */
    uint32_t    flags;      /* flags (section type and attributes)*/
    uint32_t    reserved1;  /* reserved (for offset or index) */
    uint32_t    reserved2;  /* reserved (for count or sizeof) */
    uint32_t    reserved3;  /* reserved */
};
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容