免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 660 | 回复: 0
打印 上一主题 下一主题

Linux动态链接技术 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-11-09 15:51 |只看该作者 |倒序浏览

在动态链接的应用程序或共享库中,ELF的程序头描述表具有一个PT_DYNAMIC类型的描述符,它指?br>隽?dynamic段的位置,dynamic段用来描述动态链接过程。当应用程序调用的共享库函数时,要通过。plt段进行跳转。plt段又称为过程连接表,它是连接器ld所生成的一组静态的trampline,是只读的可执行的段,包含在。text段一起映射到内存。plt每16个字节为一个槽位,plt的第1个槽位保留给动态解析器使用,其余的槽位表示对不同共享库函数的调用。plt依赖于全局偏移量表(。got段),GOT表是一可写的数据段,包含在。data段中一起映射到内存,用来存放共享符号的绝对地址。?br>τ贸绦虻饔霉蚕砜夂??褪峭ü齪lt槽位上的一条jmp指令跳转到GOT表所指的一个共享函数指针。这样,共享库的重定位就化为对GOT表项的重定位。GOT表的第1个指针指向。dynamic段,第2、3个?br>刚胗雙lt段的第1个槽位对应,用来安装动态解析器。为了少做无用功,Linux采用了动态解析技术,就是说在加载共享库时,并不进行函数的解析,而是安装动态解析器,让共享库调用指向解析器,只有当函数调用发生时才进行解析。为此,在ld在生成可执行程序时,让其GOT共享函数指针指?br>蚋髯詐lt槽位上的两条指令,一条是pushl指令,将该函数所对应GOT重定位表的索引作为参数压入堆栈,然后通过另一条jmp指令跳转到plt槽位1,它再跳转到GOT表第3个指针所表示的动态解析器?br>肟凇U庋?狈⑸?⒊晒?馕瞿勘旰??诠蚕砜庵械牡刂肥保?煤??诔绦騁OT表中的指针就被实际的地址刷新。
下面是基于动态解释器ld。so-1。9。9版本的简单分析
简单的测试文件testso。c:
int x = 0;
int test()
{
return x;
}
用gcc -S -fPIC testso。c编绎成的汇编代码:
。globl x
。data
。align 4
。type x,@object
。size x,4
x:
。long 0
。text
。align 4
。globl test
。type test,@function
test:
pushl %ebp
movl %esp,%ebp
pushl %ebx
call 。L2
。L2:
popl %ebx # 取标号。L2所在的地址
addl $_GLOBAL_OFFSET_TABLE_+[。-。L2],%ebx #_GLOBAL_OFFSET_TABLE_为当前地址到GOT表的偏移
movl
[email=x@GOT(%ebx),%eax]x@GOT(%ebx),%eax[/email]
# ebx在-fPIC编绎的函数中用于指向本模块的GOT表
movl (%eax),%eax #
[email=x@GOT]x@GOT[/email]
表示符号x在GOT表中的索引
movl -4(%ebp),%ebx
leave
ret
。Lfe1:
。size test,。Lfe1-test
用gcc -shared testso。s -o testso。so生成共享库的反汇编的有关输出:
Disassembly of section 。plt:
00000258 :
258: ff b3 04 00 00 pushl 0x4(%ebx) #GOT表的第2个指针,对-fPIC编绎的函数,ebx总是指向GOT表
25d: 00
25e: ff a3 08 00 00 jmp *0x8(%ebx) #跳转到GOT表的第3个指针,调用__dl_linux_resolover
263: 00
264: 00 00 addb %al,(%eax)
266: 00 00 addb %al,(%eax)
268: ff a3 0c 00 00 jmp *0xc(%ebx) # 跳转到共享函数test()所在的GOT的指针
26d: 00
26e: 68 00 00 00 00 pushl x0 # test()所在GOT指针的初始入口
273: e9 e0 ff ff ff jmp 258  # 跳转到plt槽位1
Disassembly of section 。text:
000002d8 :
2d8: 55 pushl %ebp
2d9: 89 e5 movl %esp,%ebp
2db: 53 pushl %ebx
2dc: e8 00 00 00 00 call 2e1
2e1: 5b popl %ebx
2e2: 81 c3 ab 10 00 addl x10ab,%ebx # 取GOT表指针
2e7: 00
2e8: 8b 83 10 00 00 movl 0x10(%ebx),%eax # 从GOT表中取变量x的地址
2ed: 00
2ee: 8b 00 movl (%eax),%eax
2f0: 8b 5d fc movl 0xfffffffc(%ebp),%ebx
2f3: c9 leave
2f4: c3 ret
引用testso的应用程序文件test。c:
main()
{
printf("%d\n",test());
}
用gcc test。c testso。so -o test生成可执行文件的反汇编输出:
Disassembly of section 。plt:
08048398 :
8048398: ff 35 54 95 04 pushl 0x8049554 # GOT表的第2个指针
804839d: 08
804839e: ff 25 58 95 04 jmp *0x8049558 #GOT表的第3个指针,运行_dl_linux_resolver
80483a3: 08
80483a4: 00 00 addb %al,(%eax)
80483a6: 00 00 addb %al,(%eax)
80483a8: ff 25 5c 95 04 jmp *0x804955c # printf()在plt的入口
80483ad: 08
80483ae: 68 00 00 00 00 pushl x0
80483b3: e9 e0 ff ff ff jmp 8048398
80483b8: ff 25 60 95 04 jmp *0x8049560
80483bd: 08
80483be: 68 08 00 00 00 pushl x8
80483c3: e9 d0 ff ff ff jmp 8048398
80483c8: ff 25 64 95 04 jmp *0x8049564 # test()在plt段的调用点
80483cd: 08 # [0x8048564]初始时指向0x80483ce
80483ce: 68 10 00 00 00 pushl x10 # test()在GOT表重定位表。rel。got中的索引
80483d3: e9 c0 ff ff ff jmp 8048398  # 跳转到plt的第1槽位
80483d8: ff 25 68 95 04 jmp *0x8049568
80483dd: 08
80483de: 68 18 00 00 00 pushl x18
80483e3: e9 b0 ff ff ff jmp 8048398
80483e8: ff 25 6c 95 04 jmp *0x804956c
80483ed: 08
80483ee: 68 20 00 00 00 pushl x20
80483f3: e9 a0 ff ff ff jmp 8048398
80483f8: ff 25 70 95 04 jmp *0x8049570
80483fd: 08
80483fe: 68 28 00 00 00 pushl x28
8048403: e9 90 ff ff ff jmp 8048398
Disassembly of section 。text:
080484c8 :
80484c8: 55 pushl %ebp
80484c9: 89 e5 movl %esp,%ebp
80484cb: e8 f8 fe ff ff call 80483c8  #
80484d0: 89 c0 movl %eax,%eax
80484d2: 50 pushl %eax
80484d3: 68 38 85 04 08 pushl x8048538
80484d8: e8 cb fe ff ff call 80483a8
80484dd: 83 c4 08 addl x8,%esp
80484e0: c9 leave
80484e1: c3 ret
ld。so-1。9。9/d-link/i386/resolve。S
#define ALIGN 4
#define RUN linux_run
#define RESOLVE _dl_linux_resolve
#define RESOLVER _dl_linux_resolver
#define EXIT _interpreter_exit
#define INIT __loader_bootstrap
。text
。align ALIGN
。align 16
。globl RESOLVE
。type RESOLVE,@function
RESOLVE:
pusha
lea 0x20(%esp),%eax /* eax = tpnt and reloc_entry params */
pushl 4(%eax) /* push copy of reloc_entry param */
pushl (%eax) /* push copy of tpnt param */
pushl %eax /* _dl_linux_resolver expects a dummy
* param - this could be removed */
#ifdef __PIC__
call 。L24
。L24:
popl %ebx
addl $_GLOBAL_OFFSET_TABLE_+[。-。L24],%ebx
movl
[email=RESOLVER@GOT(%ebx),%ebx]RESOLVER@GOT(%ebx),%ebx[/email]
/* eax = resolved func */
call *%ebx
#else
call RESOLVER
#endif
movl %eax,0x2C(%esp) /* store func addr over original
* tpnt param */
addl xC,%esp /* remove copy parameters */
popa /* restore regs */
ret /* jump to func removing original
* reloc_entry param from stack */
。LFE2:
。size RESOLVE,。LFE2-RESOLVE
d-link/i386/elfinterp。c:
unsigned int _dl_linux_resolver(int dummy, int i)
{
unsigned int * sp;
int reloc_entry;
int reloc_type;
struct elf32_rel * this_reloc;
char * strtab;
struct elf32_sym * symtab;
struct elf32_rel * rel_addr;
struct elf_resolve * tpnt;
int symtab_index;
char * new_addr;
char ** got_addr;
unsigned int instr_addr;
sp = &i;
reloc_entry = sp[1];
tpnt = (struct elf_resolve *) sp[0];
rel_addr = (struct elf32_rel *) (tpnt->dynamic_info[DT_JMPREL] +
tpnt->loadaddr); 取可执行程序的GOT重定位表
this_reloc = rel_addr + (reloc_entry >> 3);
reloc_type = ELF32_R_TYPE(this_reloc->r_info);
symtab_index = ELF32_R_SYM(this_reloc->r_info);
symtab = (struct elf32_sym *) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr);
strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr);
if (reloc_type != R_386_JMP_SLOT) {
_dl_fdprintf(2, "%s: Incorrect relocation type in jump relocations\n",
_dl_progname);
_dl_exit(1);
};
/* Address of jump instruction to fix up */
instr_addr = ((int)this_reloc->r_offset + (int)tpnt->loadaddr);
got_addr = (char **) instr_addr;
#ifdef DEBUG
_dl_fdprintf(2, "Resolving symbol %s\n",
strtab + symtab[symtab_index]。st_name);
#endif
/* Get the address of the GOT entry */
new_addr = _dl_find_hash(strtab + symtab[symtab_index]。st_name,
tpnt->symbol_scope, (int) got_addr, tpnt, 0);
if(!new_addr) {
_dl_fdprintf(2, "%s: can't resolve symbol '%s'\n",
_dl_progname, strtab + symtab[symtab_index]。st_name);
_dl_exit(1);
};
/* #define DEBUG_LIBRARY */
#ifdef DEBUG_LIBRARY
if((unsigned int) got_addr

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/34426/showart_418475.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP