程序的链接-符号解析

首先,从程序到可执行程序要经过如下几个步骤:

  • 预处理
  • 编译
  • 汇编
  • 链接

其中链接又分为如下几部:

  • 确定符号引用关系(符号解析)
  • 合并相关 .o (可重定位)文件(重定位)
  • 确定每个符号的地址(重定位)
  • 在指令中填入新的地址(重定位)

在汇编结束之后,就已经生成了二进制的可执行文件。这次我们主要介绍符号解析这一步。

符号解析

符号解析将每个模块中引用的符号(符号引用)与某个目标模块的定义符号(符号定义)建立关联

简单理解就是比如在某处调用了一个函数fun()。这个fun就可以看做是其符号引用,fun函数的定义可以看做是符号定义。然后符号解析就是把这个符号引用fun与他的函数定义(符号定义)连接起来,也就是建立起一个映射关系,用符号表来记录这个映射关系。

为什么呢?因为每个定义符号在代码段(函数)和数据段(变量)都分配了存储空间,这样经过符号解析之后

看一个例子:

main.c

int a[3] = {1,2,3};


void fun1(){//符号定义
        int a = 1;
        int b = 2;
}

int main(){

        fun();//符号引用
        fun1();//符号引用
        return 0;
}

gcc -c main.c -o main.o

生成可重定位文件

然后用readelf -s main.o

看到符号表如下:

可以看到main函数的节数也就是Ndx 就是1说明他是在可重定位文件的第一节,以及fun1函数也是在1,这就是有符号定义的符号,对比来看fun符号Ndx是UND就是undefine,就是没有定义的意思。fun1 fun main这几个符号的作用域都是全局作用域

强符号,弱符号

强符号:有函数的定义函数名和已初始化的全局变量名

弱符号:没有函数的定义的函数声明以及未初始化的全局变量名

很好理解:

test1.c

int a;//弱符号
int b = 1;//强符号
void p();//弱符号
void fun(){//强符号
    ...
}

int main(){
    ...
}

弱一个符号被定义为一次强符号和多次若符号,则按强定义为准

objcopy

有一个objcopy命令就非常有意思了:

objcopy [OPTION] [INFILE] [OUTFILE]

他有一个选项:-W

-W symbolname, --weaken-symbol=symbolname
将指定符号变为弱符号。该选项可以多次指定

-W能干什么呢?可以在不被别人发现的情况下(也就是不该别人的源代码),将别人的函数偷换成你的实现。这种技术有时候在公司的项目会用到,这样可以避免修改太多源代码,要做的是写一个shell脚本文件就行了。

我们只要把函数原来的符号定义(原来的强符号)弱化即可,这样这个函数的所有符号引用(弱符号)就会以我们自己写的符号定义(强符号)为准。

举个例子:

源代码 main.c

#include <stdio.h>
void f1(){
    printf("f1\n");
}
int main(){
    f1();
    return 0;
}

我想讲f1的函数实现给换掉,我就可以写一个swap.c

#include <stdio.h>
void f1(){
    printf("this is my f1\n");
}

然后操作他们的.o文件 对符号引用做手脚

  • 将main.c swap.c汇编为,o文件
gcc -c main.c -o main.o
gcc -c swap.c -o swap.o
  • 弱化f1.o中的f1符号,将f1强引用弱化为弱引用。
objcopy -W f1 main.o
  • 然后链接合并.o文件
gcc main.o swap.o  -o main

运行可执行文件

会输出this is my f1.

说明函数定义已经被替换.

他还有以下功能:

--redefine-sym old=new
变更符号名称。当链接两个目标文件产生符号名称冲突时,可以使用该选项来解决

可以直接修改符号引用,这个功能顺路介绍一下,因为和符号相关。他可以将一个函数的符号变更为另一个符号。

例子:

还是main.c,将f1符号定义删除

int main(){
    f1();
    return 0;
}

修改swap.c,重新起一个名字f2.

#include <stdio.h>
void f2(){
    printf("this is my f2\n");
}

直接编译肯定会出错,因为f1没有对应的符号定义,这个时候我们可以在不改变main.c的源代码的情况下修改f1的符号为f2

首先:

gcc -c main.c -o main.o
gcc -c swap.c -o swap.o

然后,让f1符号引用变更为f2

objcopy --redefine-sym f1=f2 main.o

最后链接并执行

gcc main.o swap.o -o main
./main

输出 this is my f2

posted @ 2020-09-02 19:30  驿站Eventually  阅读(880)  评论(0编辑  收藏  举报