Mach-O文件格式说明及从中提取机器指令

https://blog.csdn.net/kakukemeit/article/details/38734647

Mach-O 可执行文件

http://objccn.io/issue-6-3/

Mach-O文件格式说明及从中提取机器指令

http://hi.baidu.com/sangwf/item/5824a73b51b6f5f62784f40d

在写一个简单的操作系统,boot部分都是用汇编实现,但继续实现内核,再用汇编就太累了,是时候转向C了。在《Linux内核完全剖析》及《自己动手写操作系统》中,C语言的编译都是在Linux下完成的,输出的目标文件及执行程序是ELF格式的,而在Mac OS下,输出格式是Mach-O的。如何从mach-o文件中提取机器指令,网上的文章很稀少。而我又不想直接再装个linux搞这个东西,加上对Mac OS感兴趣,借机研究一下Mach-O执行程序的文件格式。

首先,你要了解执行程序只是一种特殊的格式,和word文档、excel文档,并没有太大的区别。只是执行程序能够被加载器所加载到操作系统中,创建专门的进程,并执行程序中的指令。而对于word文档之类的,是先打开了word执行程序,然后读入了文档内容。

对于Mach-O文件的格式,可以直接在apple官方文章下载,在google搜索“Mach-O File Format”即可,备注里有链接。由于说明文档是英文的,理解起来比较痛苦,再加上里面的图画的少了一个关键的信息,导致理解上可能存在问题。另外,关于load_command结构体的说明,看了之后可能也会产生歧义。因此,我决定写一篇,试图快速将清楚Mach-O的基本格式。

一个Mach-O文件的内容可以通过下图来表示:

首先是一个mach_header结构体,描述了整个文件的基本信息。(如果是64位执行程序,则是mach_header_64,两者差别不大)。mach_header结构体的定义是:

struct mach_header

{

        uint32_t magic;

       cpu_type_t cputype;

       cpu_subtype_t cpusubtype;

       uint32_t filetype;

       uint32_t ncmds;

       uint32_t sizeofcmds;

       uint32_t flags;

};

其中magic用来标识执行程序的类型,比如区分是32位或64位。另一个重要参数是ncmds,说明后续有多少个command。

紧接着mach_header,就是连续的这些load command。这些load command基本描述了具体的一些段信息,以及各种在系统中的加载特性。command种类很多,想详细了解,就看参考手册吧。每一个command都具有不同的结构体格式,在前两个字段都是相同的。为了方便编程,定义了一个结构体:

struct load_command

{

       uint32_t cmd;

       uint32_t cmdsize;

};

cmd表示command编号,cmdsize表示这个command的实际长度。注意,这个结构体并不是独立存在的,它只是为了表示方便,真正的command的后面还有一部分内容。比如segment_command,其结构体是:

struct segment_command

{

       uint32_t cmd;

       uint32_t cmdsize;

       char segname[16];

       uint32_t vmaddr;

       uint32_t vmsize;

       uint32_t fileoff;

       uint32_t filesize;

       vm_prot_t maxprot;

       vm_prot_t initprot;

       uint32_t nsects;

       uint32_t flags;

};

看到了吧?前面两个字段就是一个load_command。

对于segment_command,紧接着就存放了segment_command所包含的section的说明。这也是官方文档的结构图上所没有画清楚的,竟然将这个信息省略了,看起来会云里雾里的。其中的nsects说明了后面跟了几个section。section结构体的定义是:

struct section

{

       char sectname[16];

       char segname[16];

       uint32_t addr;

       uint32_t size;

       uint32_t offset;

       uint32_t align;

       uint32_t reloff;

       uint32_t nreloc;

       uint32_t flags;

       uint32_t reserved1;

       uint32_t reserved2;

};

最重要的定义是size和offset。size表示这个section的内容的长度,而offset表示在执行程序中的文件偏移。通过这两个信息,我们对于机器指令的section,就能读出机器指令了。

实际上,Mac OS下有一个工具叫otool,就能读取出Mach-O文件各种信息,类似于linux的objdump,可以查看ELF的各种信息。比如otool -t -v filename,就能打印出执行程序的机器指令。我也写了一个程序,用于提取机器指令,见:https://github.com/sangwf/walleos/blob/master/tools/macho_parse.c

通过这个程序,我成功将机器指令提取,并在walleos下成功调用。

会对这篇文章感兴趣的人可能比较少,随着mac os的普及,我相信越来越多。

注1:

Mach-O的格式文档见:https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/MachORuntime/Mach-O_File_Format.pdf

猜你喜欢

转载自blog.csdn.net/chenyijun/article/details/85097883