《程序员的自我修养——链接、装载与库》读书笔记—— 7.6 动态链接的步骤和实现

本文详细介绍了动态链接的三个关键步骤:动态链接器的自举,包括其如何处理自身重定位;装载共享对象并搜索路径;以及重定位和初始化过程,包括符号查找和链接。通过LD_DEBUG环境变量跟踪动态链接器的日志,展示了链接和初始化的具体实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简述

动态链接可以分为以下几个步骤:

  1. 动态链接器的自举
  2. 装载共享对象
  3. 重定位和初始化

动态链接器的自举

根据前面的文章,我们知道可执行程序的so由动态链接器进行装载和重定位。但是动态链接器本身也是个so,谁来对它重定位呢?答案是它自己。动态链接器将自己重定位的这段代码过程,被称为自举(Bootstrap)。
自举过程有以下两个特殊性:
1、自举代码不可以使用全局变量,静态变量。这个比较好理解,因为.so里的全局变量,静态变量都需要重定位后才能使用。
2、自举代码不可以使用函数。这个原因其实和上边一样。使用PIC 模式编译的so,对模块内部的函数调用,也是需要使用GOT/PLT的方式,也需要先重定位才能使用。

使用LD_DEBUG 环境变量查看动态链接器工作log

使用LD_DEBUG 环境变量,可以看到动态链接器的工作过程log。如下

zqxl@ubuntu:~/work/practice/link_load_and_lib_learn/_7.5_dynamic_stuctures$ LD_DEBUG=all ./ab

# 下面是查找并装载ab 依赖的.so
     20192:	
     20192:	file=libb.so [0];  needed by ./ab [0]
     20192:	find library=libb.so [0]; searching
     20192:	 search path=./tls/x86_64/x86_64:./tls/x86_64:./tls/x86_64:./tls:./x86_64/x86_64:./x86_64:./x86_64:.		(LD_LIBRARY_PATH)
     20192:	  trying file=./tls/x86_64/x86_64/libb.so
     20192:	  trying file=./tls/x86_64/libb.so
     20192:	  trying file=./tls/x86_64/libb.so
     20192:	  trying file=./tls/libb.so
     20192:	  trying file=./x86_64/x86_64/libb.so
     20192:	  trying file=./x86_64/libb.so
     20192:	  trying file=./x86_64/libb.so
     20192:	  trying file=./libb.so
     20192:	
     20192:	file=libb.so [0];  generating link map
     20192:	  dynamic: 0x00007f9cc735ee48  base: 0x00007f9cc715e000   size: 0x0000000000201038
     20192:	    entry: 0x00007f9cc715e540  phdr: 0x00007f9cc715e040  phnum:                  7
     20192:	
     20192:	

... ...  
# 下面开始对各个so里的符号进行重定位
     20192:	
     20192:	relocation processing: ./liba.so (lazy)
     20192:	symbol=__cxa_finalize;  lookup in file=./ab [0]
     20192:	symbol=__cxa_finalize;  lookup in file=./libb.so [0]
     20192:	symbol=__cxa_finalize;  lookup in file=/lib/x86_64-linux-gnu/libc.so.6 [0]
     20192:	binding file ./liba.so [0] to /lib/x86_64-linux-gnu/libc.so.6 [0]: normal symbol `__cxa_finalize'
     20192:	symbol=g_var_a;  lookup in file=./ab [0]
     20192:	symbol=g_var_a;  lookup in file=./libb.so [0]
     20192:	symbol=g_var_a;  lookup in file=/lib/x86_64-linux-gnu/libc.so.6 [0]
     20192:	symbol=g_var_a;  lookup in file=./liba.so [0]
     20192:	binding file ./liba.so [0] to ./liba.so [0]: normal symbol `g_var_a'
     20192:	symbol=_ITM_registerTMCloneTable;  lookup in file=./ab [0]
     20192:	symbol=_ITM_registerTMCloneTable;  lookup in file=./libb.so [0]
     20192:	symbol=_ITM_registerTMCloneTable;  lookup in file=/lib/x86_64-linux-gnu/libc.so.6 [0]
     20192:	symbol=_ITM_registerTMCloneTable;  lookup in file=./liba.so [0]
     20192:	symbol=_ITM_registerTMCloneTable;  lookup in file=/lib64/ld-linux-x86-64.so.2 [0]
     20192:	symbol=_ITM_deregisterTMCloneTable;  lookup in file=./ab [0]
     20192:	symbol=_ITM_deregisterTMCloneTable;  lookup in file=./libb.so [0]
     20192:	symbol=_ITM_deregisterTMCloneTable;  lookup in file=/lib/x86_64-linux-gnu/libc.so.6 [0]
     20192:	symbol=_ITM_deregisterTMCloneTable;  lookup in file=./liba.so [0]
     20192:	symbol=_ITM_deregisterTMCloneTable;  lookup in file=/lib64/ld-linux-x86-64.so.2 [0]
     20192:	symbol=__gmon_start__;  lookup in file=./ab [0]
     20192:	symbol=__gmon_start__;  lookup in file=./libb.so [0]
     20192:	symbol=__gmon_start__;  lookup in file=/lib/x86_64-linux-gnu/libc.so.6 [0]
     20192:	symbol=__gmon_start__;  lookup in file=./liba.so [0]
     20192:	symbol=__gmon_start__;  lookup in file=/lib64/ld-linux-x86-64.so.2 [0]

... ...

# 调用每个so 的init 段中的代码
     20192:	calling init: ./liba.so
     20192:	
     20192:	
     20192:	calling init: /lib/x86_64-linux-gnu/libc.so.6
     20192:	
     20192:	symbol=__vdso_clock_gettime;  lookup in file=linux-vdso.so.1 [0]
     20192:	binding file linux-vdso.so.1 [0] to linux-vdso.so.1 [0]: normal symbol `__vdso_clock_gettime' [LINUX_2.6]
     20192:	symbol=__vdso_getcpu;  lookup in file=linux-vdso.so.1 [0]
     20192:	binding file linux-vdso.so.1 [0] to linux-vdso.so.1 [0]: normal symbol `__vdso_getcpu' [LINUX_2.6]
     20192:	
     20192:	calling init: ./libb.so
     20192:	
     20192:	
     20192:	initialize program: ./ab
     20192:	
     20192:	

# 这里应该是开始运行ab 的代码了
     20192:	transferring control: ./ab
     20192:	

# 这里应该是延迟绑定的时候的log 输出
     20192:	symbol=g_func_b;  lookup in file=./ab [0]
     20192:	symbol=g_func_b;  lookup in file=./libb.so [0]
     20192:	binding file ./ab [0] to ./libb.so [0]: normal symbol `g_func_b'
     20192:	symbol=g_func_a;  lookup in file=./ab [0]
     20192:	symbol=g_func_a;  lookup in file=./libb.so [0]
     20192:	symbol=g_func_a;  lookup in file=/lib/x86_64-linux-gnu/libc.so.6 [0]
     20192:	symbol=g_func_a;  lookup in file=./liba.so [0]
     20192:	binding file ./libb.so [0] to ./liba.so [0]: normal symbol `g_func_a'
     20192:	

# 与init相对应的finit过程
     20192:	calling fini: ./ab [0]
     20192:	
     20192:	
     20192:	calling fini: ./libb.so [0]
     20192:	
     20192:	
     20192:	calling fini: ./liba.so [0]

附录

/*a.c*/
int g_var_a;
void g_func_a(void)
{
	g_var_a = 0x123;
}
/*b.c*/
extern int g_var_a;

static int *g_ptr_var_a = &g_var_a;

void g_func_a(void);
void g_func_b()
{
	g_func_a();
	g_var_a = 0x456;
	g_ptr_var_a = 0;
}
/*ab.c*/
extern void g_func_b();

int main(int argc, char *argv[])
{

	g_func_b();

	return 0;
}

# Makefile
ab: a.c b.c ab.c
	gcc a.c -fPIC -shared -g -o liba.so
	gcc b.c -fPIC -shared -g -o libb.so -L. -la
	gcc ab.c -no-pie -g -o ab -L. -la -lb
	objdump -ds libb.so > libb.so.S
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值