在调用者的返回序列中跳过功能

时间:2018-08-19 04:54:03

标签: c function assembly x86

在一系列函数调用中,例如

                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");
}

我想跳过从A()返回到函数B()的过程,以使printf("exit A()\n");无法执行,并给我以下结果:

enter A()                                                                                                                  
enter B()                                                                                                                  
exit B() 

1 个答案:

答案 0 :(得分:3)

在检查@PeterCordes之前,一切都将以多种方式回答问题

好的,让我们开始吧:

可以使用称为long jump的东西来完成此类工作,因此编辑后的代码将如下所示:

#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");
}

让我们了解刚刚发生的事情

在上述程序中,setjmp()将当前执行环境保存在jmp_buf中 结构和returns 0

程序继续调用A(),后者调用B()

而 在函数B()中,如果用户选择不返回long jump,则函数将 显示正常的返回顺序。

如果用户选择按longjmp(env,1234)返回, 执行将返回到带有nonzero值的最后保存的环境。

在 在这种情况下,它会导致B()绕过main()直接返回A()

原理 long jump非常简单。函数完成后,将由

返回

(caller_EIP, caller_EBP) 在当前堆栈帧中, 如果将(caller_EIP, caller_EBP)替换为

较早功能的

(saved_EIP, saved_EBP) 按照调用顺序,执行将直接返回到该函数。

另外 到(saved_EIP, saved_EBP)setjmp()可能还会保存CPU的通用寄存器,并且 原始的ESP,以便longjmp()可以还原返回的完整环境 功能。

跳远可用于中止调用序列中的函数,从而导致 从先前保存的已知环境中恢复执行。

虽然很少使用 在用户模式程序中,这是系统编程中的常见技术。

例如, 它可能会在信号捕获器中用于绕过导致 异常或陷阱错误。

您可以检查this也很好