链接器符号解析算法小解以及静态库链接顺序等等问题
2013-09-09 09:09
246 查看
在编写linux驱动程序时,时常会发现链接出错,当时往往不知道错误在哪。现在了解到链接器的工作原理之后,明白当时为什么出错了。对于以后有效率地编写驱动程序有很大帮助。
一个C语言程序,经过诸如GCC之类的编译器编译成可执行文件一般会经历4个处理过程,这个大部分的linux入门书籍都有讲到过,如果没有扔掉它,:)!
分别经过C预处理器(CPP)、C编译器(CL)、C汇编器(AS)、C链接器(LD)。这个怎么处理生成什么不是本文的主要内容。
ASCII源文件.C----》ASCII中间文件.i(由CPP处理后生成)------》汇编语言文件.s(由CL处理后生成)-----》可重定位目标文件.o(由AS处理后生成)-----》可执行目标文件(由LD处理后生成)。
说到可执行目标文件和可重定位目标文件不得不讲讲ELF(可执行可链接格式),但ELF在大部分书籍都有讲到,且网上资料相当多。
ELF定义了可重定位和可执行目标文件构成的大致框架。可重定位目标文件中,有很多的节,每个节都是固定大小的条目。这些条目存储了代码、符号、引用、变量和调试等等相关信息。
一个点C文件会引用其他文件的函数或者是全局变量,那么经过上面那一套走下来,会生成点O文件也就是可重定位目标文件。在可重定位目标文件中也会存储引用的函数或全局变量信息,而这些引用的函数和全局变量在链接生成可执行程序时都要进行重定位。
典型地,.rel.text:代码段需要重定位的信息。主要是一些引用的函数;.rel.data:数据段需要重定位的信息。主要是本模块引用的全局变量等。
那么,当我们将所有的可重定位目标文件和静态库生成完成之后,输入命令:
GCC ex1.c ex2.o ex3.o ex4.a会发生什么呢?
可以很简单的说,就是LD链接器会将它们连接生成可执行文件。具体有两个步骤:1是符号解析,2是重定位。
那么符号到底是如何解析的呢?
还是上面的例子:
GCC ex1.c ex2.o ex3.o ex4.a
这其中ex4.a包含ex5.o和ex6.o
不失一般性,我们假设有:
ex1.c引用了ex2.o中的符号2.A,ex3.o中的3.A,ex4.a里面的ex5.o中5.A,ex7.o中的7.A;
ex2.o则是ex3.o中的3.B
ex3.o则是ex2.o中的2.B,
就是如下图所示的链接器命令引用实例:
当然此处假设ex2.o定义了2.A和2.B符号,ex3.o和ex5.o 、ex6.o与此类似。
在符号解析阶段链接器从左往右按照命令行的顺序来扫描可重定位目标文件和静态库。它维持了三个集合E、U和D。
E是可重定位目标文件的集合
U是未解析符号的集合
D为已定义的符号的集合
链接器按照如下步骤来解析符号:
步骤1,未解析的符号有2.A3.A 4.A三个;
步骤2,由于EX2.O的加入,更新E集合,由于EX2.O本身定义了2.A 2.B两个符号,所以U集合中的2.A 2.B引用可以被解析。解析后的符号被放入到D集合中
步骤3同步骤2;
步骤4中,静态库包含了EX5.O和EX6.O两个可重定位目标文件,那么LD会逐个拿U集合中未解析的符号和静态库的可重定位目标文件中定义的符号匹配,如果有匹配的,这将其加入到E中,如果没有则进行下个可重定位目标文件的匹配过程。本例中,由于Ex5.o中定义的符号5.A正好和U集合中的5.A匹配,那么Ex5.o自身就被放入到E集合中了。像Ex6.o定义个符号和U匹配不上,就没有被放入到E集合中。从这点来看,LD不会将那么没有引用的可重定位目标文件链接到可执行文件中。
最后LD发现,扫描完所有的目标文件和静态库了且U集合为空,那么就会合并和重定义E集合中的目标文件,从而构建出可执行文件了。
若将上面命令改为如下命令:
GCC ex1.c ex2.o ex3.o ex4.a ex7.o
ex7.o引用了ex4.a中ex6.o中的6.A,那么会怎么样呢?
答案是LD会输出错误,并终止。
考虑到步骤4之后,扫描ex7.o的过程。
步骤4之后,还有ex7.o要扫描,那么LD继续扫描ex7.o。ex7.o被加入到E中,
将6.A加入到U中。
这样扫描完了所有的目标文件和静态库了,但是且U集合不为空。LD就会输出错误并终止。
所以一般书籍给的忠告是,将静态库尽量放在命令的最后,也可以反复输入静态库。
由此,为了避免上面的错误,我们可以这样:
GCC ex1.c ex2.o ex3.o ex7.o ex4.a
或是这样
GCC ex1.c ex2.o ex3.o ex4.a ex7.o ex4.a
从上面不难看出,LD只会把引用了的可重定位目标文件链接到可执行文件中,那些没有引用的则不会被包含进去。
以上只是说静态库和可重定位目标文件有先后顺序之分,顺序不当会造成链接错误。如果全部都是可重定位目标文件,那么就没有先后顺序之分了。
更多信息请参见:
《C专家编程》c5以及《computer systems a programmer's perspective》c7
本人享有博客文章的版权,转载请标明出处http://blog.csdn.net/baidu20008
一个C语言程序,经过诸如GCC之类的编译器编译成可执行文件一般会经历4个处理过程,这个大部分的linux入门书籍都有讲到过,如果没有扔掉它,:)!
分别经过C预处理器(CPP)、C编译器(CL)、C汇编器(AS)、C链接器(LD)。这个怎么处理生成什么不是本文的主要内容。
ASCII源文件.C----》ASCII中间文件.i(由CPP处理后生成)------》汇编语言文件.s(由CL处理后生成)-----》可重定位目标文件.o(由AS处理后生成)-----》可执行目标文件(由LD处理后生成)。
说到可执行目标文件和可重定位目标文件不得不讲讲ELF(可执行可链接格式),但ELF在大部分书籍都有讲到,且网上资料相当多。
ELF定义了可重定位和可执行目标文件构成的大致框架。可重定位目标文件中,有很多的节,每个节都是固定大小的条目。这些条目存储了代码、符号、引用、变量和调试等等相关信息。
一个点C文件会引用其他文件的函数或者是全局变量,那么经过上面那一套走下来,会生成点O文件也就是可重定位目标文件。在可重定位目标文件中也会存储引用的函数或全局变量信息,而这些引用的函数和全局变量在链接生成可执行程序时都要进行重定位。
典型地,.rel.text:代码段需要重定位的信息。主要是一些引用的函数;.rel.data:数据段需要重定位的信息。主要是本模块引用的全局变量等。
那么,当我们将所有的可重定位目标文件和静态库生成完成之后,输入命令:
GCC ex1.c ex2.o ex3.o ex4.a会发生什么呢?
可以很简单的说,就是LD链接器会将它们连接生成可执行文件。具体有两个步骤:1是符号解析,2是重定位。
那么符号到底是如何解析的呢?
还是上面的例子:
GCC ex1.c ex2.o ex3.o ex4.a
这其中ex4.a包含ex5.o和ex6.o
不失一般性,我们假设有:
ex1.c引用了ex2.o中的符号2.A,ex3.o中的3.A,ex4.a里面的ex5.o中5.A,ex7.o中的7.A;
ex2.o则是ex3.o中的3.B
ex3.o则是ex2.o中的2.B,
就是如下图所示的链接器命令引用实例:
当然此处假设ex2.o定义了2.A和2.B符号,ex3.o和ex5.o 、ex6.o与此类似。
在符号解析阶段链接器从左往右按照命令行的顺序来扫描可重定位目标文件和静态库。它维持了三个集合E、U和D。
E是可重定位目标文件的集合
U是未解析符号的集合
D为已定义的符号的集合
链接器按照如下步骤来解析符号:
步骤1 | 步骤2 | 步骤3 | 步骤4 | |
E | Ex1.o | Ex1.o Ex2.o | Ex1.o Ex2.o Ex3.o | Ex1.o Ex2.o Ex3.o Ex5.o |
U | 2.A 3.A 5.A | 3.A 5.A 3.B | 5.A | 空 |
D | 2.A 2.B | 2.A 2.B 3.A3.B | 2.A 2.B 3.A 3.B 5.A |
步骤2,由于EX2.O的加入,更新E集合,由于EX2.O本身定义了2.A 2.B两个符号,所以U集合中的2.A 2.B引用可以被解析。解析后的符号被放入到D集合中
步骤3同步骤2;
步骤4中,静态库包含了EX5.O和EX6.O两个可重定位目标文件,那么LD会逐个拿U集合中未解析的符号和静态库的可重定位目标文件中定义的符号匹配,如果有匹配的,这将其加入到E中,如果没有则进行下个可重定位目标文件的匹配过程。本例中,由于Ex5.o中定义的符号5.A正好和U集合中的5.A匹配,那么Ex5.o自身就被放入到E集合中了。像Ex6.o定义个符号和U匹配不上,就没有被放入到E集合中。从这点来看,LD不会将那么没有引用的可重定位目标文件链接到可执行文件中。
最后LD发现,扫描完所有的目标文件和静态库了且U集合为空,那么就会合并和重定义E集合中的目标文件,从而构建出可执行文件了。
若将上面命令改为如下命令:
GCC ex1.c ex2.o ex3.o ex4.a ex7.o
ex7.o引用了ex4.a中ex6.o中的6.A,那么会怎么样呢?
答案是LD会输出错误,并终止。
考虑到步骤4之后,扫描ex7.o的过程。
步骤4之后,还有ex7.o要扫描,那么LD继续扫描ex7.o。ex7.o被加入到E中,
将6.A加入到U中。
这样扫描完了所有的目标文件和静态库了,但是且U集合不为空。LD就会输出错误并终止。
所以一般书籍给的忠告是,将静态库尽量放在命令的最后,也可以反复输入静态库。
由此,为了避免上面的错误,我们可以这样:
GCC ex1.c ex2.o ex3.o ex7.o ex4.a
或是这样
GCC ex1.c ex2.o ex3.o ex4.a ex7.o ex4.a
从上面不难看出,LD只会把引用了的可重定位目标文件链接到可执行文件中,那些没有引用的则不会被包含进去。
以上只是说静态库和可重定位目标文件有先后顺序之分,顺序不当会造成链接错误。如果全部都是可重定位目标文件,那么就没有先后顺序之分了。
更多信息请参见:
《C专家编程》c5以及《computer systems a programmer's perspective》c7
本人享有博客文章的版权,转载请标明出处http://blog.csdn.net/baidu20008
相关文章推荐
- 链接器符号解析算法小解以及静态库链接顺序等等问题
- 链接器解析外部符号的过程 深入理解计算机系统 P475 注:在链接时的注意事项
- 如何解决VS2010 MySql Connect C 链接mysql:error LNK2019: 无法解析外部符号的问题
- VS2012配置Opengl以及“无法解析的外部符号 __imp____glutInitWithExit@12,该符号在函数 _glutInit_ATEXIT_HACK@8 中被引用”问题
- unix下静态库的链接顺序问题(zz)
- VS2013下glew库链接失败问题OpenGL.obj : error LINK2001: 无法解析的外部符号 __imp____glewFramebufferTexture2DEXT
- GCC链接时外部符号解析的问题——extern关键字
- extern "c" 动态链接库符号表导出问题 以及函数参数压栈顺序
- error LNK2019:无法解析的外部符号.lib是用vc6编的,其中用到了消息响应,vc6的消息响应函数和vc2005的消息响应函数的参数和返回值类型不同造成的链接问题.
- 关于vs2010 C++编译链接总是出现“LNK:2019 无法解析的外部符号”的问题
- OpenCv在win下静态库的编译和使用,以及部分无法解析外部符号的错误解决
- extern "c" 动态链接库符号表导出问题 以及函数参数压栈顺序
- android静态库链接顺序问题
- # 数据挖掘算法 ## 18大经典DM算法 18大数据挖掘的经典算法以及代码实现,涉及到了决策分类,聚类,链接挖掘,关联挖掘,模式挖掘等等方面,后面都是相应算法的博文链接,希望能够帮助大家学。 目前
- VS2012中使用opencv出现“无法解析的外部符号”问题
- 静态库与动态库链接、执行时的搜索路径顺序
- 算法学习--3 最大子数组和以及进阶问题
- OpenC的问题,对安装,编译,动态库,链接顺序,运行环境,有界面程序的影响
- [转载链接]跳跃的舞者,舞蹈链(Dancing Links)算法——求解精确覆盖问题
- Class.getResource 等等各种获取路径以及一些问题总结