您的位置:首页 > 其它

链接器对符号的管理和解析

2015-09-17 16:59 162 查看

链接

符号和符号表

如下是示例代码,文件为swap.c

extern int buf[];

int *bufp0 = &buf[0];
static int *bufp1;

static void incr() 
{
    static int count;       

    count++;  
}

void swap() 
{
    int temp;   

    incr();
    bufp1 = &buf[1];
    temp = *bufp0;
    *bufp0 = *bufp1;
    *bufp1 = temp;
}


如下是经过预处理,编译后对应的ELF文件swap.o,它的符号表.symbol

Symbol table ‘.symtab’ contains 12 entries:

Num: Value Size Type Bind Vis Ndx Name

0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND

1: 0000000000000000 0 FILE LOCAL DEFAULT ABS swap.c

2: 0000000000000000 0 SECTION LOCAL DEFAULT 1

3: 0000000000000000 0 SECTION LOCAL DEFAULT 3

4: 0000000000000000 0 SECTION LOCAL DEFAULT 5

5: 0000000000000000 0 SECTION LOCAL DEFAULT 7

6: 0000000000000000 0 SECTION LOCAL DEFAULT 8

7: 0000000000000000 0 SECTION LOCAL DEFAULT 6

8: 0000000000000000 38 FUNC GLOBAL DEFAULT 1 swap

9: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND buf

10: 0000000000000008 8 OBJECT GLOBAL DEFAULT COM bufp1

11: 0000000000000000 8 OBJECT GLOBAL DEFAULT 3 bufp0

以上列表的每一个条目的格式使用一个C结构表示,如下:

typedef struct { 
    int   name;      /* String table offset */ 
    char  type:4,    /* Function or data (4 bits) */ 
      binding:4; /* Local or global (4 bits) */ 
    char  reserved;  /* Unused */  
    short section;   /* Section header index */
    long  value;     /* Section offset or absolute address */ 
    long  size;      /* Object size in bytes */ 
} Elf64_Symbol;


下面解释该结构各个成员描述了列表各个列,其中section成员对应Nxd列,说明每一个符号必须与某一个节是相关的,比如符号bufp0是在C文件swap.c里的一个初始化的全局变量,对应在节.data占用8字节大小,再比如符号bufp1是在C文件swap.c里的一个未初始化的全局变量,符号表里使用COM所引导节.bss。而swap 符号是在C文件swap.c里的一个函数,对应在节.text占用38字节大小。

符号解析

符号从链接的角度上来看分为3类,一类是模块内的带有static属性的函数或全局变量,这类符号不能被其他模块引用。第二类是模块内定义且被模块自身引用的全局符号。第三类是由其他模块定义但被自己模块引用的全局符号,这类符号叫做外部符号

第一:编译器能识别出重定义并在编译时报告这种错误,一个模块的本地符号只允许有一个定义

第二:编译器能识别出未定义并在编译时报告这个错误,比如,第三类符号,如果编译器在其他任何输入模块都找不到它的定义的话,编译器就会报告符号未定义错误

第三:编译如何解析多处定义的全局符号,使用如下规则:

规则1,不允许有多个强符号

规则2,如果有一个强符号和多个弱符号,那么选择强符号

规则3,如果有多个弱符号,那么从这些弱符号中任意选择一个。

规则2的实验:

code.c

#include <stdio.h>
void f(void);

int x = 15213; 

int main()  
{ 
    f(); 
    printf("x = %d\n", x); 
    return 0;
}


bar.c

int x; 

void f()  
{ 
    x = 15212; 
}


构建测试程序,gcc -O2 -o test code.c bar.c

运行测试,观察结果:# ./test

x = 15212。

发现链接器选择了强符号,而强符号的值被恶意修改了,这是危险的。

规则3的实验:

code.c

#include <stdio.h>
void f(void);

int x; 

int main()  
{ 
    x = 15123;
    f(); 
    printf("x = %d\n", x); 
    return 0;
}


bar.c

int x; 

void f()  
{ 
    x = 15212; 
}


构建测试程序,gcc -O2 -o test code.c bar.c

运行测试,观察结果:# ./test

x = 15212

发现链接器选择了code.c文件里定义的弱符号,该弱符号的值在其他文件被错误的修改了,这是不符合我们预期的,是危险的。

以上是程序设计时,容易忽略的问题,应特别注意。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: