公司法
当前位置: 首页 法律大全

计算机的十大冷知识(计算机大佬让你彻底了解)

时间:2023-06-14 作者: 小编 阅读量: 2 栏目名: 法律大全

首先编译器驱动程序会对main.c与sum.c文件的源代码进行翻译,翻译过程如下:image其中,main.o称为可重定位目标文件。链接器将这些块连接起来,确定被连接块的运行时位置,并且修改代码和数据块中的各种位置。然而,和编译器中的符号表不同,symtab符号表不包含局部变量的条目。注意,可执行目标文件中并不需要重定位信息,因此通常省略,除非用户显式地指示链接器包含这些信息。这些符号对应于非静态的C函数和全局

前言

当我们点击Xcode的运行按钮时,你会注意到在界面顶端的提示栏上会出现“Building”的字样,紧接着会出现“Linking”的字样,我们知道Building是编译过程,那这个Linking(链接)是什么过程呢?本文将对链接过程做一个讲解,了解链接的过程,可以帮助你理解计算机系统的底层原理,并解答你平时关于计算机怎样识别并执行程序的一些疑惑。另外,本文也是后续篇章的基础,我们会由链接的知识延展出Mach-O文件、fishhook原理以及hook objc_msgSend的知识讲解。

链接的基本概念

链接(linking)是将各种代码和数据片段收集并组合成为一个单一文件的过程,这个文件可被加载(复制)到内存并执行。

链接可以执行与编译时(complie time),也就是源代码被翻译成机器代码时;也可以执行于加载时(load time),也就是在程序被加载器(load-er)加载到内存并执行时;甚至可以执行在运行时(run time),也就是由应用程序来执行。在早期的计算机系统中,链接是手动执行的。在现代系统中,链接是由叫做连接器(linker)的程序自动执行的。

链接的作用

链接器使分离编译成为可能,我们不用将一个大型的应用程序组织为一个巨大的源文件,而是可以把它分解为更小、更好管理的模块,可以独立地修改和编译这些模块。当我们改变这些模块中的一个时,只需简单地重新编译它,并重新链接应用,而不必重新编译其它文件。

下面的讨论基于这样的环境:一个运行Linux的x86-64系统,使用标准的ELF-64目标文件格式。

编译器驱动程序

下面的C语言示例程序,由两个源文件组成,main.c和sum.c。main函数初始化一个整数数组,然后调用sum函数来对数组元素求和。

// sum.cint sum(int *a, int n) {int s = 0;for (int i = 0; i < n; i) {s= a[i];}return s;}// main.cint array[2] = {1, 2};int main() {int val = sum(array, 2);return val;}

大多数的编译系统会提供编译器驱动程序(compile driver),包含语言预处理器、编译器、汇编器和链接器。首先编译器驱动程序会对main.c与sum.c文件的源代码进行翻译,翻译过程如下:

image

其中,main.o称为可重定位目标文件。

之后,编译系统会运行链接器ld,将main.o和sum.o以及一些必要的系统目标文件组合起来,创建一个可以执行目标文件,这个过程是静态链接,过程如下:

image

再之后,操作系统会调用加载器(loader),将可执行文件prog中的代码和数据复制到内存中,然后执行。

静态链接

静态链接器(static linker)以一组可重定位目标文件作为输入,生成一个完全链接的、可以加载和运行的可执行目标文件。输入的可重定位目标文件由各种不同的代码和数据节(section)组成,每一节都是一个连续的字节序列。指令在一节中,初始化了的全局变量在另一个节中,而未初始化的变量又在另外一节中。

为了构造可执行文件,链接器必须完成两个重要的任务:

  • 符号解析(symbol resolution)。目标文件定义和引用符号,一个个符号对应一个函数或一个全局变量或一个静态变量(即C语言中以static属性声明的变量)。符号解析的目的是将每个符号引用正好和一个符号定义关联起来。
  • 重定位(relocation)。编译器和汇编器生成从地址0开始的代码和数据节。链接器通过把每个符号定义与一个内存位置关联起来,从而重定位这些节,然后修改所有对这些符号的引用,使它们指向这个内存位置。

目标文件纯粹是字节块的集合,这些块中,有些包含程序代码,有些包含数据,而有些则是引导链接器和加载器的数据结构。链接器将这些块连接起来,确定被连接块的运行时位置,并且修改代码和数据块中的各种位置。

目标文件

目标文件有三种形式:

  • 可重定位目标文件。包含二进制代码和数据,其形式可以在编译时与其他可重定位目标文件合并起来,创建一个可执行目标文件。
  • 可执行目标文件。包含二进制代码和数据,其形式可以被直接复制到内存并执行。
  • 共享目标文件。一种特殊类型的可重定位目标文件,可以在加载或者运行时被动态的加载进内存并链接。动态库就是这种形式的。

目标文件的生成方式:

  • 编译器和汇编器生成可重定位目标文件(包括共享目标文件)。
  • 链接器生成可执行目标文件。

目标文件的格式:

  • 在iOS和MacOS-X中,目标文件的格式是Mach-O格式。
  • X86-64 Linux和Unix系统使用可执行可连接格式ELF。
可重定位目标文件

image.png

下上展示了一个典型的ELF可重定位目标文件的格式。ELF头包含很多信息,包括生成该文件的系统的字节大小,字节顺序,ELF头的大小,目标文件的类型,机器类型等等。节头部表描述了不同节的位置和大小。

加载ELF头和节头部表的是节:

  • .text:已编译程序的机器代码。
  • .rodata:只读数据,比如 printf语句中的格式串和开关语句的跳转表。
  • .data:已初始化的全局和静态C变量。局部C变量在运行时被保存在栈中,既不出现在,data节中,也不出现在.bss节中
  • .bss:未初始化的全局和静态C变量,以及所有被初始化为0的全局或静态变量。在目标文件中这个节不占据实际的空间,它仅仅是一个占位符。目标文件格式区分已初始化和未初始化变量是为了空间效率:在目标文件中,未初始化变量不需要占据任何实际的磁盘空间。运行时,在内存中分配这些变量,初始值为0。
  • .symtab:一个符号表,它存放在程序中定义和引用的函数和全局变量的信息。一些程序员错误地认为必须通过-g选项来编译一个程序,才能得到符号表信息。实际上,每个可重定位目标文件在. symtab中都有一张符号表(除非程序员特意用 STRIP命令去掉它)。然而,和编译器中的符号表不同, symtab符号表不包含局部变量的条目。
  • .rel.text:一个.text节中位置的列表,当链接器把这个目标文件和其他文件组合时,需要修改这些位置。一般而言,任何调用外部函数或者引用全局变量的指令都需要修改。另一方面,调用本地函数的指令则不需要修改。注意,可执行目标文件中并不需要重定位信息,因此通常省略,除非用户显式地指示链接器包含这些信息。
  • .rel.data:被模块引用或定义的所有全局变量的重定位信息。一般而言,任何已初始化的全局变量,如果它的初始值是一个全局变量地址或者外部定义函数的地址,都需要被修改。
  • .debug:一个调试符号表,其条目是程序中定义的局部变量和类型定义,程序中定义和引用的全局变量,以及原始的C源文件。只有以-g选项调用编译器驱动程序时,才会得到这张表。
  • .line:原始C源程序中的行号和.text节中机器指令之间的映射。只有以-g选项调用编译器驱动程序时,才会得到这张表。
  • .strtab:一个字符串表,其内容包括. symtab和, debug节中的符号表,以及节头部中的节名字。字符串表就是以nu11结尾的字符串的序列。
符号和符号表

每个可重定位目标模块(目标文件)m都有一个符号表,它包含m定义和引用的符号的信息。在链接器的上下文中,有三种不同的符号:

  • 由模块m定义并能被其它模块引用的全局符号。这些符号对应于非静态的C函数和全局变量。
  • 由其它模块定义并被模块m引用的全局符号。这些符号称为外部符号,对应于在其它模块中定义的非静态C函数和全局变量。
  • 只被模块m定义和引用的局部符号。它们对应于带static属性的C函数和全局变量。这些符号在模块m中任何位置都可见,但是不能被其它模块引用。

.symtab中的符号表不包含非静态程序变量的任何符号,这些程序变量符号在栈中被管理,链接器对此类符号不感兴趣。

如何解析多重定义的全局符号

链接器的输入是一组可重定位目标模块。每个模块定义一组符号,有些是局部的(只对定义该符号的模块可见),有些是全局的(对其他模块也可见)。如果多个模块定义同名的全局符号,会发生什么呢?下面是 Linux编译系统采用的方法。

在编译时,编译器向汇编器输出每个全局符号,或者是强( strong)或者是弱(weak),而汇编器把这个信息隐含地编码在可重定位目标文件的符号表里。函数和已初始化的全局变量是强符号,未初始化的全局变量是弱符号。

根据强弱符号的定义,Linux链接器使用下面的规则来处理多重定义的符号名

  • 规则1:不允许有多个同名的强符号。
  • 规则2:如果有一个强符号和多个弱符号同名,那么选择强符号。
  • 规则3:如果有多个弱符号同名,那么从这些弱符号中任意选择一个。
静态库

迄今为止,我们都是假设链接器读取一组可重定位目标文件,并把它们链接起来,输出一个可执行目标文件。实际上,所有的编译系统都提供一种机制,将所有相关的目标模块打包成一个单独的文件,称为静态库。静态库可以用做链接器的输入,当链接器构造一个输出的可执行目标文件时,它只复制静态库里被应用程序引用的目标模块,这就减少了可执行文件在磁盘和内存中的大小。在Linux系统中,静态库由后缀.a标识。

重定位

一旦链接器完成了符号解析这一步,就把代码中的每个符号引用和正好一个符号定义(即它的一个输入目标模块中的一个符号表条目)关联起来。此时,链接器就知道它的输入目标模块中的代码节和数据节的确切大小。现在就可以开始重定位步骤了,在这个步骤中,将合并输入模块,并为每个符号分配运行时地址。重定位由两步组成:

  • 重定位节和符号定义。在这一步中,链接器将所有相同类型的节合并为同一类型的新的聚合节。例如,来自所有输入模块的.data节被全部合并成一个节,这个节成为输出的可执行目标文件的.data节。然后,链接器将运行时内存地址赋给新的聚合节,赋给输人模块定义的每个节,以及赋给输人模块定义的每个符号。当这一步完成时,程序中的每条指令和全局变量都有唯一的运行时内存地址了。
  • 重定位节中的符号引用。在这一步中,链接器修改代码节和数据节中对每个符号的引用,使得它们指向正确的运行时地址。要执行这一步,链接器依赖于可重定位目标模块中称为重定位条目(relocation entry)的数据结构。

当汇编器生成一个目标模块时,它并不知道数据和代码最终将放在内存中的什么位置,它也并不知道这个模块引用的任何外部定义的函数或者全局变量的位置。所以,无论何时汇编器遇到对最终位置的目标引用,它就会生成一个重定位条目,告诉链接器在将目标文件合并成可执行目标文件时如何修改这个引用。

可执行目标文件 与 加载可执行目标文件

见《深入理解计算机系统》

动态链接共享库

静态库由一些缺点:静态库需要定期维护和更新;每个程序都会使用一些通用的标准函数,在运行时,这些函数的代码会被复制到每个运行进程的文本段中,在一个运行上百个进行的典型系统上,这是对内存资源的浪费。

共享库(shared library)是致力于解决静态库缺陷的一个现代创新产物。共享库是一个目标模块,在运行或加载时,可以加载到任意内存地址,并和一个在内存中的程序链接起来。这个过程称为动态链接,是由一个叫做动态链接器(dynamic linker)的程序来执行的。在Linux系统中,共享库通常由.so后缀标识。

共享库以两种不同的方式来共享的。首先,在任何给定的文件系统中,对于一个库只有一个.so文件。所有引用该哭的可执行目标文件共享这个.so文件中的代码和数据,而不是像静态库的内容那样被复制和嵌入到引用它们的可执行文件中。其次,在内存中,一个共享库的.text节的一个副本可以被不同的正在运行的进程共享。

image.png

    推荐阅读
  • 怎样烧红烧肉(家常红烧肉的做法)

    怎样烧红烧肉原料:精品五花肉、炖肉料包、葱、冰糖、茶叶。五花肉切条放入凉水中撇去血沫。焯水定型;捞出后晾凉切一样大的方块。锅中放少许油倒入白砂糖炒糖色。糖色的气泡由大变小迅速关火,倒入开水。加少许绍酒,加开水烧,熟得快,加入茶叶水,可以去腥味。改回炒锅大火,放冰糖,使汁粘稠即可出锅,香葱段点缀。

  • 2022杭州径山茶圣节时间、地点、活动一览

    最终集齐所有铜币的游客可至“大宋钱庄”兑换神秘礼物。今来茶韵生活01、陆羽说论坛为进一步挖掘径山茶宴有关历史文化,本届茶圣节特邀请茶学专家交流讨论如何更好保护和传承国家非物质文化遗产。为打造文化传播年,第二十一届中国茶圣节以春迎、夏凉、秋韵、冬福四大主题贯穿全年。

  • 《重生之门》给罗队发短信的人身份

    但是通过前文,不难推测应该是庄文杰发给罗队的短信,只是没有暴露自己的身份。罗坚来到青檀假日酒店排查,没有发现任何异常,庄文杰和许正清乔装改扮随后赶来,他们一出现就被人盯上,庄文杰和许正清来到地下停车场,庄文杰巧妙引开那些人,混进游客中进入酒店。这件事情把十二年前的洛神案串联起来了。

  • 爱情名著哪个好看(随侃名著佳作第6期)

    言下之意,他主动向周晓白提出分手。钟跃民成为一个军人,上了战场,并且是在战斗中受伤,被送到战地医疗帐篷内救治。而周晓白和钟跃民在时隔十多年后的相遇一刻,也是被编剧以及导演,安排得相当的特别,并不是那种悲情欲绝又或者是感动无比的相遇时刻。

  • 板栗可以保存多久 板栗怎么能保存时间长

    如果是晒干的板栗可以存放3-4个月,生板栗在常温下合理贮存可以存放1-2个月,煮熟的栗子大概可以放一周,熟板栗放冰箱冷冻能保存30天左右,熟板栗放冰箱冷藏保存可以存放5天。

  • 贾宝玉与红楼梦的关系(贾宝玉的春梦到底在暗示什么)

    贾宝玉与红楼梦的关系?要知道,贾琏这个人极其好色,而且好的就是熟女,那么从这个曲折的描述中,我们可以推断出,秦可卿应该是那种熟女中的极品。这个问题在书中得不到直接的答案,因为在后面的文章中,秦可卿一共只出现三个镜头:介绍弟弟秦钟与贾宝玉相见,秦可卿病后王熙凤带贾宝玉去探病,秦可卿临死前在梦里向王熙凤交代后事。

  • 简单又好看的剪纸适合儿童(孩子能学会的幼儿简单剪纸教程)

    接下来我们就一起去研究一下吧!简单又好看的剪纸适合儿童幼儿园的孩子经常要做各种各样的手工,通过做手工,提高孩子的审美能力,锻炼孩子的动手能力,培养孩子的专注力和耐心,让孩子更聪明。用蓝天白云绿色的草地,太阳、小兔子和小蘑菇,可以贴出一幅画,也可以用这个画面编出一个小故事,带孩子度过愉快的亲子时光。欢迎关注,学习更多幼儿小手工。

  • 摩尔庄园钓鲤鱼的最佳方法(摩尔庄园钓鲤鱼的有什么最佳方法)

    以下内容希望对你有帮助!摩尔庄园钓鲤鱼的最佳方法工具/原料:华为手机、安卓系统、摩尔庄园游戏。进入游戏后操纵游戏角色进行移动了。去商店购买钓鱼的诱饵。来到池塘边进行的钓鱼。等待的水面出现波动即可钓到鲤鱼了。

  • 国外的懒人产品(歪国产品咖在用哪些可爱的小工具)

    quotes=trueUsabilityHub我通常使用UsabilityHub来帮助确定设计方案。

  • 一年四季水果时间表(一年四季的时令水果是什么)

    3月(春季):枇杷、红香蕉、樱桃、杨桃、番荔枝、青枣、甘果蔗、草莓、番石榴、牛奶蕉、柑桔、观赏南瓜、果桑、鹤首瓜。12月(冬季):樱桃、番茄、红香蕉、鸡蛋果、木瓜、草莓、百香果、杨桃、无花果、番石榴、牛奶蕉、鹤首瓜、观赏南瓜、果蔗、台湾青枣、黑提子、人心果、柠檬、菠萝、油梨、柑橘、橙子。