简单代码分析
#include <stdio.h>
void print_banner()
{
printf("Welcome to World of PLT and GOT\n");
}
int main(void)
{
print_banner();
return 0;
}
对这个文件编译,链接后形成汇编指令call**<printf函数的地址>**,但是printf函数在glibc动态库,编译和链接阶段没办法知道printf函数加载地址。唯一的办法是运行时重定位。运行时重定位无法修改代码段,只能将printf定位到数据段。编译阶段生成的call命令,通过链接器生成的额外小代码片段,获取printf函数地址。(上述代码生成print_stub)
展开编译链接过程:
编译
每个编译单元都会经历编译和链接两个阶段,编译是将.c源码翻译成汇编指令的中间文件,生成…o文件。
链接
将一个或者多个中间文件通过链接器将他们链接成一个可执行文件。
综上,链接阶段对printf_stub做链接重定位,运行时才对printf做运行重定位。
动态链接PLT GOT
动态链接需要考虑两点,需要存放外部函数的数据段和获取数据段存放函数地址的一小段额外代码。当可执行文件调用多个动态库函数,每个函数都需要这两个东西,这样每样东西就形成一个表,每个函数使用其中一项。存放函数地址的数据表,称为重局偏移表,那个额外代码段表,称为程序链接表。
假设最开始的示例代码test.c增加一个write_file函数,在该函数里面调用glibc的write实现写文件操作。根据前面讨论的PLT和GOT原理,test在运行过程中,调用方(如print_banner和write_file)是如何通过PLT和GOT穿针引线之后,最终调用到glibc的printf和write函数的?
最后,这不是真实过程,只是一个模拟。