在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文件加载的全过程(一)的更多相关文章

  1. 从输入URL到页面加载的全过程

    前面的话 本文将详细介绍从输入URL到页面加载的全过程 概述 从输入URL到页面加载的主干流程如下: 1.浏览器构建HTTP Request请求 2.网络传输 3.服务器构建HTTP Response ...

  2. 文件加载---理解一个project的第一步

    当我最开始写php的时候,总是担心这个问题:我在这儿new的一个class能加载到对应的类文件吗?毕竟一运行就报Fatal Error,什么**文件没找到,类无法实例化等等是一种很“低级”的错误,怕别 ...

  3. scrapy cookies:将cookies保存到文件以及从文件加载cookies

    我在使用scrapy模拟登录新浪微博时,想将登录成功后的cookies保存到本地,下次加载它实现直接登录,省去中间一系列的请求和POST等.关于如何从本次请求中获取并在下次请求中附带上cookies的 ...

  4. 前端设计中关于外部js文件加载的速度优化

    在一般情况下,许多人都是将<script>写在了<head>标签中,而许多浏览器都是使用单一的线程来加载js文件的,从上往下,从左往右. 若是加载过程出错,那么网页就会阻塞,就 ...

  5. php基础知识(3)(文件加载include)

    文件加载 综述: 有4个文件加载的语法形式(注意,不是函数): include,  include_once,  require, require_once; 他们的本质是一样的,都是用于加载/引入/ ...

  6. HTML5文件加载进度管理

    /** * 文件加载进度管理 */ DownloadUtils = function(options){ options = options || {}; this.init(options); }; ...

  7. js文件加载优化

    在js引擎部分,我们可以了解到,当渲染引擎解析到script标签时,会将控制权给JS引擎,如果script加载的是外部资源,则需要等待下载完后才能执行. 所以,在这里,我们可以对其进行很多优化工作. ...

  8. Java基础之Throwable,文件加载

    Java中的异常与错误都继承自Throwable,Exception又分为运行时异常(RuntimeException)和编译时异常. 运行时异常是程序的逻辑不够严谨或者特定条件下程序出现了错误,例如 ...

  9. 在IIS上新发布的网站,样式与js资源文件加载不到(资源文件和网页同一个域名下)

    在IIS上新发布的网站,网站能打开,但样式与js资源文件加载不到(资源文件和网页是同一个域名下,例如:网页www.xxx.com/index.aspx,图片www.xxx.com/pic.png). ...

随机推荐

  1. ASP 读取Word文档内容简单示例_组件开发_新兴网络_20161014161610.jpg

  2. mongoDB authentication

    转自:http://blog.csdn.net/allen_jinjie/article/details/9235073 1. 最开始的时候,我们启动mongodb,但是不包含--auth参数: E: ...

  3. 使用maven创建springMVC时返回页面报错

    这是由于你的 Maven 编译级别是 jdk1.5 或以下,而你导入了 jdk1.6 以上的依赖包 解决办法: <build> <finalName></finalNam ...

  4. oracle删除非空的表空间

    oracle删除非空的表空间: drop tablespace tablespaceName including contents;

  5. Android学习总结(3)——Handler深入详解

    什么是Handler Handler是Android消息机制的上层接口,它为我们封装了许多底层的细节,让我们能够很方便的使用底层的消息机制.Handler的最常见应用场景之一便是通过Handler在子 ...

  6. 对Java线程安全与不安全的理解

    当我们查看JDK API的时候,总会发现一些类说明写着,线程安全或者线程不安全,比如说到StringBuilder中,有这么一句,"将StringBuilder 的实例用于多个线程是不安全的 ...

  7. Linux下 利用find命令删除所有.svn目录

    ====================实例============== 删除所有.svn目录 这也是我当初查找 Linux find 命令的目的. 1)  find . -type d -name ...

  8. BA--湿球温度和干球温度的区别

    关于湿球温度和干球温度的区别: 干湿球温度表:用一对并列装置的.形状完全相同的温度表,一支测气温,称干球温度表,另一支包有保持浸透蒸馏水的脱脂纱布,称湿球温度表.当空气未饱和时,湿球因表面蒸发需要消耗 ...

  9. 解决 Mac OS X 10.11 安装 sip 没有权限的问题

    在搭建 PYQT 的过程中我遇上了一个非常恶心的问题,在安装 sip 的时候编译源代码之后的安装过程中一直提示我:Operation not permitted ,我甚至重装了系统也无济于事,终于通过 ...

  10. Visual C++ 经常使用快捷键

    大写和小写 Ctrl+Shift+U: 所有变为大写 Ctrl+U: 所有变为小写 凝视 Ctrl+K+Crtr+C: 凝视选定内容  Ctrl+K+Crtr+U: 取消选定凝视内容 折叠 折叠代码: ...