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 加载 Mach-O

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

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

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

dyld 加载 Mach-O

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

dyld 加载 Mach-O

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

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

dyld 加载 Mach-O

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

dyld 加载 Mach-O

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

dyld 加载 Mach-O

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

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

dyld 加载 Mach-O

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

dyld 加载 Mach-O

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

dyld 加载 Mach-O

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

1.1 数据结构

mach-o 文件头 操作。

dyld 加载 Mach-O

1.2 ImageLoader

dyld 加载 Mach-O

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

dyld 加载 Mach-O

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

1.3 -main

dyld 加载 Mach-O

dyld 加载 Mach-O

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

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

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

  3. 检查文件是否Restricted

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

1.4 instantiateFromLoadedImage

dyld 加载 Mach-O

这个函数做了三件事情:

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

  2. 初始化 ImageLoader 实例

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

1.5 isCompatibleMachO

dyld 加载 Mach-O

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

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

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

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

1.6 ImageLoaderMachO: : instantiateMainExecutable

dyld 加载 Mach-O

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

dyld 加载 Mach-O

1.7 sniffLoadCommands

这个函数主要做两件事情

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

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

dyld 加载 Mach-O

dyld 加载 Mach-O

dyld 加载 Mach-O

dyld 加载 Mach-O

1.8 ImageLoaderMachOClassic: :instantiateMainExecutable

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

dyld 加载 Mach-O

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

1.9 instantiateStart

dyld 加载 Mach-O

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

2.0 ImageLoaderMachOClassic

dyld 加载 Mach-O

dyld 加载 Mach-O

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

2.1 addImage

dyld 加载 Mach-O

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

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

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

end

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

  1. 合法加测

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

  3. 添加p_w_picpath 到管理容器

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

参考

ObjC runtime 源代码

dyld 源代码

《Mac OSX and iOS Internals》

另外有需要云服务器可以了解下创新互联scvps.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。


新闻标题:dyld加载Mach-O-创新互联
文章出自:http://ybzwz.com/article/dphidp.html