Mach-O入门理解

背景

  工作以来代码中涉及Mach-o文件的操作和理解内容较多,一直也没有做一个比较全面的学习,今天先简单整理一下相关的内容,step by step,冲鸭!

What

Mach-O简述

 一句话,Mach-O是mac以及ios上的一种文件格式,用于存储程序、库的标准格式,是Mach object的缩写。那既然是文件格式,有哪些文件是Mach-O格式呢?常见的Mach-O文件类型有如下几种:

  • 目标文件.o
  • 库文件 .a(静态库文件,其实就是多个.o文件合并在一起)、.dylib(动态库文件)、.framework
  • 可执行文件
  • 动态链接/编辑器:dyld、.dsym(符号表,存储二进制文件符号信息的文件)

在Xcode中也可以查看Mach-O文件类型:
在这里插入图片描述

命令查看文件类型:file 文件
Mach-O可以是多架构的二进制文件,即通用二进制文件,也称为胖二进制文件

Mach-O文件结构

官网图
Mach-O这种文件格式是哪种格式呢?
Mach-O文件的基本结构如上图所示,文件主要包括三部分:Header、Load Command、Data

  • Header:保存文件的基本信息,如文件类型、CPU架构信息、加载命令个数等
  • Load Command:根据这里的数据确定内存分布(本身不包含数据,类似于一个指针)
  • Data:存放具体的代码和数据,是Mach-O文件真正的内容数据。
Header

文件头信息在<mach-o/loader.h>中定义。
(32位和64位结构不同)64位header结构体如下:

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指定是32位还是64位
  • cputype和cpusubtype是表示cpu的架构是x86还是x64等,即平台和版本
  • filetype:文件类型:标识是执行文件还是动态库等
  • ncmds: 表示接下来的加载命令的个数
  • sizeofcmds: 加载命令的总长度
  • flags: ldid动态加载需要的标记位
  • 最后的保留位不解释

Mach-O支持多种文件类型,filetype字段用于标识,这些文件类型定义在loader.h文件中:

#define MH_OBJECT   0x1     /* Target 文件:编译器对源码编译后得到的中间结果 */
#define MH_EXECUTE  0x2     /* 可执行二进制文件 */
#define MH_FVMLIB   0x3     /* VM 共享库文件(还不清楚是什么东西) */
#define MH_CORE     0x4     /* Core 文件,一般在 App Crash 产生 */
#define MH_PRELOAD  0x5     /* preloaded executable file */
#define MH_DYLIB    0x6     /* 动态库 */
#define MH_DYLINKER 0x7     /* 动态连接器 /usr/lib/dyld */
#define MH_BUNDLE   0x8     /* 非独立的二进制文件,往往通过 gcc-bundle 生成 */
#define MH_DYLIB_STUB   0x9     /* 静态链接文件(还不清楚是什么东西) */
#define MH_DSYM     0xa     /* 符号文件以及调试信息,在解析堆栈符号中常用 */
#define MH_KEXT_BUNDLE  0xb     /* x86_64 内核扩展 */

在这里插入图片描述

Load Command

  Load command描述了文件中数据的具体组织结构,不同的数据类型使用不同的加载命令。它的大小和数目在header中已经被提供,如上图所示,在<mach-o/loader.h>中定义了load command的结构:

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

  Load Command中存储着不同类型的command,如LC_SEGMENT_64、LC_UUID、LC_MAIN…,command主要有两种类型:指向具体数据、不指向具体数据。不同类型的command对应着不同的结构体,load_command类似于基类,其他类型的command结构体“继承”自这个command:(来自网友总结)
在这里插入图片描述

我们分析、使用最多的是LC_SEGMENT_64命令,该命令表示将相应的segment映射到虚拟地址空间中,一个程序一般会分为多个段,每一个段有唯一的段名,不同类型的数据放入不同的段中,常见的段有__PAGZERO、__TEXT、 __DATA、__LINKEDIT、__DATA_CONST,该command的数据结构定义在<mach-o.h/loader.h>中:

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];    /* 表示是段的名称。常见的有:__PAGEZERO、__LINKEDIT、__TEXT、__DATA */
    uint64_t    vmaddr;     /* 当前segment加载的虚拟内存起始地址 */
    uint64_t    vmsize;     /* 段所占的虚拟内存的大小  */
    uint64_t    fileoff;    /* segment在文件中的偏移 */
    uint64_t    filesize;   /* segment在文件中的长度 */
    vm_prot_t   maxprot;    /* 最大的保护级别 */
    vm_prot_t   initprot;   /* 初始化的保护级别 */
    uint32_t    nsects;     /* 包含sections的个数  */
    uint32_t    flags;      /* 标志位 */
};

其中,__TEXT段是代码段,里面主要存放代码,该段是可读可执行,但是不可写;__DATA段是数据段,里面主要是存放数据,该段是可读可写,但是不可执行。Segment段可以进一步分解为section,nsects表示该段包含的section的个数。segment负责内存对齐以及保持section相对位置不变,section负责数据、代码的存储。section在64位架构中结构体为:

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 */
};
Data

  该部分包含segment的具体数据,由Segment段和Section节组成,load_command和data部分的关系如下所示,类似于指针和数据:
在这里插入图片描述

How

Mach-O文件中存储者程序的代码和数据,在程序运行时系统会为其创建一个进程,以及分配虚拟内存空间,同时把程序文件中的内容加载到虚拟内存空间中。进程的映像(image)其实就是程序磁盘文件(Mach-O文件)在内存中的一个副本。Image是Mach-O文件在内存中的一个副本。对进程映像(image)操作的API都在<mach-o/dyld.h>中声明。

ASLR

简单理解:当app被加载到内存时,会有一个整体的地址偏移,使得Mach-O文件的虚拟内存有一个整体偏移。

Mach-O类型文件工具

系统自带工具
  • file : 查看mach-o的文件类型
  • nm: 查看mach-o文件的符号表
  • otool: 查看mach-o特定部分和段的内容
  • lipo: 常用于多架构mach-o文件的处理
Xcode调试命令
  • image list
  • image lookup …
第三方工具
  • MachOView:GUI工具查看Mach-O文件
  • Hopper : 反汇编查看Mach-O文件
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值