链接装载与库《程序员的自我修养》 之 全局符号冲突问题
强符号:编译器默认函数和初始化了的全局变量
弱符号:未初始化的全局变量为弱符号(也可以通过gcc的__attribute__((weak))指定)
冲突的例子:如果我们在目标文件A和目标文件B都定义了一个全局整形变量g_variable,并将它们都初始化,这样在链接时链接器就会报multiple definition的错误,这种错误就是因为强符号不能被多次定义。
但如果我们在A中定义了一个double类型的g_variable,B文件中定义了一个int类型的g_variable,它们都没有被初始化过,则链接器会选择double类型8字节为g_variable分配空间。
原因:由于链接器需要支持弱符号机制(对于程序库来说这种模式便于用户进行扩展和自定义),并且链接器并不支持链接时的符号类型,所以允许多个不同类型弱符号存在。
如果想关掉弱符号可以使用gcc的“-fno-common”选项
被多次定义的全局符号,链接器会按照以下规则进行处理:
1. 不允许强符号被多次定义,否则,链接器报符号重复定义错误。
2. 如果一个符号在某个目标文件中是强符号,在其他文件中都是弱符号,那么选择强符号
3. 如果一个符号在所有目标文件中都是弱符号,那么选择占用空间最大的那个,见上例。
2. 共享对象中的符号冲突
当不同共享对象中存在同名符号时,会存在全局符号介入(Global Symbol Interpose)的现象:一个共享对象里面的全局符号被另一个共享对象的同名全局符号覆盖。
关于这个问题,linux下动态链接器的处理策略是:当一个符号需要被加入到全局符号表时,如果相同的符号名已经存在,则后加入的符号被忽略。也就是说,如果不同的共享对象存在同名符号时,最后剩下谁是依赖于动态链接器的加载顺序的(往往按照广度优先)
例如:a.so中存在函数foo,b.so中也存在函数foo,如果一个程序既依赖于a.so也依赖于b.so,并且a.so先被动态链接器加载,则最终程序中调用的foo程序就是a.so中的。