链接器: 如何将符号绑在地址上

栏目: 服务器 · 编程工具 · 发布时间: 5年前

内容简介:在程序编译的时候生成对应的机器码, 然后再机器上直接运行优点:能够不发布代码,就能获取更新,灵活度较高 缺点:运行速度慢,执行效率低,因为只有在调用的时候才会去解释相对应的代码优点: 在编译时生成相对应的机器码,在CPU上直接运行。执行效率高,运行速度快 缺点: 想要更新,必须重新编译。

在程序编译的时候生成对应的机器码, 然后再机器上直接运行

解释器优点和缺点

优点:能够不发布代码,就能获取更新,灵活度较高 缺点:运行速度慢,执行效率低,因为只有在调用的时候才会去解释相对应的代码

编译器优点和缺点:

优点: 在编译时生成相对应的机器码,在CPU上直接运行。执行效率高,运行速度快 缺点: 想要更新,必须重新编译。

LLVM 和 GCC

苹果使用的编译器是LLVM 相比于Xcode5之前使用的GCC ,编译速度提高了3倍,同时,苹果也反过来主导LLVM的发展,让LLVM可以针对苹果公司的硬件进行更多的优化

总结来说,LLVM是编译器 工具 链技术的一个集合。而其中的lld项目,就是内置链接器。编译器会对每个文件进行编译,生成Mach-O(可执行文件);链接器会将项目中多个Mach-O进行文件合并。

这里,先简单的总结下编译的几个主要过程:

  1. LLVM 会预处理代码,例如把宏嵌入对应的位置。
  2. 预处理后,LLVM会对代码进行词法分析和语法分析,生成AST。AST是抽象语法树,结构上比代码更加简洁,遍历器更快,所以使用AST能够快速的进行静态检查,同事更快的生成IR。(IR在iOS中 为Mach-O)
  3. 最后AST会生成IR,IR是一种更接近机器码的语言,区别在于和平台无关,通过IR可生成多分不同平台的机器码
链接器: 如何将符号绑在地址上

Mach-O 文件里面的内容,主要就是代码和数据:代码是函数的定义;数据是全局变量的定义,包括全局变量的初始值。不管是代码还是数据,它们的实例都需要由符号将其关联起来。

为什么呢?因为 Mach-O 文件里的那些代码,比如 if、for、while 生成的机器指令序列,要操作的数据会存储在某个地方,变量符号就需要绑定到数据的存储地址。你写的代码还会引用其他的代码,引用的函数符号也需要绑定到该函数的地址上。

而链接器的作用,就是完成变量、函数符号和其地址绑定这样的任务。而这里我们所说的符号,就可以理解为变量名和函数名。

为什么要让链接器做符号和地址的绑定?

  1. 维护性差
  2. 可读性
  3. 代码和内存地址绑定太早 如果不让编译器做符号和地址的绑定,就会要程序猿自己去书写每一个指令的内存地址,这样针对不同平台的,就要写不同的内存指令。而且修改后的代码后 对地址的维护会让你崩溃,每次修改后都要重新确定内存地址,而且还要考虑代码内存地址绑定的时间

链接器为什么还要把项目中的多个 Mach-O 文件合并成一个?

我们总不能把在一个文件中写整个项目。既然要分开写,就要编译成每个文件对应的Mach-O文件,而我们每个文件中会有调用其他文件的函数,如果Mach-O不合并的话,我们无法调用其他Mach-O中的方法。所以必然要将每一个Mach-O文件合并成一个文件

链接器在链接多个目标文件的过程中,会创建一个符号表,用于记录所有已定义的和所有未定义的符号。链接时如果出现相同符号的情况,就会出现“ld: dumplicate symbols”的错误信息;如果在其他目标文件里没有找到符号,就会提示“Undefined symbols”的错误信息。

链接器对代码主要做了哪几件事儿。

  1. 去项目文件里查找目标代码文件里没有定义的变量。
  2. 扫描项目中的不同文件,将所有符号定义和引用地址收集起来,并放到全局符号表中。
  3. 计算合并后长度及位置,生成同类型的段进行合并,建立绑定。
  4. 对项目中不同文件里的变量进行地址重定位。

你在项目里为某项需求写了一些功能函数,但随着业务的发展,一些功能被下掉了或者被其他负责的同事在另一个文件里用其他函数更新了功能。那么这时,你以前写的那些函数就没有用武之地了。日长月久,无用的函数越来越多,生成的 Mach-O 文件也就越来越大。 这时,链接器在整理函数的符号调用关系时,就可以帮你理清有哪些函数是没被调用的,并自动去除掉。那这是怎么实现的呢?

链接器在整理函数的调用关系时,会以 main 函数为源头,跟随每个引用,并将其标记为 live。跟随完成后,那些未被标记 live 的函数,就是无用函数。然后,链接器可以通过打开 Dead code stripping 开关,来开启自动去除无用代码的功能。并且,这个开关是默认开启的。

动态库链接

在真实的 iOS 开发中,你会发现很多功能都是现成可用的,不光你能够用,其他 App 也在用,比如 GUI 框架、I/O、网络等。链接这些共享库到你的 Mach-O 文件,也是通过链接器来完成的。 链接的共用库分为静态库和动态库:静态库是编译时链接的库,需要链接进你的 Mach-O 文件里,如果需要更新就要重新编译一次,无法动态加载和更新;而动态库是运行时链接的库,使用 dyld 就可以实现动态加载。 Mach-O 文件是编译后的产物,而动态库在运行时才会被链接,并没参与 Mach-O 文件的编译和链接,所以 Mach-O 文件中并没有包含动态库里的符号定义。也就是说,这些符号会显示为“未定义”,但它们的名字和对应的库的路径会被记录下来。运行时通过 dlopen 和 dlsym 导入动态库时,先根据记录的库路径找到对应的库,再通过记录的名字符号找到绑定的地址。 dlopen 会把共享库载入运行进程的地址空间,载入的共享库也会有未定义的符号,这样会触发更多的共享库被载入。dlopen 也可以选择是立刻解析所有引用还是滞后去做。dlopen 打开动态库后返回的是引用的指针,dlsym 的作用就是通过 dlopen 返回的动态库指针和函数符号,得到函数的地址然后使用。 使用 dyld 加载动态库,有两种方式:有程序启动加载时绑定和符号第一次被用到时绑定。为了减少启动时间,大部分动态库使用的都是符号第一次被用到时再绑定的方式。 加载过程开始会修正地址偏移,iOS 会用 ASLR 来做地址偏移避免攻击,确定 Non-Lazy Pointer 地址进行符号地址绑定,加载所有类,最后执行 load 方法和 Clang Attribute 的 constructor 修饰函数。 每个函数、全局变量和类都是通过符号的形式定义和使用的,当把目标文件链接成一个 Mach-O 文件时,链接器在目标文件和动态库之间对符号做解析处理。

ps:本篇文章基于 戴铭老师的课程,也算是学习后的一些笔记和见解。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

多处理器编程的艺术

多处理器编程的艺术

(美)Maurice Herlihy、(美)Nir Shavit / 机械工业出版社 / 2013-2 / 79.00元

工业界称为多核的多处理器机器正迅速地渗入计算的各个领域。多处理器编程要求理解新型计算原理、算法及编程工具,至今很少有人能够精通这门编程艺术。 现今,大多数工程技术人员都是通过艰辛的反复实践、求助有经验的朋友来学习多处理器编程技巧。这本最新的权威著作致力于改变这种状况,作者全面阐述了多处理器编程的指导原则,介绍了编制高效的多处理器程序所必备的算法技术。了解本书所涵盖的多处理器编程关键问题将使在......一起来看看 《多处理器编程的艺术》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

SHA 加密
SHA 加密

SHA 加密工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具