dyld 加载 Mach-O

原文出自【听云技术博客】:http://blog.tingyun.com/web/article/detail/1346

前言

最近看 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》

时间: 2024-09-17 00:49:53

dyld 加载 Mach-O的相关文章

loaded-在xcode6 上加载动态库

问题描述 在xcode6 上加载动态库 在xcode6 上加载自己创建的动态库时会出现 "dyld: Library not loaded: @rpath/.... Reason: image not found"这个问题除了把 Linked FrameWorks and Libraries的所在动态库选项改为 option 外有没有其他解决办法呢?

Android Jni调用so库,加载库失败分析

现有一个项目,通过JNI接口调用底层库,例如:lib***.so 如下所示,总是加载不成功.调试发现,每次加载so库,会跳到catch异常处理部分,然后打印异常log信息. static{ try{ System.load("/data/data/com.***/lib/lib***.so"); } catch(UnsatisfiedLinkError ulink){ Log.i("HVnative====","Can not load library&q

Daemon Tools如何加载镜像

  首先在下载吧下虚拟光驱Daemon Tools 然后对Daemon Tools进行安装. 安装好后可以看到 右键点击该图标的DAEMON Tools Lite虚拟光驱工具. 如图 再点击装载和驱动管理. 点击出现如下图: 此时你就从这个对话框中,去找到你下载到的游戏的目录,就会发现所谓的映像文件! 游戏的映像文件格式很多种,不需要去记什么格式,只要用这个对话框去找游戏,去到下载到的游戏文件夹里, 你就会看见哪个是映像文件!若游戏只有1个映像文件,这就好办了!直接选中它,打开就是了! (强烈建

安卓viewpager加载问题

问题描述 安卓viewpager加载问题 在viewpager实现底部选项的时候,我首先进入的是首页,然后我直接点几最后一个个人中心的按钮,但是点进去的时候,他首先会想加载第二个和第三个的里面的数据,我想问下,怎么才能在点几最后一个按钮的时候,不加载第二个和第三个里面的东西,只有点击第几个就记载第几个里面的东西,这个怎么实现? 解决方案 首先先让我吐槽一下错别字,点几,还一错就错两个,是点击呀.你这个其实不应该使用viewPager而是直接使用四个fragment就行了,然后使用Fragment

给jsp页面加图片加载不出来

问题描述 给jsp页面加图片加载不出来 jsp页面在main里 图片在images里,我路径 .为啥jsp页面显示不出来呢? 解决方案 src='${request.pageContext.contextPath}/images/1.gif' 解决方案二: 解决方案三: 有木有大神 挺着急的 解决方案四: 1.先看看${request.pageContext.contextPath}对不对,一般这个不至于不正确 2.换个png等图片试试,可能是你的gif图片格式不支持,无法显示 解决方案五: 不

webview加载html,a标签超链接禁止跳转

问题描述 webview加载html,a标签超链接禁止跳转 webview加载html,a标签超链接禁止跳转 webview加载html,a标签超链接禁止跳转 webview加载html,a标签超链接禁止跳转 webview加载html,a标签超链接禁止跳转 webview加载html,a标签超链接禁止跳转 解决方案 shouldOverrideUrlLoading方法 return true 解决方案二: 简单,你在setWebViewClient()方法中把你的连接过滤掉就行了.

转圈圈定制加载中视图

// // HYCircleLoadingView.h // HYCircleLoadingViewExample // // Created by Shadow on 14-3-7. // Copyright (c) 2014年 Shadow. All rights reserved. // #import <UIKit/UIKit.h> /*! * @brief 圆形转圈圈加载等待视图 * @author huangyibiao */ @interface HYBCircleLoading

cortex a9 jtag-IMX6Q JTAG加载UBOOT失败原因

问题描述 IMX6Q JTAG加载UBOOT失败原因 UBoot加载问题点:用JTAG连接板卡,出现报错"Load Debugger fs3",截图如附件. 板卡最小系统参数: - CPU:Freescale IMX6Q Cortex-A9 - DDR3:Micorn 2Gb(32位) - Flash:Micron 256M(8位) 测试发现问题点: - 主晶振24M没有起振(更换16pF~36pF均如此---不振:测试晶振的电平XTALI/O均为457mV,NVCC_PLL_OUT电

Js表格万条数据瞬间加载实现代码

 一条数据创建一行,如果数量大的时候,一次性要加载完数据的话,浏览器就会卡上半天,下面有个不错的方法,大家可以参考下 Js表格,万条数据瞬间加载    在Ajax动态加载数据的实际应用中,大家都习惯了一种思维方式:一条数据创建一行.  于是如果数量大的时候,一次性要加载完数据的话,浏览器就会卡上半天    受Flex的DataGrid控件的启发,在Flex的DataGrid控件中,展示数据的方法并不是有多少条数据就创建多少行,它最多只创建你在界面上所看到的十几二十行(假设为n行),如果数据多的话