符号-链接的接口

l  符号表

 

链接过程的本质就是把多个不同的目标文件之间相互粘在一起。在链接中,目标文件之间相互拼合实际上是目标文件之间对地址的引用,即对函数和变量(函数,变量统称符号)的地址的引用。

符号是链接过程中的粘合剂,整个链接过程是基于符号引用完成的,链接过程中很关键的一部分就是符号的管理,每一个目标文件都会有一个相应的符号表(Symbol Table),这个表里面记录了目标文件中用到的所有符号。每个定义的符号有一个对应的值,叫做符号值,对于变量和函数来说,符号值就是它们的地址。

符号主要包括定义在本文件内的全局符号,在本文件中引用的全局符号(但定义在其它文件),段名,局部符号,行号信息(目标文件指令与源代码中代码行的对应关系)等,对于链接来说,段名,局部符号,行号信息等对其它文件不可见的信息并不重要,关键处理全局符号之间的引用。

 

    ELF符号表的结构如下:

typedef struct {
Elf32_Word st_name;   /*
符号名,符号名在字符串表中的下标
*/
Elf32_Addr st_value;  /*
符号值 */
Elf32_Word st_size;   /*
符号大小,为0时表示大小为0或大小未知
*/
unsigned char st_info;
unsigned char st_other;
Elf32_Half st_shndx;  /*
符号所在的段
*/
} Elf32_Sym;

l  特殊符号

 

使用ld作为链接器来链接生成可执行文件时,它会定义很多特殊的符号,程序员可以直接声明并且引用这些特殊符号,常见的特殊符号包括:

__executable_start,该符号为程序的起始地址。

__etextetext,该符号为代码段的结束地址。

__edataedata,该符号为数据段的结束地址。

__endend,该符号为程序的结束地址。

在程序中可先声明这些符号,并引用,如:

extern char __executable_start[];

printf(“%x\n”, __executable_start);

 

l  弱符号与强符号

 

如果目标文件A,B都定义了某变量global,并进行了初始化,当ld链接目标文件AB时会报错,提示multiple definition of “global”,类似于global(多次定义链接器报错)的这种符号成为强符号,强符号相对于弱符号而言,编译器默认函数和初始化了的全局变量为强符号,未初始化的全局变量为弱符号。可通过__attribute__((weak))来定义任何一个强符号为弱符号。

针对强弱符号,链接器通常按如下规则处理与选择被多次定义的全局符号:

1.  不允许强符号被多次定义(即不同目标文件中不能有同名的强符号);如果有多个强符号定义,则链接器会报错。

2.  如果一个符号在某个目标中为强符号,则其它文件中都是弱符号,则选择强符号。

3.  如果一个符号在所有目标文件中都是弱符号,则选择占用空间最大的一个(但我做了个小测试,貌似并非如此)。

 

l  弱引用与强引用

 

目标文件中对外部符号的引用在被链接时会被正确的决议,如果没有找到该符号的定义,链接器会报符号未定义错误,这种引用被称为强引用,与之相对的称为弱引用,对于弱引用,链接器在链接时如果找不到符号,链接器并不报错可通过__attribute__((weakref))来声明对一个外部符号的引用为弱引用。

弱符号和弱引用对于库来说十分有用,如库中定义的弱符号可以被用户定义的强符号所覆盖,从而使得程序可以使用自定义版本的库函数;或者程序可以对某些扩展功能模块的引用定义为弱引用,当将扩展模块与程序链接在一起时,功能模块就可以正常使用;如果去掉了某些功能模块,那么程序也能正确的链接,只是缺少了相应的功能,这使得程序的功能更加容易裁剪和组合。

 

 

 
posted @ 2013-04-19 14:06  ydzhang  阅读(555)  评论(0编辑  收藏  举报