❗️iOS高级强化

iOS进阶02: Mach-O

2021-02-03  本文已影响0人  黑白森林无间道

一、什么是Mach-O文件?

Mach-OMach Object文件格式的缩写,是 mac以及 iOS上可执行文件的格式,对应系统通过应用二进制接口(application binary interface,缩写为ABI)来运行该格式的的文件。Mach-O文件对应有多种格式:

Mach-O格式用来替代BSD系统的a.out格式。Mach-O文件格式保存了在编译过程链接过程中产生的机器代码和数据,从而为静态链接和动态链接的代码提供了单一文件格式。

clang单文件编译

Xcode中我们可以直接创建.c文件,通过终端clang命令来对.c文件进行编译或生成可执行文件,下面看一下clang怎样使用的。

#include <stdio.h>
int main(){
    printf("打印:hello\n");
    return 0;
}
clang -c main.c

会生成main.o文件,该文件即为mach-o文件,通过命令file main.o查看文件信息如下

main.o: Mach-O 64-bit object x86_64

是一个object类型的文件称为目标文件,并不是可执行文件

./a.out 或 ./main
image

多个文件是如何编译的呢?

开发中根据不同功能模块我们会分很多文件来实现,在clang中是可以对多个文件进行一次性打包,生成一个可执行文件。如下:

#include <stdio.h>
void sleep(){
    printf("正在睡觉\n");
}
void sleep();//声明方法
int main(){
    printf("打印:hello\n");
    sleep();//调用方法
    return 0;
}
clang -o main main.c people.c

二、通用二进制文件(Universal binary)

iOS中不同手机对应着可能不同的架构,如arm64、armv7、armv7s。为了兼容不同架构的手机,苹果推出了通用二进制文件,包含了应用程序常用的这些架构,因此通用二进制文件,比单一架构二进制文件要大很多。

架构选择

image

通过以上配置真实编译出来的是包含arm64、armv7架构

通用二进制文件在哪呢?

xxx.app中的xxx黑色文件即是通用二进制文件,右键xxx.app显示包内容即可获得。

lipo命令

通过lipo命令可以查看、拆分及合并以上提出的架构,在做静态库时也会使用,来合并真机下和模拟器下的静态库,以适应不同的调试环境。

LoginApp.app中我获取可执行文件LoginApp

// 终端输出结果如下
Architectures in the fat file: LoginApp are: armv7 arm64
lipo LoginApp -thin armv7 -output LoginApp_armv7
lipo LoginApp -thin arm64 -output LoginApp_arm64
// 终端输出结果如下
Non-fat file: LoginApp_armv7 is architecture: armv7
Non-fat file: LoginApp_arm64 is architecture: arm64
lipo -create LoginApp_armv7 LoginApp_arm64 -output LoginApp_ALL

三、Mach-O文件结构

官方图解:


image

文件分为三个部分:

直接使用 MachOView打开LoginApp可执行文件,如下:

image
image
结构体对齐

1、查看Macho headers

// 查看 Header,包括 Load commands
objdump --macho --private-headers LoginApp
otool -l ${MACHO_PATH}
// 只查看 Header
objdump --macho --private-header LoginApp
otool -h ${MACHO_PATH}
image
image

Mach-O中定义了程序的入口main函数(定义在LC_MAIN的Load command命令),告诉动态链接器去加载程序的入口

// grep代表搜索, -A 5:向下输出5行
objdump --macho --private-headers LoginApp | grep "MAIN" -A 5
image
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 */
};
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 */
};

2、Load commands

Header之后是load commands段为加载命令段,在header结构体中有对加载命令段相关信息的描述,用于解析加载命令。在objc4源码loader.h中,有对loadcommand的定义:

struct load_command {
    uint32_t cmd;       /* type of load command */
    uint32_t cmdsize;   /* total size of command in bytes */
};
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 */
};

以上加载命令含义如下:

3、Data

Data区域由Segment段和Section节组成:

总结

上一篇下一篇

猜你喜欢

热点阅读