C 跳过调用方返回序列中的函数
在函数调用序列中,例如C 跳过调用方返回序列中的函数,c,function,assembly,x86,C,Function,Assembly,X86,在函数调用序列中,例如 main() --> A() --> B()-->C(); 当被调用函数完成时,它通常返回调用函数,例如C()返回B(),后者返回a(),等等 我想知道是否也可以直接返回调用序列中较早的函数 因此C()返回main()并跳过B()和A() 如果可能的话,我怎么做?你能解释一下它是如何工作的,以及它在现实中的使用时间吗 这是我的密码 #include <stdio.h> int A(void); int B(v
main() --> A() --> B()-->C();
当被调用函数完成时,它通常返回调用函数,例如C()
返回B()
,后者返回a()
,等等
我想知道是否也可以直接返回调用序列中较早的函数
因此C()
返回main()
并跳过B()
和A()
如果可能的话,我怎么做?你能解释一下它是如何工作的,以及它在现实中的使用时间吗
这是我的密码
#include <stdio.h>
int A(void);
int B(void);
void main(void )
{
A();
}
int A()
{
printf("enter A()\n");
B();
printf("exit A()\n");
}
int B()
{
printf("enter B()\n");
printf("exit B()\n");
}
在一切结束之前,@PeterCordes不仅仅以一种方式回答了这个问题 好的,让我们开始: 这种类型的东西可以使用所谓的
跳远
来完成,因此编辑后的代码如下所示:
#include <stdio.h>
#include <setjmp.h>//c standard library header
jmp_buf env; // for saving longjmp environment
main()
{
int r, a=100;
printf("call setjmp to save environment\n");
if ((r=setjmp(env)) == 0){
A();
printf("normal return\n");
}
else
printf("back to main() via long jump, r=%d a=%d\n", r, a);
}
int A()
{
printf("enter A()\n");
B();
printf("exit A()\n");
}
int B()
{
printf("enter B()\n");
printf("long jump? (y|n) ");
if (getchar()=='y')
longjmp(env, 1234);
printf("exit B()\n");
}
#包括
#include//c标准库标题
jmp_buf env;//用于保存longjmp环境
main()
{
int r,a=100;
printf(“调用setjmp以保存环境\n”);
如果((r=setjmp(env))==0){
A();
printf(“正常返回\n”);
}
其他的
printf(“通过跳远返回main(),r=%d a=%d\n”,r,a);
}
int A()
{
printf(“输入A()\n”);
B();
printf(“退出A()\n”);
}
int B()
{
printf(“输入B()\n”);
printf(“跳远”(y | n)”;
如果(getchar()='y')
longjmp(环境署,1234);
printf(“退出B()\n”);
}
让我们了解刚才发生了什么
在上面的程序中,setjmp()
将当前执行环境保存在jmp\u buf
结构和返回0
程序继续调用A()
,这将调用B()
当
在函数B()
中,如果用户选择不通过跳远返回,则函数将
显示正常的返回顺序
如果用户选择通过longjmp(env,1234)
返回,
执行将返回上一次保存的环境,其值为非零
在
在这种情况下,它会导致B()
直接返回main()
,绕过A()
原则
跳远
非常简单。当函数完成时,它通过
(呼叫者\u EIP,呼叫者\u EBP)
在当前堆栈帧中,
如果我们将(呼叫者\u EIP,呼叫者\u EBP)
替换为
(已保存的\u EIP,已保存的\u EBP)
以前的函数
在调用序列中,执行将直接返回到该函数
另外
对于(保存的EIP,保存的EBP)
,setjmp()
还可以保存CPU的通用寄存器和
原始的ESP
,这样longjmp()
就可以恢复返回文件的完整环境
功能
跳远可用于中止调用序列中的函数,从而导致
从先前保存的已知环境恢复执行
虽然很少使用
在用户模式程序中,它是系统编程中的常用技术
比如说,,
它可用于信号捕捉器中,以绕过导致错误的用户模式功能
异常或陷阱错误
您可以检查是否也很好除非使用setjmp
/longjmp
,否则在C中无法检查。或者您可以使用B()
调用exit()
而不是返回。如果这是C++,则可以使用Test/catch和Ext来将堆栈解压缩回<代码>主()/>代码>,如果<代码>主< /代码>已编译为支持(通过使用<代码>尝试< /Calp> /Cord> catch < /Cord>),为什么要标记这个x86程序集?是否要有<代码>()
jmp直接连接到main
的末尾,不管它是从哪里调用的。我猜理论上,对于特定的平台,您可以展开堆栈并获取父级的返回地址,但对于Linux和Windows,使用.eh_frame
元数据在Linux上的工作方式会有所不同。例如,在或者简单的例子。请注意,main()
可能被编译为使用jmp A
作为尾部调用A,因为您声明了它void
。此外,您的程序具有未定义的行为:您从非void函数A()和B()的末尾掉下来。以及void main(void)
不是一个有效的函数签名是ISO C:int main(void)
是合法的。void main()
将在x86的普通编译器上运行,您只会得到一个垃圾退出状态。我使用intel pc这就是为什么x86 Tag我想我们可能在这里讨论一个XY问题,我的意思是,如果您后退一步,并描述通过跳过部分返回路径来实现的目标,您可能会得到更好的帮助。您想使用这种机制做什么ism的用途?如果您描述了这一点,它可能以不同的方式实现,更容易理解和/或更广泛地使用(这意味着您将学到一些更好地用于其他问题的东西)。我会尝试一下,让您知道结果。-fomit frame pointer
在默认情况下处于打开状态,因此longjmp
不能假设存在保存的EBP/返回地址的链接列表。我认为longjmp
根本不必实际展开堆栈,它只需跳转到jmp\u buf
中保存的地址,然后还原ESP
e> /RSP
和来自jmp_buf
的其他保留调用的寄存器。因此,展开堆栈寻找第一个可以处理它的catch块并不是一个异常。longjmp
有一个特定的jmp_buf
作为其目标。请参阅如何实现它。当然,您描述setjump/l的方式是错误的ongjump工作。setjum保存非易失性寄存器(以及可能的特定于操作系统的数据,如windows x86上的SEH帧)和longjump还原上下文(以及可能在x86 windows上执行某些特定于操作系统的,RtlUnwind
)
#include <stdio.h>
#include <setjmp.h>//c standard library header
jmp_buf env; // for saving longjmp environment
main()
{
int r, a=100;
printf("call setjmp to save environment\n");
if ((r=setjmp(env)) == 0){
A();
printf("normal return\n");
}
else
printf("back to main() via long jump, r=%d a=%d\n", r, a);
}
int A()
{
printf("enter A()\n");
B();
printf("exit A()\n");
}
int B()
{
printf("enter B()\n");
printf("long jump? (y|n) ");
if (getchar()=='y')
longjmp(env, 1234);
printf("exit B()\n");
}