更多技术干货请戳:听云博客

前言

最近看 ObjC的runtime 是怎么实现 +load 钩子函数的实现。进而引申分析了 dyld 处理 Mach-O 的这部分机制。

1.简单分析 Mach-O 在dyld 中是如何被加载到内存中的;

2.分析了 +load 的 特殊加载时机;

+ load

上图的调用栈告诉我们哪些函数被调用了。

dyld 是Apple 的动态链接器;在 xnu 内核为程序启动做好准备后,就会将 PC 控制权交给 dyld 负责剩下的工作 (dyld 是运行在 用户态的, 这里由 内核态 切到了用户态)。

每当有新的镜像加载之后,都会执行 load-images 方法进行回调,这里的回调是在整个ObjC runtime 初始化时 -objc-init 注册的 :

有新的镜像被 map 到 runtime 时,调用 load-images 方法,并传入最新镜像的信息列表 infoList:

这里的镜像就是 一些 System framework 的二进制。

进入 下图函数 load-images-nolock 查找 load 函数

调用 prepare-load-methods 对 load 方法的调用进行准备(将需要调用 load 方法的类添加到一个列表中)

调用 -getObjc2NonlazyClassList 获取所有的类的列表之后,会通过 remapClass 获取类对应的指针,然后调用 schedule-class-load 递归地 将当前类和没有调用 + load 父类进入列表。

在执行 add-class-to-loadable-list(cls) 将当前类加入加载列表之前,会先把父类加入待加载的列表,保证父类在子类前调用 load 方法。

在将镜像加载到运行时、对 load 方法的准备就绪,执行 call-load-methods,开始调用 load 方法:

其中 call-class-loads 会从一个待加载的类列表 loadable-classes 中寻找对应的类,然后找到 @selector(load) 的实现并执行。

分析到这里,已经能得知 load 函数是如何被调用的。

接下来分析 dyld 这部分怎么加载镜像的

1.1 数据结构

mach-o 文件头 操作。

1.2 ImageLoader

每一个加载的 Mach-O 文件都会存在这样一个ImageLoader 的 实例,上图可以看出 这里ImageLoader是一个抽象类,每一种具体的Mach-O 文件都会继承 ImageLoader类, 继承关系 如下图:

在加载时会根据Mach-O的格式不同选择生成不用的实例。

1.3 -main

在调用-main 函数之后,做了一下几件事情:

  1. 选择运行环境(iOS 模拟器)

  2. 初始化数据、设置全局变量、上下文信息

  3. 检查文件是否Restricted

走完这些流程,就会调用 instantiateFromLoadedImage 函数,开始加载Mach-O 并且实例化 为 ImageLoader。

1.4 instantiateFromLoadedImage

这个函数做了三件事情:

  1. 检查Mach-O 文件是否合法

  2. 初始化 ImageLoader 实例

  3. 调用addImage 函数添加 初始化后的实例到管理模块中

1.5 isCompatibleMachO

Mach-O 文件的合法性检查:

  1. mach-header 中的 cputype与当前运行的CPU 版本是否支持

  2. mach-header 中的 subtype 在该CPU 架构下的所有版本都可以支持

cputype 就是CPU 平台, x86,ARM ,POWERPC 等, 而subtype 就是同一个平台下的不同版本, 例如:arm7,arm7.

1.6 ImageLoaderMachO: : instantiateMainExecutable

该函数主要通过 sniffLoadCommands 函数来判断 Mach-O 文件是否是压缩过的,然后分别 选择不同的 子类实例化。

1.7 sniffLoadCommands

这个函数主要做两件事情

  1. 判断Mach-O文件是classic的还是compressed的。

  2. 获取mach-O文件的segment的数量。

1.8 ImageLoaderMachOClassic: :instantiateMainExecutable

classic 与 compressed 的初始化大同小异,先分析Classic 的实现

可以看到加载的核心代码 还在 instantiateStart 函数中

1.9 instantiateStart

这里仍然没有出现加载的核心代码,只是根据之前获得的数据申请分配了内存,并计算 segments的 指针。 ImageLoaderMachOClassic 的构造函数才是加载 的核心逻辑。

2.0 ImageLoaderMachOClassic

根据Mach-O 文件 segments 将数据加载到 内存中, 任何返回 调用 addImage 函数。

2.1 addImage

这个函数只是做了数据更新

  1. 将image 添加到管理容器中

  2. 更新了内存分布的信息

end

整个加载过程基本分为三个步骤:

  1. 合法加测

  2. 解析Mach-O文件头信息,将segments 的具体信息 构建到image 的实例中

  3. 添加image 到管理容器

根据 dyld的源代码的粗略分析, 更多信息需要分析 xnu 内核代码。

参考

ObjC runtime 源代码

dyld 源代码

《Mac OSX and iOS Internals》

原文链接:http://blog.tingyun.com/web/article/detail/1346

dyld 加载 Mach-O的更多相关文章

  1. Mac-O文件加载的全过程(一)

    在Mac的开发中, 有没有想过当我们点击可执行文件之后,Mac究竟做了什么事情才让我们的程序运行起来? 对于应用层开发人员或者普通的用户而言, 其实无需知道的这么详细:但是对于内核开发人员而言, 如果 ...

  2. mkimage工具 加载地址和入口地址 内核启动分析

    第三章第二节 mkimage工具制作Linux内核的压缩镜像文件,需要使用到mkimage工具.mkimage这个工具位于u-boot-2013. 04中的tools目录下,它可以用来制作不压缩或者压 ...

  3. 动态库连接器–动态库链接信息(Mach-O文件格式和程序从加载到执行过程)

    section cmd 说明 举例 __text 主程序代码   __stubs 用于动态库链接的桩   __stub_helper 用于动态库链接的桩   __cstring 常亮字符串符号表描述信 ...

  4. Devices Tree加载流程

    DT.IMG布局 hdr zImage Ramdisk.img DT.img 其中DT.img由DTBTOOL打包所有编译生成的dtb生成:布局如下: DT header dt_entry_0 dt_ ...

  5. 百度推出新技术 MIP,网页加载更快,广告呢?

    我们在2016年年初推出了MIP,帮助移动页面加速(原理).内测数据表明,MIP页面在1s内加载完成.现在已经有十多家网站加入MIP项目,有更多的网站正在加入中.在我们收到的反馈中,大部分都提到了广告 ...

  6. 探真无阻塞加载javascript脚本技术,我们会发现很多意想不到的秘密

    下面的图片是我使用firefox和chrome浏览百度首页时候记录的http请求 下面是firefox: 下面是chrome: 在浏览百度首页前我都将浏览器的缓存全部清理掉,让这个场景最接近第一次访问 ...

  7. 高性能Javascript--脚本的无阻塞加载策略

    Javascript在浏览器中的性能,可以说是前端开发者所要面对的最重要的可用性问题. 在Yahoo的Yslow23条规则当中,其中一条是将JS放在底部 .原因是,事实上,大多数浏览器使用单进程处理U ...

  8. 懒加载session 无法打开 no session or session was closed 解决办法(完美解决)

           首先说明一下,hibernate的延迟加载特性(lazy).所谓的延迟加载就是当真正需要查询数据时才执行数据加载操作.因为hibernate当中支持实体对象,外键会与实体对象关联起来.如 ...

  9. Bootstrap-Select 动态加载数据的小记

    关于前端框架系列的可以参考我我刚学Bootstrap时候写的LoT.UI http://www.cnblogs.com/dunitian/p/4822808.html#lotui bootstrap- ...

随机推荐

  1. js动态加载css和js

    之前写了一个工具类点此链接里面含有这段代码,感觉用处挺多,特意提出来 var loadUtil = { /* * 方法说明:[动态加载js文件css文件] * 使用方法:loadUtil.loadjs ...

  2. EasyPR--开发详解(8)文字定位

    今天我们来介绍车牌定位中的一种新方法--文字定位方法(MSER),包括其主要设计思想与实现.接着我们会介绍一下EasyPR v1.5-beta版本中带来的几项改动. 一. 文字定位法 在EasyPR前 ...

  3. 2、利用蓝牙定位及姿态识别实现一个智能篮球场套件(二)——CC2540/CC2541基于广播的RSSI获得

    CC2541一拖多例程中RSSI获得是通过一个事件回调函数实现的,前提是需要连接上蓝牙设备. 这个对于多点定位来说是不可行的,由于主机搜索蓝牙设备过程中也能获得当前蓝牙设备的RSSI等信息,因此可基于 ...

  4. ASP.NET Web API 控制器创建过程(二)

    ASP.NET Web API 控制器创建过程(二) 前言 本来这篇随笔应该是在上周就该写出来发布的,由于身体跟不上节奏感冒发烧有心无力,这种天气感冒发烧生不如死,也真正的体会到了什么叫病来如山倒,病 ...

  5. React-Native 组件开发方法

    前言 React Native的开发思路是通过组合各种组件来组织整个App,在大部分情况下通过组合View.Image等几个基础的组件,可以非常方便的实现各种复杂的跨平台组件,不过在需要原生功能支持. ...

  6. 再次学习 java 类的编译

    做JAVA开发的都知道myeclipse, 我们在myeclipse中新建一个类,然后保存, 如何正常的话,那么在项目指定的目录(也就是项目的output目录)就会生成同名的class文件, 可是,我 ...

  7. 我为NET狂官方面试题

    基础牢不牢测一测便了解,工作没工作测一测便清楚,工作有几年测一测便知道 最近帮人过一遍C#基础,出了点题目,有需要的同志拿走 答案不唯一,官方答案只供参考,若有错误欢迎提出~ 更新ing 1.面向过程 ...

  8. Dapper:The member of type SeoTKD cannot be used as a parameter Value

    异常汇总:http://www.cnblogs.com/dunitian/p/4523006.html#dapper 上次说了一下Dapper的扩展Dapper.Contrib http://www. ...

  9. Bootstrap WPF Style,Bootstrap风格的WPF样式

    简介 GitHub地址:https://github.com/ptddqr/bootstrap-wpf-style 此样式基于bootstrap-3.3.0,样式文件里的源码行数都是指的这个版本.CS ...

  10. ASP.NET Core真实管道详解[1]:中间件是个什么东西?

    ASP.NET Core管道虽然在结构组成上显得非常简单,但是在具体实现上却涉及到太多的对象,所以我们在 <ASP.NET Core管道深度剖析[共4篇]> 中围绕着一个经过极度简化的模拟 ...