http://arthurchiao.art/blog/how-does-ltrace-work-zh/
strace
是一个系统调用,也是一个信号跟踪器(signal tracer),
- 主要用于跟踪系统调用,打印系统调用的参数、返回值、时间戳等很多信息。
- 也可以跟踪和打印进程收到的信号。
- 在前一篇文章strace 是如何工作的 中介绍过,
strace
内部基于 ptrace 系统调用。
ltrace
是一个函数库调用跟踪器(library call tracer)
- 顾名思义,主要用于跟踪程序的函数库调用信息。
- 它也可以像
strace
一样跟踪系统调用和信号。 - 它的命令行参数和
strace
很相似。 ltrace
也是基于 ptrace。
虽然 strace
和 ltrace
底层都是基于 ptrace 系统调用, 但跟踪库函数和跟踪系统调用还是有很大差别的,这就是为什么会有 ltrace
的原因。
#重要概念
共享库可以被加载到任意地址。这意味着,共享库内的函数地址只有在运行时加载以后才能确定。 即使重复执行同一程序,加载同一动态库,库内的函数地址也是不同的。那么,程序是如何调用地址未知的函数的呢?
简短版的回答是:二进制格式、操作系统,以及加载器。在 Linux 上,这是一 支程序和动态加载器之间的曼妙舞蹈
下面是详细版的回答。
Linux 程序使用 ELF binary format,它提供了 许多特性。出于本文目的,我们这里只介绍两个:
- 过程链接表(Procedure Linkage Table,
PLT
) - 全局偏移表(Global Offset Table,
GOT
)
库函数在 PLT 里都有一组对应的汇编指令,通常称作 trampoline
,在函数被调用的时候执行。
PLT trampoline 代码
PLT trampoline 都遵循类似的格式,下面是一个例子:
PLT1: jmp *name1@GOTPCREL(%rip)
pushq $index1
jmp .PLT0
- 第一行代码跳转到一个地址,这个地址的值存储在 GOT 中。
- GOT 存储了绝对地址。这些地址在程序启动时初始化,指向 PLT
pushq
指令所在的地址(第二行代码)。 - 第三行
pushq $index1
为动态连接器准备一些数据,然后通过jmp .PLT0
跳转到另一段代码,后者会进而调用动态链接器。
动态链接器通过 $index1
和其他一些数据来判断程序想调用的是哪个库函数,然后定位 到函数地址,并将其写入 GOT,覆盖之前初始化时的默认值。
当后面再次调用到这个函数时,就会直接找到函数地址,而不需再经过以上的动态链接器查 找过程。
流程总结
总结起来:
- 程序加载到内存时,程序和每个动态共享库(例如 DSO)通过 PLT 和 GOT 映射到内存
- 程序开始执行时,动态共享库里的函数的内存地址是未知的,因为动态库可以被加载到程序地址空间的任意地址
- 首次执行到一个函数的时候,执行过程转到函数的 PLT,里面是一些汇编代码(trampoline)
- trampoline 组织数据,然后调用动态链接器
- 动态链接器通过 PLT 准备的数据找到函数地址
- 将地址写入 GOT 表,然后执行转到该函数
- 后面再次调用到这个函数时,不再经过动态链接器,因为 GOT 里已经存储了函数地址,PLT 可以直接调用
标签:ltrace,调用,函数,如何,地址,PLT,GOT,2016 From: https://www.cnblogs.com/ycjstudy/p/18104215