一、<__x86.get_pc_thunk.ax>
在把C语言反汇编时,经常会看到这样的代码<__x86.get_pc_thunk.ax>
。
比如下面一段C语言代码:
// main.c
int main(void)
{
return 0;
}
然后,生成32位的可执行文件(32位的可执行文件比较简单,容易分析):
gcc main.c -o main -m32
然后反汇编,我这里使用gdb反汇编(也可以使用objdump等其他工具):
可以看到main函数的反汇编结果。其他指令都可以理解,这里有一个call 0x11c5 <__x86.get_pc_thunk.ax>
是什么意思?
二、<__x86.get_pc_thunk.ax>
的内容
乍一看<__x86.get_pc_thunk.ax>
后面是ax,可能跟ax寄存器有关,确实,这种类型的还有比如<__x86.get_pc_thunk.bx>
、<__x86.get_pc_thunk.cx>
等格式都差不多的符号。确实跟结尾的寄存器有关。
查了网上资料,这样的一条指令,就类似下面的操作:
__get_pc_thunk.ax:
movel (%esp),%eax
return
就是把esp的内容赋值给eax。
内容上,很容易理解,可是为什么要这样干呢?
三、<__x86.get_pc_thunk.ax>
的意义
这里要注意的是,get_pc_thunk.xx
是一个子程序,是被调用的,程序调用时,会把调用者的下一条指令的地址压栈保存(用于返回),所以进入get_pc_thunk.xx
时,栈顶esp就是调用者的下一条指令地址。
比如,还是下面的例子:
这里是Intel汇编
call 0x11c5 <__x86.get_pc_thunk.ax>
执行时,会把该指令的下一条指令的地址压栈保存- 也就是把指令
add eax, 0x2e23
的地址0x000011b9
压入栈中
所以当进入子程序0x11c5 <__x86.get_pc_thunk.ax>
时,主要是执行:
movel (%esp),%eax
这里是AT&T风格汇编,注意区分
- 就把指令
add eax, 0x2e23
的地址0x000011b9
赋值给了eax。
当调用者call 0x11c5 <__x86.get_pc_thunk.ax>
执行完毕返回时,将要执行的是:
add eax,0x2e23
这里eax原始值就是该条指令本身的地址(在<__x86.get_pc_thunk.ax>
中给eax赋值的)!然后加上0x2e23,就是在本条指令偏移0x2e23地址。
所以call 0x11c5 <__x86.get_pc_thunk.ax>
的意义就是为了获取下一条指令的地址。
利用了调用子程序会将下一条指令地址压栈的特性,提前获取下一条指令的地址。