如何在C语言中让sec函数直接返回至main?禁用汇编、exit与goto
问题需求
给定一段C代码,要求仅修改sec()函数(禁止使用汇编代码、exit(0)和goto),让sec执行后直接返回main函数,跳过first函数中sec()调用后的printf("dont print this");语句,最终输出如下内容:
print start main print this from first print this from sec print exit main
原代码如下:
void sec() { /*change only here */ printf("print this from sec"); } void first() { printf("print this from first"); sec(); printf("dont print this"); } int main() { printf("print start main"); first(); printf("print exit main\n"); return 0; }
栈帧工作机制与调试方法
栈帧基本原理
函数调用时,CPU会为每个函数创建独立栈帧:
- 调用函数前,将返回地址(当前函数调用结束后要执行的下一条指令地址)压入栈中
- 进入被调用函数后,会把当前栈基址寄存器
rbp的值压栈,再将栈指针rsp的值赋值给rbp,作为新栈帧的基址 - 栈帧结构(x86_64架构,栈向下生长):
[高地址] ... 调用方函数的局部变量 ... 调用方的rbp值 → 当前函数rbp指向的位置 当前函数的返回地址 → rbp + 8 当前函数的局部变量 → rbp - n [低地址]
本题的调用链是main() → first() → sec(),sec的返回地址指向first中sec()之后的代码,first的返回地址指向main中first()之后的代码。我们需要让sec返回时直接跳转到first的返回地址,从而回到main。
GDB调试找rbp与返回地址
- 编译代码时添加调试信息:
gcc -g test.c -o test - 启动GDB并运行程序:
gdb ./test break sec # 在sec函数入口设置断点 run # 运行程序 - 查看栈帧与rbp:
bt:查看完整调用栈,能看到sec、first、main的层级关系print $rbp:直接打印当前sec函数的栈基址rbp的值info frame:查看当前栈帧的详细信息,包括保存的rbp地址、返回地址等frame 1:切换到first函数的栈帧,再用info frame可查看first的返回地址(也就是我们需要让sec返回的目标地址)
修改sec函数实现需求
在sec函数中,通过操作栈帧的返回地址,让它直接指向first的返回地址,代码如下:
void sec() { printf("print this from sec"); // 获取当前栈帧的rbp(内联汇编仅用于获取寄存器值,未编写独立汇编逻辑) unsigned long long *rbp; __asm__ __volatile__("mov %%rbp, %0" : "=r"(rbp)); // 获取first函数的返回地址:first的rbp是当前rbp[0],它的返回地址在first_rbp+8的位置 unsigned long long target_addr = ((unsigned long long*)rbp[0])[1]; // 将当前sec函数的返回地址替换为目标地址 rbp[1] = target_addr; }
代码解释
rbp[0]:当前sec栈帧中保存的first函数的rbp值((unsigned long long*)rbp[0])[1]:first栈帧中的返回地址,也就是main函数中first()调用结束后要执行的指令地址rbp[1]:当前sec函数的返回地址,将它替换为target_addr后,sec执行完毕返回时,会直接跳转到main的对应位置,跳过first中剩余的代码
内容的提问来源于stack exchange,提问作者Tzur Mantzur
相关产品推荐
相关产品推荐

