动态链接库中函数的地址确定---PLT和GOT [转]
前面写过动态链接库 延迟绑定的一篇博文,那篇文章我非常喜欢,但是当时刚搞清楚,自己写的比较凌乱,我最近学习了Ulrich Drepper的How to write share library,学习了几篇其他的讲述动态链接的文章,再次整理了这篇文章。
- #include<stdio.h>
- #include<stdlib.h>
- #include<pthread.h>
- void* myfunc()
- {
- while(1)
- {
- sleep(10);
- }
- return NULL;
- }
- int main()
- {
- sleep(15);
- pthread_t tid = 0;
- int ret = pthread_create(&tid,NULL,myfunc,NULL);
- if(ret)
- {
- fprintf(stderr,"pthread create failed %m \n");
- return -1;
- }
- ret = pthread_join(tid,NULL);
- if(ret)
- {
- fprintf(stderr,"pthread join failed %m\n");
- return -2;
- }
- return 0;
- }
- root@libin:~/program/C/plt_got# LD_DEBUG=symbols ./test
- 2849: symbol=_res; lookup in file=./test [0]
- 2849: symbol=_res; lookup in file=/lib/tls/i686/cmov/libpthread.so.0 [0]
- 2849: symbol=_res; lookup in file=/lib/tls/i686/cmov/libc.so.6 [0]
- 2849: symbol=_IO_file_close; lookup in file=./test [0]
- 2849: symbol=_IO_file_close; lookup in file=/lib/tls/i686/cmov/libpthread.so.0 [0]
- 2849: symbol=_IO_file_close; lookup in file=/lib/tls/i686/cmov/libc.so.6 [0]
- 2849: symbol=rpc_createerr; lookup in file=./test [0]
- 2849: symbol=rpc_createerr; lookup in file=/lib/tls/i686/cmov/libpthread.so.0 [0]
- 2849: symbol=rpc_createerr; lookup in file=/lib/tls/i686/cmov/libc.so.6 [0]
然后停了15s,才解析出pthread_create的地址,由此可见,得确是运行时重定位,知道用到这个函数pthread_create才真正去找这个函数的地址。
- 2849:
- 2849: symbol=sleep; lookup in file=./test [0]
- 2849: symbol=sleep; lookup in file=/lib/tls/i686/cmov/libpthread.so.0 [0]
- 2849: symbol=sleep; lookup in file=/lib/tls/i686/cmov/libc.so.6 [0]
- ===================================================================================
- 2849: symbol=pthread_create; lookup in file=./test [0]
- 2849: symbol=pthread_create; lookup in file=/lib/tls/i686/cmov/libpthread.so.0 [0]
- 2849: symbol=__getpagesize; lookup in file=./test [0]
- 2849: symbol=__getpagesize; lookup in file=/lib/tls/i686/cmov/libpthread.so.0 [0]
- 2849: symbol=__getpagesize; lookup in file=/lib/tls/i686/cmov/libc.so.6 [0]
- 2849: symbol=mmap; lookup in file=./test [0]
- 2849: symbol=mmap; lookup in file=/lib/tls/i686/cmov/libpthread.so.0 [0]
- 2849: symbol=mmap; lookup in file=/lib/tls/i686/cmov/libc.so.6 [0]
- root@libin:~/program/C/plt_got# readelf -r test
- Relocation section '.rel.dyn' at offset 0x394 contains 2 entries:
- Offset Info Type Sym.Value Sym. Name
- 08049ff0 00000206 R_386_GLOB_DAT 00000000 __gmon_start__
- 0804a020 00000905 R_386_COPY 0804a020 stderr
- Relocation section '.rel.plt' at offset 0x3a4 contains 6 entries:
- Offset Info Type Sym.Value Sym. Name
- 0804a000 00000107 R_386_JUMP_SLOT 00000000 pthread_join
- 0804a004 00000207 R_386_JUMP_SLOT 00000000 __gmon_start__
- 0804a008 00000407 R_386_JUMP_SLOT 00000000 __libc_start_main
- 0804a00c 00000507 R_386_JUMP_SLOT 00000000 fprintf
- 0804a010 00000607 R_386_JUMP_SLOT 00000000 pthread_create
- 0804a014 00000707 R_386_JUMP_SLOT 00000000 sleep
我们看到了有全局变量stderr和__gmon_start__需要重定位,这些本文并不关心。下面是需要重定位的函数,可以看出,我们调用动态库里面的函数都在这了,fprintf是Glibc库的,pthread_create是pthread库的等等。
动态库里面需要重定位的函数在.got.plt这个段里面,我们看下:


- (gdb) b main
- Breakpoint 1 at 0x8048551: file test.c, line 19.
- (gdb) r
- Starting program: /home/libin/program/C/plt_got/test
- [Thread debugging using libthread_db enabled]
- Breakpoint 1, main () at test.c:19
- 19 sleep(15);
- (gdb) x/24x 0x8049ff4
- 0x8049ff4 <_GLOBAL_OFFSET_TABLE_>: 0x08049f18 0x0012c8f8 0x00123270 0x0804841a
- 0x804a004 <_GLOBAL_OFFSET_TABLE_+16>: 0x0804842a 0x0015daf0 0x0804844a 0x0804845a
- 0x804a014 <_GLOBAL_OFFSET_TABLE_+32>: 0x0804846a 0x00000000 0x00000000 0x0029c580
- 0x804a024 : 0x00000000 0x00000000 0x00000000 0x00000000
- 0x804a034: 0x00000000 0x00000000 0x00000000 0x00000000
- 0x804a044: 0x00000000 0x00000000 0x00000000 0x00000000
蓝色的0x0849f18是dynamic段的地址
- [21] .dynamic DYNAMIC 08049f18 000f18 0000d8 08 WA 7 0 4
- (gdb) disas main
- ....
- 0x0804857e <+54>: lea 0x1c(%esp),%eax
- 0x08048582 <+58>: mov %eax,(%esp)
- 0x08048585 <+61>: call 0x8048454<pthread_create@plt>
- 0x0804858a <+66>: mov %eax,0x18(%esp)
- 0x0804858e <+70>: cmpl $0x0,0x18(%esp)
- .....
要执行pthread_create 函数,跳到PLT部分。
- libin@libin:~/program/C/plt_got$ objdump -dj .plt test
- test: file format elf32-i386
- Disassembly of section .plt:
- 08048404 <pthread_join@plt-0x10>:
- 8048404: ff 35 f8 9f 04 08 pushl 0x8049ff8
- 804840a: ff 25 fc 9f 04 08 jmp *0x8049ffc
- 8048410: 00 00 add %al,(%eax)
- ...
- 08048414 <pthread_join@plt>:
- 8048414: ff 25 00 a0 04 08 jmp *0x804a000
- 804841a: 68 00 00 00 00 push $0x0
- 804841f: e9 e0 ff ff ff jmp 8048404 <_init+0x30>
- 08048424 <__gmon_start__@plt>:
- 8048424: ff 25 04 a0 04 08 jmp *0x804a004
- 804842a: 68 08 00 00 00 push $0x8
- 804842f: e9 d0 ff ff ff jmp 8048404 <_init+0x30>
- 08048434 <__libc_start_main@plt>:
- 8048434: ff 25 08 a0 04 08 jmp *0x804a008
- 804843a: 68 10 00 00 00 push $0x10
- 804843f: e9 c0 ff ff ff jmp 8048404 <_init+0x30>
- 08048444 <fprintf@plt>:
- 8048444: ff 25 0c a0 04 08 jmp *0x804a00c
- 804844a: 68 18 00 00 00 push $0x18
- 804844f: e9 b0 ff ff ff jmp 8048404 <_init+0x30>
- 08048454 <pthread_create@plt>:
- 8048454: ff 25 10 a0 04 08 jmp *0x804a010
- 804845a: 68 20 00 00 00 push $0x20
- 804845f: e9 a0 ff ff ff jmp 8048404 <_init+0x30>
- 08048464 <sleep@plt>:
- 8048464: ff 25 14 a0 04 08 jmp *0x804a014
- 804846a: 68 28 00 00 00 push $0x28
- 804846f: e9 90 ff ff ff jmp 8048404 <_init+0x30>
PLT部分认为pthread_create函数存放在GOT,0x804a010是GOT里面的一个条目,这个条目存储着pthread_create函数的地址。当第二次以至于第N次调用pthead_create的时候,的的确确存放着pthread_create的地址,但是第一次不行,第一次这个条目里面还没记录这个地址。那么这个条目记录的是什么呢?
- (gdb) x/10i 0x8048454
- 0x8048454 <pthread_create@plt>: jmp *0x804a010
- 0x804845a <pthread_create@plt+6>: push $0x20
- 0x804845f <pthread_create@plt+11>: jmp 0x8048404
- 0x8048464 <sleep@plt>: jmp *0x804a014
- 0x804846a <sleep@plt+6>: push $0x28
- 0x804846f <sleep@plt+11>: jmp 0x8048404
- 0x8048474: add %al,(%eax)
- 0x8048476: add %al,(%eax)
- 0x8048478: add %al,(%eax)
- 0x804847a: add %al,(%eax)
- (gdb) x/10x 0x804a010
- 0x804a010 <_GLOBAL_OFFSET_TABLE_+28>: 0x0804845a 0x0804846a 0x00000000 0x00000000
- 0x804a020 <stderr@@glibc_2.0>: 0x0029c580 0x00000000 0x00000000 0x00000000
- 0x804a030: 0x00000000 0x00000000
0x804a010这个地址最终应该记录的是pthread_create的地址,但是目前还不是,记录的是0x084845a
- 08048454 <pthread_create@plt>:
- 8048454: ff 25 10 a0 04 08 jmp *0x804a010
- 804845a: 68 20 00 00 00 push $0x20
- 804845f: e9 a0 ff ff ff jmp 8048404 <_init+0x30>
从PLT跳到GOT 找地址,但是第一次找的时候,并不是pthread_create的地址,而是又跳回来PLT,我们看到push了0x20之后,跳到了0x8048404。 每一个PLT的代码段,都是push了一个值之后,跳到了0x8048404。大家可以去上面的图验证。

- (gdb) x/10i 0x8048404
- 0x8048404: pushl 0x8049ff8
- 0x804840a: jmp *0x8049ffc
- 0x8048410: add %al,(%eax)
- 0x8048412: add %al,(%eax)
- 0x8048414 <pthread_join@plt>: jmp *0x804a000
- 0x804841a <pthread_join@plt+6>: push $0x0
- 0x804841f <pthread_join@plt+11>: jmp 0x8048404
- 0x8048424 <__gmon_start__@plt>: jmp *0x804a004
- 0x804842a <__gmon_start__@plt+6>: push $0x8
- 0x804842f <__gmon_start__@plt+11>: jmp 0x8048404
- (gdb) x/10x 0x8049ffc
- 0x8049ffc <_GLOBAL_OFFSET_TABLE_+8>: 0x00123270 0x0804841a 0x0804842a 0x0015daf0
- 0x804a00c <_GLOBAL_OFFSET_TABLE_+24>: 0x0804844a 0x0804845a 0x0804846a 0x00000000
- 0x804a01c <__dso_handle>: 0x00000000 0x0029c580
- (gdb) x/10i 0x00123270
- 0x123270 <_dl_runtime_resolve>: push %eax
- 0x123271 <_dl_runtime_resolve+1>: push %ecx
- 0x123272 <_dl_runtime_resolve+2>: push %edx
- 0x123273 <_dl_runtime_resolve+3>: mov 0x10(%esp),%edx
- 0x123277 <_dl_runtime_resolve+7>: mov 0xc(%esp),%eax
- 0x12327b <_dl_runtime_resolve+11>: call 0x11d5a0 <_dl_fixup>
- 0x123280 <_dl_runtime_resolve+16>: pop %edx
- 0x123281 <_dl_runtime_resolve+17>: mov (%esp),%ecx
- 0x123284 <_dl_runtime_resolve+20>: mov %eax,(%esp)
- 0x123287 <_dl_runtime_resolve+23>: mov 0x4(%esp),%eax
我们看到0x8049ffc就是GOT的第三项,前文提到的dl_runtime_resolve的地址。这个函数将帮助我们将pthread_create函数地址定位,并且填入GOT表的相应位置 0x804a010。
- (gdb) b main
- Breakpoint 1 at 0x8048551: file test.c, line 19.
- (gdb) r
- Starting program: /home/libin/program/C/plt_got/test
- [Thread debugging using libthread_db enabled]
- Breakpoint 1, main () at test.c:19
- 19 sleep(15);
- (gdb) watch *0x804a010
- Hardware watchpoint 2: *0x804a010
- (gdb) c
- Continuing.
- Hardware watchpoint 2: *0x804a010
- Old value = 134513754
- New value = 1260912
- _dl_fixup (l=<value optimized out>, reloc_arg=<value optimized out>) at dl-runtime.c:155
- 155 dl-runtime.c: 没有那个文件或目录.
- in dl-runtime.c
- (gdb) bt
- #0 _dl_fixup (l=<value optimized out>, reloc_arg=<value optimized out>) at dl-runtime.c:155
- #1 0x00123280 in _dl_runtime_resolve () at ../sysdeps/i386/dl-trampoline.S:37
- #2 0x0804858a in main () at test.c:21
- (gdb)
- (gdb) x/10i 1260912
- 0x133d70 <__pthread_create_2_1>: push %ebp
- 0x133d71 <__pthread_create_2_1+1>: mov %esp,%ebp
- 0x133d73 <__pthread_create_2_1+3>: push %edi
- 0x133d74 <__pthread_create_2_1+4>: push %esi
- 0x133d75 <__pthread_create_2_1+5>: push %ebx
- 0x133d76 <__pthread_create_2_1+6>: call 0x132340 <__i686.get_pc_thunk.bx>
- 0x133d7b <__pthread_create_2_1+11>: add $0x10279,%ebx
- 0x133d81 <__pthread_create_2_1+17>: sub $0x4c,%esp
- 0x133d84 <__pthread_create_2_1+20>: mov 0xc(%ebp),%edx
- 0x133d87 <__pthread_create_2_1+23>: test %edx,%edx
这是第一次。第二次就比较简单了,因为GOT里面有一个条目已经有了pthread_create函数的地址。
来源:http://blog.chinaunix.net/uid-24774106-id-3349549.html
动态链接库中函数的地址确定---PLT和GOT [转]的更多相关文章
- 如何识别IDA反汇编中遇到的动态链接库中的函数
在使用IDA静态反汇编时,如果正在逆向的文件中有动态链接库函数(比如调用了程序自定义so库中的函数),IDA只会显示一个地址,跟进去会发现是延迟绑定中关于plt的代码,无法知道具体调用了哪个函数,对于 ...
- JavaScript中函数参数的按值传递与按引用传递(即按地址传递)
首先声明一句:JavaScript中所有函数的参数都是按值传递的!不存在按引用传递! 在讲传递参数之前我们先来讲一下指针. 学过C指针的应该都知道,指针变量中保存的是一个地址,程序可以根据所保存的地址 ...
- VC 使用msxml6.dll动态链接库中的函数读写XML文件
VC 使用msxml6.dll动态链接库中的函数读写XML文件 目录 1 引言 2 .dll使用方法 3 常用函数总结 4 实例应用 5 运行效果预览 6 补充说明 7 不足之处 8 更新 引言: ...
- 在DLL动态链接库中封装VCL的MDI子窗体
在DLL动态链接库中封装VCL的MDI子窗体不多说了,看代码就应该明白了,曾经我遇到的问题,现在放出来大家共享! 这里是工程文件的部分: 在DLL中封装MDI子窗体需要重写DLL入口函数,具体代码如下 ...
- 【C/C++开发】C语言 DLL(动态链接库)中申请动态内存释放的问题
参考:首先,声明一点,凡是使用malloc之类命令动态申请的内存,必须进行释放操作,否则就会发生内存泄漏问题. DLL中申请的内存释放,如果没有做过,很可能会认为是直接在调用程序中释放就可以了,其实不 ...
- 如何理解javaSript中函数的参数是按值传递
本文是我基于红宝书<Javascript高级程序设计>中的第四章,4.1.3传递参数小节P70,进一步理解javaSript中函数的参数,当传递的参数是对象时的传递方式. (结合资料的个人 ...
- JS 中没有按地址(引用)传递,只有按值传递
很多人,包括我,受书本知识消化不彻底的影响,认为 JS 中参数有两种传递方式:数字.字符串等按值传递:数组.对象等按地址(引用)传递.对此种观点,我们要谨慎. var v1 = [] var v2 = ...
- string.h文件中函数用法
下面为string.h文件中函数的详细用法: strcpy函数名:strcpy功 能: 拷贝一个字符串到另一个用 法: char *strcpy(char *destin, char *source) ...
- 三种语言(c++、as、lua)中函数的差异性
对于不同的语言, 尤其是静态语言和动态语言, 对于函数的定义(即如何看待一个函数)和处理截然不同.具体来说可以分为两类: 1.将函数视为第一类型值, 即函数和其他的对象一样, 都是语言中一个普通的对象 ...
随机推荐
- lib静态链接库,dll动态链接库,h文件
最近在弄摄像头,发现我在调用摄像头自带的函数的时候,库没连接上,于是经过高人指点,学习了一下lib静态链接库,dll动态链接库来补充一下自己的基础知识. 一.首先我们来介绍一下lib静态链接库. li ...
- 3D中的切线空间简介
转自:http://www.cnblogs.com/cxrs/archive/2009/10/25/1589515.html 1. 什么是Tangent space? Tangent space和wo ...
- System.out.println()输出到指定文件里
public static void main(String[] args) throws Exception{ String str = "abcd"; PrintStream ...
- hdu 2041
ps:这道题之前一直没思路,有大神提醒我用递推,但当时没搞清...今天做了那个小蜜蜂..才懂得用递推做这道题.. 代码: #include "stdio.h"long long d ...
- python03函数、递归
本节内容 1. 函数基本语法及特性 2. 参数与局部变量 3. 返回值 4.递归 5.匿名函数 6.函数式编程介绍 7.高阶函数 8.内置函数 1.函数基本语法及特性 函数是什么? 函数一词来源于数学 ...
- Reason we use Camel
Camel is mainly for integration purpose, in our project we also use it inside the single component t ...
- HDU 1721
http://acm.hdu.edu.cn/showproblem.php?pid=1721 非常有趣的一道水题,注意到相隔一个点的粒子数是可以相互转移的,所以只要判红点的和与蓝点的和是否相等 #in ...
- Smart210学习记录-------文件操作
一.linux文件操作(只能在linux系统上用) 创建:int creat(const char* filename, mode_t mode) filename 表示要创建的文件名,mode表示对 ...
- C++学习笔记15:操作符重载的函数原型列表(推荐)
//普通四则运算 friend A operator +(const A & lhs, const A & rhs); friend A operator -(const A & ...
- LeetCode First Bad Version (二分查找)
题意: 有一个bool序列表示对应下标的版本是否出问题(下标从1开始),如果一个版本出了问题,那么其后面全部版本必定出问题.现在给出判断任意版本是否出问题的API,请找到第一个出问题的版本. 思路: ...