05-链接器:符号是怎么绑定到地址上的?

一、知识点

1.1编译器和解释器

iOS编写的代码是使用编译器将代码编译成机器码,直接在CPU上运行机器码。像Java是先使用编译器将代码编译成字节码,再通过解释器将字节码解释为不同平台的机器码。

  • 编译器优点是执行效率高,缺点是调试周期长。
  • 解释器优点是方便调试,缺点是执行效率低。

1.2 iOS的编译器

iOS现在的编译器集合叫做LLVM,其内置编译器为lld。编译器将每个文件都编译成Mach-O(可执行文件),链接器将多个Mach-O合并成一个。
编译的过程:

  • LLVM预处理代码,比如将宏嵌入到对应的位置。
  • LLVM对代码进行词法分析和语法分析,生成AST(抽象语法树)。AST结构更简单,遍历更快,而且可以快速生成IR。
  • 最后AST生成IR(一种更接近于机器码的语言),IR可以生成多份适合不同平台的机器码。对于iOS来说,IR生成的机器码是Mach-O。

1.3 链接器的功能

  • 将变量、函数符号和其地址绑定起来
  • 将多个Mach-O文件合成一个

1.4 动态库链接

静态库是编译时链接的库,会链接到Mach-O文件中。动态库是运行时链接的库,使用dyld实现动态加载。
dyld做了哪些事:

  • 先执行 Mach-O 文件,根据 Mach-O 文件里 undefined符号加载对应的动态库,系统会设置一个共享缓存来解决递归依赖问题。
  • 加载后,将 undefined 的符号绑定到动态库里对应的地址上。
  • 最后再处理 +load 方法,main 函数返回后运行 static terminator。

二、课后作业

在 App 运行时通过 dlopen 和 dlsym 链接加载 bundle 里的动态库。

实现思路

1.制作一个简单的包含动态库的bundle文件
2.在运行时通过dlopen函数打开对应的动态库可执行文件,通过dlsym函数找到对应符号的函数进行调用。

2.1 如何制作动态库和bundle文件

这一步就不做过多解释了,网上很多对应的文章。这里我还是以戴铭老师对应专题里的例子文件来做。Boy.m文件代码如下:

#import "Boy.h"

@implementation Boy

void mytest(int a) {
    NSLog(@"%s --- parama = %d",__func__,a);
}

- (void)say {
    NSLog(@"%s",__func__);
}

@end

示例工程结构如下图(其中Test.framework即是Boy文件编译的动态库):


工程结构

2.2 运行时加载动态库

1. 先找到动态库中可执行文件的路径

// 包含动态库的bundle路径
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"dyld" ofType:@"bundle"];
// framework的路径
NSString *frameworkPath = [[NSBundle bundleWithPath:bundlePath] pathForResource:@"Test" ofType:@"framework"];
// 动态库中可执行文件的真实路径
NSString *dyldFilePath = [frameworkPath stringByAppendingString:@"/Test"];

如下图所示,framework中的Test才是动态库的可执行文件


动态库可执行文件

2.打开动态库

// 以指定模式打开指定的动态链接库文件,并返回一个句柄
// 不同的模式的详细介绍见dlopen百度百科
void *handle = dlopen([dyldFilePath UTF8String], RTLD_LAZY);
// handle == null 表示打开动态库失败,dlerror能获取到失败的信息
if (!handle) {
    NSLog(@"dlopen error == %s",dlerror());
}
NSLog(@"dlopen = %s",handle);

3.通过符号找到函数并执行

// 定义函数
void(*pMytest)(int);
// 通过"mytest"符号找到其对应的函数
pMytest = dlsym(handle, "mytest");
// pMytest == null 表示没有找到符号对应的地址,dlerror能获取到失败的信息
if (!pMytest) {
    NSLog(@"dlsym error == %s",dlerror());
}else {
    // 调用函数
    pMytest(5);
}

4.通过runtime调用OC方法

// runtime调用oc方法
[self runtimeCallMethod];
// 可以试试把该方法的调用放到dlopen之前,看看有什么区别
- (void)runtimeCallMethod {
    Class Boy = NSClassFromString(@"Boy");
    id boy = [[Boy alloc] init];
    
    SEL boySaySel = NSSelectorFromString(@"say");
    
    [boy performSelector:boySaySel withObject:nil afterDelay:0];
}

5.控制台打印结果

2019-05-05 22:20:28.939416+0800 05-加载bundle中的动态库[95027:7738314] dlopen = \M-`\M-I=\^O\^A
2019-05-05 22:20:28.939622+0800 05-加载bundle中的动态库[95027:7738314] dlsym = UH\M^I\M-eH\M^C\M-l\^PH\M^M\^E\M-y\^A
2019-05-05 22:20:28.939726+0800 05-加载bundle中的动态库[95027:7738314] mytest --- parama = 5
2019-05-05 22:20:28.956245+0800 05-加载bundle中的动态库[95027:7738314] -[Boy say]

已经能成功调用私有动态库中的C函数和OC方法了。

最后附上完整代码

更多详细内容,请移步至戴铭老师的专栏

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容