首页 > Unix/Linux > Mach-o格式头部结构

Mach-o格式头部结构

  Mach-o格式,是Mach操作系统内核(Mac、iOS系统的内核)主要支持的可执行文件格式。
  用otool工具可以查看Mach-o的头部,并参考Xcode自带的关于Mach-o的头文件仔细分析了一下,关于Mach-o的头文件在/Applications/Xcode.app/Contents/Developer/Platforms/
iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include/mach-o下面。
  Note:下面的宏定义以MH_开头,应该是mach_header的缩写。

  下面是Mach-o文件头部的标注。

Mach-o文件头部的标注

➜  ~ otool -h Hello
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
 0xfeedfacf 16777223          3  0x80           2    18       1616 0x00200085

  从十六进制下面查看下(在VI下面执行 :%!xxd 命令 或者 用任何其他十六进制编辑器)

00000000: cffa edfe 0700 0001 0300 0080 0200 0000  ................
00000010: 1200 0000 5006 0000 8500 2000 0000 0000  ....P..... .....

  疑惑点note: otool打印出来的cputype怎么是16777223呢?想了一下,明白是十进制表示的,0x10000070==16777223。
  在下面的结构体声明

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 */
};
#define	MH_MAGIC	0xfeedface	/* the mach magic number */
#define MH_CIGAM	0xcefaedfe	/* NXSwapInt(MH_MAGIC) */
struct mach_header_64 {
	uint32_t	magic;		/* mach-o 格式的标识符 */
	cpu_type_t	cputype;	/* cpu区分符 */
	cpu_subtype_t	cpusubtype;	/* machine区分符 */
	uint32_t	filetype;	/* 文件类型 */
	uint32_t	ncmds;		/* 加载命令的个数 */
	uint32_t	sizeofcmds;	/* 加载命令的字节数 */
	uint32_t	flags;		/* 程序的标识位 */
	uint32_t	reserved;	/* 保留字段 */
};
#define MH_MAGIC_64 0xfeedfacf /* the 64-bit mach magic number */
#define MH_CIGAM_64 0xcffaedfe /* NXSwapInt(MH_MAGIC_64) */

  64位的头部结构体和32位的相比只多了一个reserved字段,然而这个字段是保留字段,目前还是0x0。等于说现在头的结构都是一样的。

第一个字段magic

  magic字段就是个特征数值,没有什么好说的,取自于下面的宏定义。
  疑惑点note: 为什么是4个宏定义?查找资料并仔细观察发现:CIGAM是MAGIC反过来的拼写,写反的意思是在大端序(big endian mode)(不了解大端序,请点击wiki https://en.wikipedia.org/wiki/Endianness)环境下面使用。

第二&&三个字段CPU type 和 CPU subtype

  定义了Mach-o所能支持的所有CPU类型,这两个位置的标记表明了运行程序的平台是啥,太长了就只把 x86和arm的copy出来记一下。示例中的就是CPU_TYPE_X86_64这个类型。

#define CPU_ARCH_ABI64	0x01000000		/* 64 bit ABI */
#define CPU_TYPE_X86		((cpu_type_t) 7)
#define CPU_TYPE_I386		CPU_TYPE_X86	
#define	CPU_TYPE_X86_64		(CPU_TYPE_X86 | CPU_ARCH_ABI64)
#define CPU_TYPE_ARM		((cpu_type_t) 12)
#define CPU_TYPE_ARM64          (CPU_TYPE_ARM | CPU_ARCH_ABI64)

第四个字段 filetype

  表明Mach-o的文件类型,下面列出了所有的文件类型。每一种我在Github上面有收集的每一种filetype

#define MH_OBJECT 0x1
/* relocatable object file */
/* 一般后缀名为(.o),可重定位文件,编译器的中间产物之一,静态库(后缀名是.a)就是这类文件的集合*/

#define MH_EXECUTE 0x2
/* demand paged executable file */
/* 示例中看到的可执行文件,最基础的类型 */

#define MH_FVMLIB 0x3
/* fixed VM shared library file */
/* 后续补上,不知道在哪里见过 */

#define MH_CORE 0x4
/* core file */
/* 程序Crash之后产生的Core文件,现在默认的core dumps都是关闭的,可以通过 XXX 打开,一个Crash就生成了700多M的一个文件,我截取了他的前1600字节数据,收集起来。 */

#define MH_PRELOAD 0x5
/* preloaded executable file */
/* */

#define MH_DYLIB 0x6
/* dynamically bound shared library */
/* 一般后缀名为(.dylib),动态链接库,很常见*/

#define MH_DYLINKER 0x7
/* dynamic link editor */
/* 动态库的连接器,就这个文件/usr/lib/dyld ,Fat文件就用lipo提取一下,下面会讲到*/

#define MH_BUNDLE 0x8
/* dynamically bound bundle file */
/* Xcode里面可以创建bundle的Target,编译之后在bundle后缀名的文件夹下面可以找到*/

#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 */
/* 这个也很常见,Xcode->Product->Archive打包之后,里面就会生成一个叫DSYM后缀名的文件夹,查看包内容就可以找到它*/

#define MH_KEXT_BUNDLE 0xb
/* x86_64 kexts */
/* 内核功能扩展文件,常见与 /System/Library/Extensions 下面,这个自己可以打开瞅瞅*/

第五和六个字段 ncmds、sizeofcmds

  ncmds 指的是加载命令(load commands)的数量
  sizeofcmds 所有加载命令的大小。如果没有设置的话,没有办法知道加载命令从什么时候结束啦。

第七个字段 flags

  dyld加载时的标志位

#define MH_NOUNDEFS 0x1
/* the object file has no undefined references */
/* 目前没有未定义的符号,不存在链接依赖*/

#define MH_INCRLINK 0x2
/* the object file is the output of an incremental link against a base file and can’t be link edited again */
/* 一个对基本文件的一个增量链接的输出文件,无法被再次链接*/

#define MH_DYLDLINK 0x4
/* the object file is input for the dynamic linker and can’t be staticly link edited again */
/* 动态连接器(dyld)的输入文件,无法被再次静态链接 */

#define MH_BINDATLOAD 0x8
/* the object file’s undefined references are bound by the dynamic linker when loaded. */

#define MH_PREBOUND 0x10
/* the file has its dynamic undefined references prebound. */

#define MH_SPLIT_SEGS 0x20
/* the file has its read-only and read-write segments split */

#define MH_LAZY_INIT 0x40
/* the shared library init routine is to be run lazily via catching memory faults to its writeable segments (obsolete) */

#define MH_TWOLEVEL 0x80
/* the image is using two-level name space bindings */
/* 这个镜像使用的是两级名称空间绑定*/

#define MH_FORCE_FLAT 0x100
/* the executable is forcing all images to use flat name space bindings */

#define MH_NOMULTIDEFS 0x200
/* this umbrella guarantees no multiple defintions of symbols in its sub-images so the two-level namespace hints can always be used. */

#define MH_NOFIXPREBINDING 0x400
/* do not have dyld notify the prebinding agent about this executable */

#define MH_PREBINDABLE 0x800
/* the binary is not prebound but can have its prebinding redone. only used when MH_PREBOUND is not set. */

#define MH_ALLMODSBOUND 0x1000
/* indicates that this binary binds to all two-level namespace modules of its dependent libraries. only used when MH_PREBINDABLE and MH_TWOLEVEL are both set. */

#define MH_SUBSECTIONS_VIA_SYMBOLS 0x2000
/* safe to divide up the sections into sub-sections via symbols for dead code stripping */

#define MH_CANONICAL 0x4000
/* the binary has been canonicalized via the unprebind operation */

#define MH_WEAK_DEFINES 0x8000
/* the final linked image contains external weak symbols */

#define MH_BINDS_TO_WEAK 0x10000
/* the final linked image uses weak symbols */

#define MH_ALLOW_STACK_EXECUTION 0x20000
/* When this bit is set, all stacks in the task will be given stack execution privilege. Only used in MH_EXECUTE filetypes. */
/* 当这个位被设置了之后,所有在任务中的栈被赋予栈内可执行的权限。仅在filetype是MH_EXECUTE的时候使用*/

#define MH_ROOT_SAFE 0x40000
/* When this bit is set, the binary declares it is safe for use in processes with uid zero */
/* 当这个位被设置了之后,程序声明它对root(进程UID是0的用户)是安全的 */

#define MH_SETUID_SAFE 0x80000
/* When this bit is set, the binary declares it is safe for use in processes when issetugid() is true */

#define MH_NO_REEXPORTED_DYLIBS 0x100000
/* When this bit is set on a dylib, the static linker does not need to examine dependent dylibs to see if any are re-exported */

#define MH_PIE 0x200000
/* When this bit is set, the OS will load the main executable at a random address. Only used in MH_EXECUTE filetypes. */
/* 当这个位被设置了之后,系统加载主可执行程序在随机的地址空间,仅在filetype是MH_EXECUTE的时候使用 */

#define MH_DEAD_STRIPPABLE_DYLIB 0x400000
/* Only for use on dylibs. When linking against a dylib that has this bit set, the static linker will automatically not create a LC_LOAD_DYLIB load command to the dylib if no symbols are being referenced from the dylib. */

#define MH_HAS_TLV_DESCRIPTORS 0x800000
/* Contains a section of type S_THREAD_LOCAL_VARIABLES */

#define MH_NO_HEAP_EXECUTION 0x1000000
/* When this bit is set, the OS will run the main executable with a non-executable heap even on platforms (e.g. i386) that don’t require it. Only used in MH_EXECUTE filetypes. */
/* 当这个位被设置了之后,,仅在filetype是MH_EXECUTE的时候使用 */

#define MH_APP_EXTENSION_SAFE 0x02000000
/* The code was linked for use in an application extension. */

关于Fat文件

  有时候使用下面命令会看到显示两个Mach头部,如下面:

➜  ~ otool -h AlipayWallet
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
 0xfeedface      12          9  0x00           2    72       7280 0x00210085
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
 0xfeedfacf 16777228          0  0x00           2    72       8088 0x00210085

  上面介绍的是单一的Arch结构,这一种是Fat的,可以通过 lipo 进行拆解开,使用下面命令

lipo AlipayWallet -thin armv7 -output AlipayWallet_v7

  在中的Fat头部声明

#define FAT_MAGIC	0xcafebabe
#define FAT_CIGAM	0xbebafeca	/* NXSwapLong(FAT_MAGIC) */
struct fat_header {
	uint32_t	magic;		/* FAT_MAGIC or FAT_MAGIC_64 */
	uint32_t	nfat_arch;	/* number of structs that follow */
};
#define FAT_MAGIC_64	0xcafebabf
#define FAT_CIGAM_64	0xbfbafeca	/* NXSwapLong(FAT_MAGIC_64) */

  Fat文件就是一个包装盒,里面填了若干个针对不同架构的指令集,第一个参数magic就是Fat的标识,第二个参数就是里面包含了几个架构的指令集。
  在中对指令集包装的申明

struct fat_arch {
	cpu_type_t	cputype;	
	cpu_subtype_t	cpusubtype;
	uint32_t	offset;		/* 指令集在文件中的偏移量,也就是从哪点开始 */
	uint32_t	size;		/* 指令集的大小 */
	uint32_t	align;		/* 必须是2的n次方 */
};
struct fat_arch_64 {
	cpu_type_t	cputype;	/* cpu specifier (int) */
	cpu_subtype_t	cpusubtype;	/* machine specifier (int) */
	uint64_t	offset;		/* file offset to this object file */
	uint64_t	size;		/* size of this object file */
	uint32_t	align;		/* alignment as a power of 2 */
	uint32_t	reserved;	/* reserved */
};

Note: 看完之后,应该明白了mach-o文件共有
8种magic number {(32位/64位) * (大端序/小端序) * (是否是Fat文件)},
11种filetype
26中flag

  1. 还没有评论
评论提交中, 请稍候...

留言


可以使用的标签: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>
Trackbacks & Pingbacks ( 0 )
  1. 还没有 trackbacks