Mac-O文件加载的全过程(一)
在Mac的开发中, 有没有想过当我们点击可执行文件之后,Mac究竟做了什么事情才让我们的程序运行起来? 对于应用层开发人员或者普通的用户而言, 其实无需知道的这么详细;但是对于内核开发人员而言, 如果能了解这一系列的过程, 那么将增强我们的内核的开发功底。
那么下面我们开始分析我们的鼠标点击之后, Mac都做了什么事情。
1. Mac的历史
这一部分有更好的文章
2. 准备工作
(1). 你需要下载XNU内核源代码以及dyld源代码.
(2). Xcode,vim或者其他什么浏览源代码的工具。
3. 分析
在分析加载过程的时候, 需要涉及到内核和应用层两个部分。因此这篇文章也将分为两部分分别阐述。
3.1 内核部分
Mac的内核经过多次的发展, 目前这部分被称作XNU。具体为什么叫这个名字, 大家可以去搜索一下。在最初开始设计XNU的时候, Apple是打算设计一个微内核, 也就是将最重要的事情放在内核里面并且内核只负责仲裁而非逻辑处理。 这个内核也就是在Mac OS 9上面使用的内核,但是这个产品是一个效率底下系统。因此当乔布斯回归以后对内核进行了大改, 在内核部分引入了FreeBSD的部分, 这个产品就是我们现在在使用的Mac OSX的内核XNU。 简单的历史说完了, 下面我们来点儿干货。
3.1.1 Mac-O文件格式的分析
既然是要分析Mac-O文件的加载过程, 那么必须涉及到Mach-O的格式问题。 对于这个格式, 我们可以参考下面的图:
从这这张图片中, 我们可以看到Mach-O文件在结构上可以分成三个部分:
(1) 文件头
(2) 命令区域
(3) 数据区域(包括数据, 代码等等)
这三个部分共同组成了Mach-O文件格式。下面我们将以此讨论这个三个部分, 在最后我讲给出一些需要注意的部分和部分代码。
3.1.1.1 Mach-O文件头
Mach-O文件头的定义如下:
struct mach_header(_64)
代码来自:${XNU_ROOT}/EXTERNAL_HEADER/mach-o/loader.h:
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;
};
struct mach_header_64 {
uint32_t magic;
cpu_type_t cputype;
cpu_subtype_t cpusubtype;
uint32_t filetype;
uint32_t ncmds;
uint32_t sizeofcmds;
uint32_t flags;
uint32_t reserved;
};
上面的头部定义包含了32bit和64bit的头部。字段的含义如下(下面是以64bit的头部说明的):
命令 | 含义 | 说明 |
---|---|---|
magic | 魔数字 | 主要用来区分当前Mach-O所支持的CPU架构(当前只有32bit和64bit)。 |
cputype | CPU类型 | 主要的CPU类型(32/64bit), 以及其他的属性。 |
cpusubtype | CPU子类型 | cpu具体的类型。 |
filetype | 文件类型 | 文件类型比较多,比如MH_EXECUTE代表可执行文件。 |
ncmds | 命令个数 | 也就是下一个segment中得segment的数量。 |
sizeofcmd | 第三个部分的大小 | None。 |
flags | 当前Mach-O的属性 | 比较常见的属性包括MH_PIE(当前文件执行ASLR)等。 |
更多的值可以参考这里.
3.1.1.2 Mach-O命令区域
这一部分是Mach-O文件中最重要的部分, 我们从上面的Mach-O结构图中可以看到, 所谓的segment其实是数据区域的一个索引。每个segment都在数据区域对应了一段自己的区域。我们需要做的就是找到这些部分并执行他们。首先我们看一下Segment的结构:
Segment的结构定义如下:
struct load_command
代码来自:${XNU_ROOT}/EXTERNAL_HEADER
struct load_command {
uint32_t cmd; /* type of load command */
uint32_t cmdsize; /* total size of command in bytes */
};
这个结构对应的成员比较少, cmd代表当前段的类型。cmdsize当前段的大小。 我们主要看看cmd有哪些的类型, 这个至关重要。在这里我需要说明的是, 下面的命令并不代表全部,之所以要在这里列出的它们的原因是这些命令将在内核中被加载。 那么你可能会问, 那么其他的段命令呢?这个你和dyld去说好了。列表如下:
命令 | 十六进制 | 作用 |
---|---|---|
LC_SEGMENT LC_SEGMENT_64 |
0x01/0x19 | 将这些段加载到对应的进程空间上去(区分32位和64位) |
LC_LOAD_DYLINKER | 0x0E | 加载dyld, 值得注意的是,每个Mach-O文件只能有一个段 |
LC_UUID | 0x1B | 将UUID这个值保存到执行进程的上下文中,同样每个Mach-O文件只能有一个段 |
LC_THREAD | 0x04 | 开启一个Mach线程, 但是不分配栈(这个不常见) |
LC_UNIXTHREAD | 0x05 | 开启一个UNIX线程,其实最主要的用途是告诉加载器当前主函数的位置. 这条命令在10.8之后被LC_MAIN取代。 |
LC_MAIN | 0x80000028 | 在10.8之后代替LC_UNIXTHREAD, 告诉加载器当前主函数的位置 |
LC_CODE_SIGNATURE | 0x1D | 这个是数字签名段 |
LC_ENCRYPTION_INFO | 0x21 | 加密二进制文件, 貌似在IOS下使用的比较频繁。 |
3.1.1.3 Mach-O数据区域
这一部分我们将在dyld的时候着重讲。现在先略过。
3.1.1.4 例子
我们查看一下Mac for QQ的二进制文件格式:
首先, 我们查看一下QQ文件头, 我们发现这个文件是一个32位的Mach-O文件。文件类型是MH_EXEXUTE,也就是可执行文件。 这个执行文件一共有56个segment, 全部的segment的大小有6580.最后一个数据室flag数据, 可以看到这个可执行文件,在加载的时候必须使用ASLR的保护技术,除此之外还有一个标志位MH_NO_HEAP_EXECUTION,这个标志位是防止当前的可执行文件的数据部分有执行权限, 一旦有执行权限, 黑客可以进行所谓的“堆喷射攻击”。
在看第二张图片, 我们发现当前segment中LC_SEGMENTM可以存在多个;还有就像我们上面说的,对于LC_UUID, LC_MAIN, LC_LOAD_DYLINKER等段来说,有且仅有一个。
值得注意的是, 当前这个可执行文件中存在段LC_MAIN, 因此说明当前QQ的编译环境是Mac OSX 10.8+, 因为我们在表格中说过LC_MAIN是在10.8的时候引入的, 因此只有10.8或者更高版本的系统才能编译出这个二进制文件。
http://blog.csdn.net/dongaxis/article/details/41114071
Mac-O文件加载的全过程(一)的更多相关文章
- 从输入URL到页面加载的全过程
前面的话 本文将详细介绍从输入URL到页面加载的全过程 概述 从输入URL到页面加载的主干流程如下: 1.浏览器构建HTTP Request请求 2.网络传输 3.服务器构建HTTP Response ...
- 文件加载---理解一个project的第一步
当我最开始写php的时候,总是担心这个问题:我在这儿new的一个class能加载到对应的类文件吗?毕竟一运行就报Fatal Error,什么**文件没找到,类无法实例化等等是一种很“低级”的错误,怕别 ...
- scrapy cookies:将cookies保存到文件以及从文件加载cookies
我在使用scrapy模拟登录新浪微博时,想将登录成功后的cookies保存到本地,下次加载它实现直接登录,省去中间一系列的请求和POST等.关于如何从本次请求中获取并在下次请求中附带上cookies的 ...
- 前端设计中关于外部js文件加载的速度优化
在一般情况下,许多人都是将<script>写在了<head>标签中,而许多浏览器都是使用单一的线程来加载js文件的,从上往下,从左往右. 若是加载过程出错,那么网页就会阻塞,就 ...
- php基础知识(3)(文件加载include)
文件加载 综述: 有4个文件加载的语法形式(注意,不是函数): include, include_once, require, require_once; 他们的本质是一样的,都是用于加载/引入/ ...
- HTML5文件加载进度管理
/** * 文件加载进度管理 */ DownloadUtils = function(options){ options = options || {}; this.init(options); }; ...
- js文件加载优化
在js引擎部分,我们可以了解到,当渲染引擎解析到script标签时,会将控制权给JS引擎,如果script加载的是外部资源,则需要等待下载完后才能执行. 所以,在这里,我们可以对其进行很多优化工作. ...
- Java基础之Throwable,文件加载
Java中的异常与错误都继承自Throwable,Exception又分为运行时异常(RuntimeException)和编译时异常. 运行时异常是程序的逻辑不够严谨或者特定条件下程序出现了错误,例如 ...
- 在IIS上新发布的网站,样式与js资源文件加载不到(资源文件和网页同一个域名下)
在IIS上新发布的网站,网站能打开,但样式与js资源文件加载不到(资源文件和网页是同一个域名下,例如:网页www.xxx.com/index.aspx,图片www.xxx.com/pic.png). ...
随机推荐
- linux 结构需要清理 (structure needs cleaning)
下面操作会删除挂载点所有文件,注意备份. df -T 查看出错的挂载点对应的文件系统和文件系统类型 然后umount这个文件系统 umount /dev/sda1 然后文件系统类型不同操作不同 ...
- C++基础 (9) 第九天 编译器对模板类的二次编译 类模板 自定义数组类
1 昨日回顾 2 编译器对于模板的二次编译 写一个模板函数 然后进行调用 g++ template.cpp -o template // 汇编 g++ -S template.cpp –o templ ...
- node中exports和module.exports的关系及使用
在node中,需要记住,在使用exports和module.exports的时候,实际输出的是module.exports. exports指向module.exports,是module.expor ...
- redis各数据类型应用概述
前言 redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存.亦可持久化的日志型.key-value数据库,并提供多种语言的API. 它是内存存储的数据结构服务器,可用作数据库.高速缓存 ...
- BA-siemens-insight时间表设置
时间表问题汇总: 如果遇到这种问题,显示"unable to locate databse object",就使用database transfer上传一边所有的模块信息,然后在操 ...
- Grace Hopper 葛丽丝 霍普
Grace Murray Hopper(1906-1992), COBOL之母, Debug之母, A ship in port is safe, but that is not what ships ...
- 【C/C++学院】0724-堆栈简单介绍/静态区/内存完毕篇/多线程
[送给在路上的程序猿] 对于一个开发人员而言,可以胜任系统中随意一个模块的开发是其核心价值的体现. 对于一个架构师而言,掌握各种语言的优势并能够运用到系统中.由此简化系统的开发.是其架构生涯的第一步. ...
- Window7幻灯片字体显示混乱,难道真的是病毒么
这个问题有几天了.就是在其它人的PowerPoint2010做的ppt文件.发到这台有问题的电脑上(PowerPoint2007)就会显示全然不一样.例如以下所看到的. watermark/2/tex ...
- 杭电1018-Big Number(大数)
Big Number Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total ...
- 遗传奥秘的伟大揭秘者:J.Watson
J.Watson的近照: 人们公认,揭秘生命体的遗传奥秘(DNA)是二十世纪最伟大的科技成果之中的一个,或许就是人类最伟大的科技进步(而不是"之中的一个"). 上世纪是人类做出伟大 ...