在没有内置函数或程序集的情况下在C中实现setjmp和longjmp(获取不正确的返回值)

adv*_*oge 5 c stack pointers longjmp setjmp

我正在尝试测试我的两个函数,它们模仿setjmp和longjmp用于作业 - 这是非常困难的,因为我们不允许使用内置函数或汇编asm()来实现longjmp和setjmp函数.(是的,那真的是作业.)

问题:我一直收到错误的返回值.所以,简而言之,当main()调用foo()和foo()调用bar(),而bar()调用longjump()时,bar()不应该返回foo(),而是setjmp()应该返回main的返回值为1,应打印"error"(参见下面的main()).

相反,我的输出结果如下:

start foo
start bar
segmentation fault
Run Code Online (Sandbox Code Playgroud)

分段错误,我尝试通过使用malloc初始化指针*p来修复,但似乎没有做任何事情.虽然,分段错误,是我没有得到正确的返回值的原因?

码:

#include <stdio.h>
#include <stdlib.h>

int setjmp(int v);
int longjmp(int v);
int foo(void);
int bar(void);
int *add;


int main(void) {

    int r;

    r = setjmp(r);
    if(r == 0) {
        foo();
        return(0);
    } else {
        printf("error\n");
        return(2);
    }

}

int _main(void) {
    return(0);
}

int setjmp(int v)
{
    add = &v;
    return(0);
}

int longjmp(int v)
{
    int *p;
    p = &v;
    *(p - 1) = *add;
    return(1);
}

int foo(void) {
    printf("start foo\n");
    bar();
    return(0);
}

int bar(void) {
    int d;
    printf("start bar\n");
    longjmp(d);
    return(0);
}
Run Code Online (Sandbox Code Playgroud)

小智 5

实现setjmp()longjmp()需要访问堆栈指针。不幸的是,您正在处理的分配明确禁止您使用所有合理的方法来执行此操作(即,使用汇编或使用编译器内置函数来访问堆栈指针)。

更糟糕的是,他们修改了示例代码中setjmp()和的定义longjmp()。参数需要是解析为数组的类型(例如,typedef int jmp_buf[1]),而不是int……

无论如何。您需要某种方法来可靠地从 C 中的堆栈帧中找到旧的堆栈指针。可能最好的方法是在堆栈上定义一个数组,然后查看它的“后面”......

void get_sp(void) {
    int x[1];
    sp = x[-1]; // or -2 or -3, etc…
Run Code Online (Sandbox Code Playgroud)

确切的偏移量将取决于您使用的编译器,以及您的函数采用的参数以及函数具有的其他局部变量。您需要进行一些实验才能做到这一点。在模拟器中运行您的应用程序,和/或查看生成的程序集,以确保您选择了正确的值。

同样的技巧可能会在从longjmp(). 但是,某些编译器优化可能会使这变得困难,尤其是在具有链接寄存器的体系结构上——例如 MIPS。确保禁用编译器优化。如果所有其他方法都失败了,您可能需要调用一个虚拟函数longjmp()来强制编译器将链接寄存器保存在堆栈中,而不是将它留在寄存器中(它不能被覆盖)。