LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 1058|回复: 12

setjmp&longjmp的问题

[复制链接]
发表于 2005-6-27 21:42:24 | 显示全部楼层 |阅读模式
碰到一个麻烦的问题, 想要用setjmp/longjmp解决,问题是这两个函数不会保存堆栈,
只是保存修改寄存器,因此在从里向外longjmp是安全的,从外向里longjmp则局部变量
会出问题。

下面是一个示例代码,从main跳到f1,再跳回main,在这一跳中必须要先保存
堆栈否则f1的局部变量就会丢失,再跳到f1,再返回到main。

想要实现的效果是: 在第二次main跳到f1之后,对于f1来说,似乎就是从switch(setjmp...)
返回1开始执行, 但是下面的代码有问题,总是f1的局部变量b,c被setjmp的返回值和参数地址
所覆盖。

代码只能用gcc编译,因为使用的jmp_buf实现是gcc特有的, 我用的gcc3.3.5,setjmp/
longjmp的实现看了看glibc的i386/setjmp/longjmp代码,大概知道怎么回事。

另外restorestack是有问题的,可能缺页或者访问非法内存,不过我运行的时候倒没
出现这些问题。


  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <setjmp.h>

  4. /*
  5. # define JB_BX        0
  6. # define JB_SI        1
  7. # define JB_DI        2
  8. # define JB_BP        3
  9. # define JB_SP        4
  10. # define JB_PC        5
  11. */

  12. #define JMPBUF(buf, idx) (((int*)buf)[idx])
  13. #define BX(buf) JMPBUF(buf, 0)
  14. #define SI(buf) JMPBUF(buf, 1)
  15. #define DI(buf) JMPBUF(buf, 2)
  16. #define BP(buf) JMPBUF(buf, 3)
  17. #define SP(buf) JMPBUF(buf, 4)
  18. #define PC(buf) JMPBUF(buf, 5)

  19. #define savestack() if (NULL != sp1 && NULL != sp2) { \
  20.         for (i=0; i < sp1 - sp2; ++i) { \
  21.                 stack[i] = *(sp2 + i);  \
  22.         } \
  23.         } else abort();

  24. #define restorestack() if (NULL != sp1 && NULL != sp2) { \
  25.         for (i=0; i < sp1 - sp2; ++i) { \
  26.                 *(sp2 + i) = stack[i];  \
  27.         } \
  28.         } else abort();

  29. jmp_buf buf1;
  30. jmp_buf buf2;
  31. int j1 = 1;
  32. int j2 = 1;
  33. int i = 0;
  34. int* sp1 = NULL;
  35. int* sp2 = NULL;
  36. int stack[1024];

  37. void output(jmp_buf buf) {
  38.         int* p = (int*)buf;
  39.         int  i = 0;
  40.         printf("BX=%u, SI=%u, DI=%u, BP=%u, SP=%u, PC=%u\n",
  41.                         p[0], p[1], p[2], p[3], p[4], p[5]);
  42.         printf("stack from SP: ");
  43.         for (i = 0; i < 30; ++i) {
  44.                 printf("%u ", *(int*)(p[4] + i));
  45.         }
  46.         printf("\n&output()=%u, p =%u, buf=%u\n", output, p, (int*)buf);
  47. }

  48. void f1() {
  49.         int a = 6;
  50.         int b = 7;
  51.         int c = 8;
  52.         printf("f1: enter: \t\ta=%x, b=%x, c=%x\n", a, b, c);
  53.         switch (setjmp(buf2)) {
  54.                 case 0:
  55.                         sp2 = (int*)SP(buf2);
  56.                         printf("f1: case 0: \t\ta=%x, b=%x, c=%x\n", a, b, c);
  57.                         savestack();
  58.                         if (j1) longjmp(buf1, 1);
  59.                         printf("f1: case 0: after longjump\n");
  60.                         break;
  61.                 case 1:
  62.                         printf("f1: case 1: jump from main\n");
  63.                         printf("f1: \t\ta=%x, b=%x, c=%x\n", a, b, c);
  64.                         printf("&buf1=%x, &buf2=%x, &a=%x, &b=%x, c=%x\n", &buf1, &buf2,
  65.                                         &a, &b, &c);
  66.                         break;
  67.         }
  68.         printf("f1: leave: \t\ta=%x, b=%x, c=%x\n", a, b, c);
  69. }
  70. int main(int argc, char** argv) {
  71.         int x = 3;
  72.         int y = 4;
  73.         int z = 5;
  74.         (void)argc;
  75.         (void)argv;
  76.         printf("main: enter: \t\tx=%x, y=%x, z=%x\n", x, y, z);
  77.         switch (setjmp(buf1)) {
  78.                 case 0:
  79.                         sp1 = (int*)SP(buf1);
  80.                         printf("main: case 0: call f1(): \t\tx=%x, y=%x, z=%x\n", x, y, z);
  81.                         f1();
  82.                         printf("main: case 0: back from f1()\n");
  83.                         break;
  84.                 case 1:
  85.                         printf("main: case 1\n");
  86.                         {
  87.                                 int aa[512];
  88.                                 int i;
  89.                                 for (i=0; i < 512; ++i) {
  90.                                         aa[i] = 512 - i;
  91.                                 }
  92.                         }
  93.                         printf("main: case 1: jump to f1(): \t\tx=%x, y=%x, z=%x\n", x, y, z);
  94.                         restorestack();
  95.                         if (j2) longjmp(buf2, 1);
  96.                         printf("main: case 1: back from f1()\n");
  97.                         break;
  98.         }
  99.         printf("main: leave: \t\tx=%x, y=%x, z=%x\n", x, y, z);
  100.         return 0;
  101. }
复制代码
 楼主| 发表于 2005-6-27 21:52:32 | 显示全部楼层
事情是这样的:
func1() ->func2()->func3() ->func4() ->.... ->funcN();

funcN()有可能引起阻塞,会返回WDBLOCK,这样func4, func3等一层一层返回这个
WDBLOCK就可以退出调用,但是中间这些代码是已经写好的,改接口很麻烦,而且每个函数
都得有一个int stage变量记录运行到哪一步了,再switch goto过去,造成
维护非常的不方便, 所以才想到用setjmp/longjmp的办法。

系统必须是单线程的(其实是一个运行于一个OS进程/线程里的一个JVM)。

上面这个解决办法还要考虑Java线程中止后这些未运行结束的函数中分配的
资源比如内存/文件等等的释放,头大。。。要将代码修改量降到最少。。。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-6-27 21:54:14 | 显示全部楼层
各位对汇编熟悉的朋友帮帮忙,看看那个程序是什么原因出错,该怎么改,谢谢!
回复 支持 反对

使用道具 举报

发表于 2005-6-28 20:46:24 | 显示全部楼层
>>总是f1的局部变量b,c被setjmp的返回值和参数地址所覆盖。

我试过了,把a,b,c声明成static的静态变量,可以使它们的值不受影响。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-6-28 20:59:24 | 显示全部楼层
那是当然,static变量不保存在堆栈里头,应该在程序的数据段里,相当于一个只有这个函数可见(编译
器搞的鬼)的全局变量。

我这个地方必须要用局部变量,因为这些函数不是我写的, 我不能把每个变量改成static.
回复 支持 反对

使用道具 举报

发表于 2005-6-28 21:08:49 | 显示全部楼层
既然如此,关键在于你如何去恢复stack。我不认为你这种恢复stack的办法是可靠的。但我所学有限,也想不出什么好办法。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-6-28 21:22:28 | 显示全部楼层
怎么删除帖子?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-6-28 21:24:06 | 显示全部楼层
恢复堆栈应该用push安全些,直接mov不安全。

我做这个东西也是无奈之举,系统比较特殊,限定了只能一个OS进程/线程,jvm
中所有本地方法都是共用的一个堆栈,而且本地方法的调用不会半途中断(就是说
从Java bytecode->kni->native c function, 然后必须返回到java bytecode
中去),  所以不会有两个Java线程程同时访问堆栈的情况,这种特殊情况下拷贝堆栈应该是安全的,
也是解决我碰到的问题的一个省事的办法.
目前感觉思路还是对的,无奈汇编功底差了,调试了好久都没搞定。

多谢回复!
回复 支持 反对

使用道具 举报

发表于 2005-7-1 22:26:25 | 显示全部楼层
Post by dieken_qfz

#define savestack() if (NULL != sp1 && NULL != sp2) { \
        for (i=0; i < sp1 - sp2; ++i) { \
                stack = *(sp2 + i);  \
        } \
        } else abort();

#define restorestack() if (NULL != sp1 && NULL != sp2) { \
        for (i=0; i < sp1 - sp2; ++i) { \
                *(sp2 + i) = stack;  \
        } \
        } else abort();

这里, i 太小了, 把 i<sp1-sp2 改为 i < sp1-sp2+20 差不多够了

Post by dieken_qfz

void f1() {
        int a = 6;
        int b = 7;
        int c = 8;
        printf("f1: enter: \t\ta=%x, b=%x, c=%x\n", a, b, c);
        switch (setjmp(buf2)) {
                case 0:
                        sp2 = (int*)SP(buf2);
                        printf("f1: case 0: \t\ta=%x, b=%x, c=%x\n", a, b, c);
                        savestack();
                        if (j1) longjmp(buf1, 1);
                        printf("f1: case 0: after longjump\n");
                        break;
                case 1:
                        printf("f1: case 1: jump from main\n");
                        printf("f1: \t\ta=%x, b=%x, c=%x\n", a, b, c);
                        printf("&buf1=%x, &buf2=%x, &a=%x, &b=%x, c=%x\n", &buf1, &buf2,
                                        &a, &b, &c);
                        break;
        }
        printf("f1: leave: \t\ta=%x, b=%x, c=%x\n", a, b, c);
}

restorestack() 从main函数中移置case 1 的后面

该完这两个地方,试试看,看还有什么问题
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-7-3 12:45:57 | 显示全部楼层
试过了, 我多拷贝了30个整型都不对。
Post by rickxbx
这里, i 太小了, 把 i<sp1-sp2 改为 i < sp1-sp2+20 差不多够了


restorestack() 从main函数中移置case 1 的后面

该完这两个地方,试试看,看还有什么问题


不能移到case 1的后面,如果如此,后面的for循环会修改堆栈,就白restore了。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表