链接(linking)


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

1.编译器驱动程序

1.1步骤

例: main.c sum.c
预处理 ----> 编译 ----> 汇编 ----> 链接

  1. 运行预处理器(cpp),将C的源程序 main.c 翻译成一个ASCII码的中间文件 main.i
    cpp [other arguments] main.c /tmp/main.i

  2. 驱动程序运行C编译器(cc1),将 main.i 翻译成一个ASCII汇编语言文件 main.s :
    cc1 /tmp/main.i -Og [other arguments] -o /tmp/main.s

  3. 驱动程序运行汇编器(as),将main.s翻译成一个可重定位目标文件(relocatable object file) main.o;经过相同步骤生成sum.o
    as [other arguements] -o /tmp/main.o /tmp/main.s

  4. 最后,运行链接器程序 ld,将 main.o 和 sum.o 以及一些必要的系统目标文件组合起来,创建一个可执行目标文件(executable object file)prog:
    ld -o prog [system object files and args] /tmp/main.o /tmp/sum.o

  5. 运行可执行文件prog.Linux中调用加载器(loader)函数,将可执行文件prog中的代码和数据复制到内存,然后将控制转移到这个程序的开头。
    ./prog

1.2链接器的两个任务
  • 符号解析(symbol resolution)
    将目标文件中的每个全局符号都绑定到一个唯一的定义
  • 重定位(relocation)
    确定每个符号的最终内存地址,并修改对那些符号的引用

2.静态链接

静态链接器(static linker)以一组可重定位目标文件和命令行参数作为输入,生成一个完全链接的、可以加载和运行的可执行目标文件作为输出。

2.1目标文件的三种形式:
  • 可重定位目标文件:包含二进制代码和数据,其形式可以在编译时与其他可重定位目标文件合并起来,创建一个可执行目标文件
  • 可执行目标文件:包含二进制代码和数据,其形式可以被直接复制到内存并执行
  • 共享目标文件:一种特殊类型的可重定位目标文件,在运行时由动态链接器链接和加载,或者隐含地在调用程序被加载和开始执行时,或者根据需要在程序调用 dlopen 库的函数时
2.2静态链接库

编译器提供一种机制,将相关的目标模块打包成为一个单独的文件,称为静态库(static library),可用作链接器的输入。当链接器构造一个输出的可执行文件时,只复制静态库里被应用程序引用的目标模块

例,使用C标准库和数学库中函数的程序可用如下命令进行编译和链接:

linux-> gcc main.c  /usr/lib/libm.a   /usr/lib/libc.a

Linux系统中,静态库以一种称为存档(archive)的特殊文件格式存放在磁盘中。存档文件是一组连接起来的可重定位目标文件的集合,有一个头部来描述每个成员目标文件的大小和位置。存档文件名由后缀名 .a 标识

3.动态链接库

3.1静态库缺点
  • 需要定期维护和更新
    若想使用一个库的最新版本,须以某种方式了解到该库的更新情况,然后显示地将程序与更新了的库重新链接
  • 占用较多内存资源
    几乎每个C程序都是用标准I/O函数(printf、scanf),在运行时,这些函数的代码会被复制到每个运行进程的文本段中。在运行上百个进程的典型系统中,是对稀缺的内存资源的极大浪费
3.2动态链接共享库(shared library)

致力于解决静态库的缺陷;

共享库是一个目标模块,在运行或加载时,可以加载到任意的内存地址,并和一个在内存中的程序链接起来;此过程为动态链接(dynamic linking)

共享库也成为共享目标(shared object) : Linux中 .so 后缀名 , Windows中 DLL

被编译为位置无关代码的共享库可以加载到任何地方,也可以在运行时被多个进程共享。为了加载、链接和访问共享库的函数和数据,应用程序也可以在运行时使用动态链接器。

生成共享库:
linux-> gcc -shared -fpic -o libvector.so addvec.c multvec.c
-fpic:指示编译器生成与位置无关的代码
-shared:指示链接器创建一个共享的目标文件

链接:
linux-> gcc -o prog21 main2.c ./libvector.so
在创建可执行文件时,静态执行一些链接,然后在程序加载时,动态完成链接过程;此时,没有任何 libvector.so 的代码和数据节被复制到可执行文件 prog21 中;

两种共享方式:

  1. 任何给定的文件系统中,一个库只有一个 .so 文件,所有引用该库的可执行目标文件共享这个 .so 文件的代码和数据,而不是像静态库的内容那样被复制和嵌入到引用它们的可执行文件中
  2. 在内存中,一个共享库的 .text 节(已编译程序的机器代码)的一个副本可以被不同的正在运行的进程共享
3.3从应用程序中加载和链接共享库

应用程序在它运行时要求动态链接器加载和链接某个共享库,而无需编译时将那些库链接到应用中

#include <dlfcn.h>
void *dlopen(const char* filename,int flag);
//加载和链接共享库filename;成功返回指向句柄指针,错误返回NULL

void *dlsym(void *handle,char *symbol);
//输入为共享库句柄和一个symbol名字,若存在返回符号地址,否则返回NULL;

int dlclose(void *handle);
//卸载共享库

const char *dlerror(void)
//返回字符串,描述前三个函数的调用错误

例程:

//dll.c
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

int x[2] = {1,2};
int y[2] = {3,4};
int z[2];

int main{
	void *handle;
	void(*addvec)(int *,int *,int *);
	char *error;
	
	handle = dlopen("./libvector.so",RTLD_LAZY);
	//RTLD_LAZY 指示链接器推迟符号解析直到执行来自库中的代码
	if(!handle){
		fprintf(stderr,"%s\n",dlerror());
		}
	
	addvec = dlsym(handle,"addvec");
	if((error=dlerror())!=NULL){
		fprintf(stderr,"%s\n",error);
		exit(1);
		}
	
	addvec(x,y,z,2);
	printf("z=[%d %d]",z[0],z[1]);

	if(dlclose(handle) < 0){
		fprintf(stderr,"%s\n",dlerror());
		exit(1);	
	}
	
	return 0;
}

调用方式:
linux-> gcc -rdynamic -o prog2r dll.c -ldl

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值