我在Linux上做了一些关于共享库的实验。通过阅读几篇论文,我想我知道在调用共享库函数时会发生什么 但是当我试图跟踪内存以获取共享库函数中的二进制代码时,我发现了一些奇怪的东西。在我看来,在调用共享库函数之后,.got.plt中的相应插槽应该包含实际的函数地址,但我的实验表明它仍然保持不变,即func @ plt节中第二条指令的地址。我对此很困惑,所以如果有人能帮助我吗? 这是我的代码和输出:
#include <stdio.h>
#include <string.h>
typedef unsigned long u_l;
int main()
{
char *p_ch = strstr("abc", "b");
printf("result = %s\n", p_ch);
long long *p = (long long *) &strstr;
printf("data = %llx\n", *(p));
long long k = *p >> 16;
u_l *entry_addr = (u_l *)(k & 0x00000000ffffffff);
printf("entry_addr = %lx\n", entry_addr);
u_l *func_addr = (u_l *)*entry_addr;
printf("func_addr = %lx\n", func_addr);
printf("code = %llx\n", *func_addr);
return 0;
}
输出:
result = bc
data = 680804a00c25ff
entry_addr = 804a00c
func_addr = 8048326
code = 68080400000068
先谢谢!
PS:请不要问我为什么需要获取共享库函数的代码。当然我知道源代码和二进制文件可以很容易地获得。这只是一个实验
我的GCC版本是4.7.3。内核版本是3.8.0-35
答案 0 :(得分:2)
不确定您的程序的逻辑是什么,但我会尝试显示地址更改的位置。
$ gcc -Wall -g test.c
$ gdb a.out
(gdb) break main
Breakpoint 1 at 0x40054c: file test.c, line 8.
(gdb) run
(gdb) disassemble
Dump of assembler code for function main:
0x0000000000400544 <+0>: push %rbp
0x0000000000400545 <+1>: mov %rsp,%rbp
0x0000000000400548 <+4>: sub $0x30,%rsp
=> 0x000000000040054c <+8>: movq $0x4006fd,-0x28(%rbp)
0x0000000000400554 <+16>: mov $0x400700,%eax
0x0000000000400559 <+21>: mov -0x28(%rbp),%rdx
0x000000000040055d <+25>: mov %rdx,%rsi
0x0000000000400560 <+28>: mov %rax,%rdi
0x0000000000400563 <+31>: mov $0x0,%eax
0x0000000000400568 <+36>: callq 0x400430 <printf@plt>
0x000000000040056d <+41>: movq $0x400450,-0x20(%rbp)
0x0000000000400575 <+49>: mov -0x20(%rbp),%rax
0x0000000000400579 <+53>: mov (%rax),%rdx
0x000000000040057c <+56>: mov $0x40070d,%eax
0x0000000000400581 <+61>: mov %rdx,%rsi
0x0000000000400584 <+64>: mov %rax,%rdi
0x0000000000400587 <+67>: mov $0x0,%eax
0x000000000040058c <+72>: callq 0x400430 <printf@plt>
0x0000000000400591 <+77>: mov -0x20(%rbp),%rax
0x0000000000400595 <+81>: mov (%rax),%rax
0x0000000000400598 <+84>: sar $0x10,%rax
0x000000000040059c <+88>: mov %rax,-0x18(%rbp)
让我们在printf
条目(0x400430)的PLT表中创建断点并继续:
(gdb) break *0x400430
Breakpoint 2 at 0x400430
(gdb) continue
Continuing.
Breakpoint 2, 0x0000000000400430 in printf@plt ()
(gdb) disassemble
Dump of assembler code for function printf@plt:
=> 0x0000000000400430 <+0>: jmpq *0x200bca(%rip) # 0x601000 <printf@got.plt>
0x0000000000400436 <+6>: pushq $0x0
0x000000000040043b <+11>: jmpq 0x400420
End of assembler dump.
(gdb) x/x 0x601000
0x601000 <printf@got.plt>: 0x00400436
在PLT表中你可以看到存储在GOT中的地址间接跳转到0x601000(0x200bca + 0x400430 + 6),这在第一次函数调用时解析为PLT中的下一个地址( 0x00400436 :pushq和jump到动态链接器)。动态链接器找到真实的printf
,更新它的GOT条目并跳转到它。
下次你调用相同的printf
函数(并点击断点)时,它在GOT 0x601000的条目已经更新为 0xf7a6d840 ,所以直接跳转到printf
,而不是动态链接器。
(gdb) c
Continuing.
result = bc
Breakpoint 2, 0x0000000000400430 in printf@plt ()
(gdb) disassemble
Dump of assembler code for function printf@plt:
=> 0x0000000000400430 <+0>: jmpq *0x200bca(%rip) # 0x601000 <printf@got.plt>
0x0000000000400436 <+6>: pushq $0x0
0x000000000040043b <+11>: jmpq 0x400420
End of assembler dump.
(gdb) x/x 0x601000
0x601000 <printf@got.plt>: 0xf7a6d840
此示例来自64位Linux。在其他* NIX&#39; es组件或类似细节可能会有所不同,但想法保持不变。
答案 1 :(得分:0)
还有一件事,我在libc.so中找不到printf,...
该程序为每个作为参数给出的函数显示一个地址和包含的库(使用Glibc扩展名):
/* cc -ldl */
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
int main(int argc, char *argv[])
{
while (*++argv)
{
void *handle = dlopen(NULL, RTLD_NOW);
if (!handle) puts(dlerror()), exit(1);
void *p = dlsym(handle, *argv);
char *s = dlerror();
if (s) puts(s), exit(1);
printf("%s = %p\n", *argv, p);
Dl_info info;
if (dladdr(p, &info))
printf("%s contains %s\n", info.dli_fname, info.dli_sname);
}
}