Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/62.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 跳过调用方返回序列中的函数_C_Function_Assembly_X86 - Fatal编程技术网

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