C语言函数调用栈语言函数调用栈(三三)
6 调用栈实例分析
本节通过代码实例分析函数调用过程中栈帧的布局、形成和消亡。
6.1 栈帧的布局
示例代码如下:
//StackReg.c
#include
//获取函数运行时寄存器%ebp和%esp的值
#define FETCH_SREG(_ebp, _esp) do{\
asm volatile( \
"movl %%ebp, %0 " \
"movl %%esp, %1 " \
: "=r" (_ebp), "=r" (_esp) \
); \
}while(0)
//也可使用gcc扩展register void *pvEbp __asm__ ("%ebp"); register void *pvEsp __asm__ ("%esp");获取,
// pvEbp和pvEsp指针变量的值就是FETCH_SREG(_ebp, _esp)中_ebp和_esp的值
#define PRINT_ADDR(x) printf("[%s]: &"#x" = %p", __FUNCTION__, &x)
#define PRINT_SREG(_ebp, _esp) do{\
printf("[%s]: EBP = 0x%08x", __FUNCTION__, _ebp); \
printf("[%s]: ESP = 0x%08x", __FUNCTION__, _esp); \
printf("[%s]: (EBP) = 0x%08x", __FUNCTION__, *(int *)_ebp); \
printf("[%s]: (EIP) = 0x%08x", __FUNCTION__, *((int *)_ebp + 1)); \
printf("[%s]: &"#_esp" = %p", __FUNCTION__, &_esp); \
printf("[%s]: &"#_ebp" = %p", __FUNCTION__, &_ebp); \
}while(0)
void tail(int paraTail){
int locTail = 0;
int ebpReg, espReg;
FETCH_SREG(ebpReg, espReg);
PRINT_SREG(ebpReg, espReg);
PRINT_ADDR(paraTail);
PRINT_ADDR(locTail);
}
int middle(int paraMid1, int paraMid2, int paraMid3){
int ebpReg, espReg;
tail(paraMid1);
FETCH_SREG(ebpReg, espReg);
PRINT_SREG(ebpReg, espReg);
PRINT_ADDR(paraMid1);
PRINT_ADDR(paraMid2);
PRINT_ADDR(paraMid3);
return 1;
}
int main(void){
int ebpReg, espReg;
int locMain = middle(1, 2, 3);
FETCH_SREG(ebpReg, espReg);
PRINT_SREG(ebpReg, espReg);
PRINT_ADDR(locMain);
return 0;
}
StackReg
该程序每个函数都嵌入汇编代码,以获取各函数运行时刻EBP和ESP寄存器的值。每个函数都打印出EBP寄存器所指
向内存地址处的值,以及位于其后的函数返回地址。图7给出程序的编译和运行结果。