Linux链接相关知识

链接

首先得知道链接到底是做什么的:

  • 链接是将各种代码和数据片段收集并组合成为一个单一文件的过程,这个文件可被加载(复制)到内存并执行。

这个步骤在什么时候执行:

  • 可执行于编译(源代码翻译成机器代码)时
  • 可执行于加载时(程序被加载器加载到内存并执行时)
  • 可执行于运行时(由应用程序来执行)
一个C源程序到可执行文件的过程

一个C源程序可以被执行,得经过下面四个步骤:

  • 预处理,调用语言预处理器将main.c的源文件翻译成一个ASCII码的中间文件main.i。处理的有:
    • 宏定义如:#define MAX 2000
    • 条件编译时如:#ifdef
    • 头文件如:include<stdio.h>
    • 特殊符号如:LINE标识符被认为是行号
  • 编译, 驱动程序运行编译器(cc1)将main.i翻译成一个ASCII汇编语言文件main.s
  • 驱动程序运行汇编器(as),将main.s翻译成一个可重定位目标文件main.o文件。
  • 运行连接器ld,将main.o和其它必要的可重定位目标文件,以及一些必要的系统文件,创建一个可执行文件。
将多个模块合成一个可执行文件
/* sum.c */
/* $begin sum */
int sum(int *a, int n)
{
    int i, s = 0;
    
    for (i = 0; i < n; i++) { 
        s += a[i];
    }
    return s;
}        
/* $end sum */

/* main.c */
/* $begin main */
int sum(int *a, int n);

int array[2] = {1, 2};

int main() 
{
    int val = sum(array, 2);
    printf("%d\n",val);
}
/* $end main */

运行截图:
在这里插入图片描述

可重定位目标文件

可重定位目标文件包括ELF头,节,节头部表

  • ELF头:
  • 以一个16字节的序列开始,描述了生成该文件的系统的字的大小和字节顺序
  • 剩下的部分包含了一些信息包括ELF头的大小,目标文件的类型,机器类型,节头部表中的条目和数量等。
  • 节头部表:包含接的信息,有不同节的位置和大小。
  • 节:节包括以下节:
  • .text : 已编译程序的机器代码。
  • .rodata: 只读数据
  • .data: 已初始化的全局变量和静态C变量。
  • .bss :未初始化的全局变量和静态C变量,和初始化为0的全局变量和静态C变量。
  • .symtab:一个符号表,存放程序中定义的和引用的函数和全局变量的信息 。
  • .rel.text:一个.text节中位置的列表。
  • .real.data:被模块定义和引用的所有全局变量的重定位信息。
  • .debug:一个调试符号表
  • .line:原始C文件中的行号
  • .strtab:一个字符串表
链接的步骤:
  • 符号解析:目的就是将每个符号引用正好和一个符号定义关联起来。
  • 重定位:通过把每个符号定义和一个内存位置关联起来,从而重定位这些节。
符号和符号表

每个可重定位目标模块m有一个符号表,包含定义和引用的信息,总共有三种符号:

  • 由模块m’定义并能被其他模块引用的全局符号
  • 由其他模块定义并被模块m引用的全局符号
  • 只被模块m定义和引用的局部符号,对应于用static修饰的函数和全局变量
符号解析

将每个符号引用与它输入的可重定位目标文件的符号表中的一个确定的符号定义关联起来,对于只引用在自己模块定义和的引用,十分好处理,但对于碰到一个不是在当前模块定义的全局符号, 就假设该符号在其他模块定义,交由链接器处理。如果说多个目标文件有相同的名字,是如下规定的:

  • 不允许有多个强符号
  • 如果有一个强符号和多个弱符号同名,则选强符号为其定义
  • 如果有多个弱符号同名,则从这些弱符号中任意选一个

强符号: 函数和已初始化的全局变量
弱符号:未初始化的全局变量

看一个程序
/*    mismatch-main.c    */

#include <stdio.h>
long int x;  /* Weak symbol */

int main(int argc, char *argv[]) {
    printf("%ld\n", x);
    return 0;
}
/*mismatch-variable.c     */

/*    Global strong symbol */ 
double x = 3.14;

在第一个代码 x是弱符号,因为它没有初始化,而在第二个代码中x是赋了初值,那么它是强符号,则整个程序在为x引用时是取得强符号为其定义。则最后输出:
在这里插入图片描述

重定位
  • 重定位节和符号定义:链接器将所有相同类型的节合并成为同一类型的新的聚合节。
  • 链接器将运行时内存地址赋给新的聚合节,赋给输入模块的定义的每个节,以及输入模块定义的每个符号。
  • 重定位节中的符号引用,链接器修改代码节和数据节中对每个符号的引用,使得它们指向正确的运行时地址。

两种最基本的重定位类型:

  • 重定位一个PC相对地址的引用
  • 重定位一个绝对地址的引用
一个具体程序的例子

代码如下:

#include <stdio.h>
int time;
int foo(int a) {
    int b = a + 1;
    retutn b;
}
int main(int argc, char *argv[])
{
    printf("%d\n", foo(5));
    return 0;
}/*main.c*/

首先看ELF文件头:

  • 命令为 readelf -h main.o
  • 运行截图:
    在这里插入图片描述

再看可重定位文件的符号条目:

  • 命令为 readelf -s main.o
  • 运行截图:
    在这里插入图片描述
    这里面有符号 foo,main,printf,time,前面分别写了它的类型,有FUNC函数,NOTYPE,这是因为后面为UND,即undefined,没定义,有OBJECT。

看节信息:

  • 命令为: readelf -S main.o
  • 运行截图:
    在这里插入图片描述
    这里面有.data,.bss等信息。

看重定位信息:

  • 命令为:objdump -r main.c
  • 运行截图:
    在这里插入图片描述
    这里面有重定位的.text等信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值