在c程序的飞行中更改堆栈

时间:2013-07-23 20:47:25

标签: c stack heap-memory

我正在编写以下代码,可以更改函数调用的堆栈。但它总是在printf上遇到段错误。我用汇编调试代码,堆栈成功切换。是printf语句创建了段错误而不确定原因。任何人都有任何线索我应该考虑更多的方向?

感谢。

char stack[4000000*4]; 

void foo(int ad) {
    int i = 100;
    int sum = i*i + ad;
    printf("stack changed to %X\n", stack);
}

/* in this example, foo (and its decendents) live on a new stack */
void change_stack(void *newstack) {
    void *ctx[5]; // Jump buffer for setjmp/longjmp.
    if (0 == __builtin_longjmp(ctx)) {
        ctx[2] = newstack; // switch stack  
        __builtin_longjmp(ctx, 1);/* here stack is switched */
    } else {
    /* now live on new stack, can we pass parameters now ? */
    int ad = 20;
    foo(ad);
    }
}

int main (int argc, char** argv)
{
  int i = 10;
  change_stack(stack);
  printf("return, %d\n", i);
  return 0;
}

2 个答案:

答案 0 :(得分:2)

切换堆栈而不复制旧堆栈的内容。当change_stack返回时,结果是未定义的(例如,它可能会跳转到地址NULL,从而导致段错误)。此外,局部变量之类的东西也是未定义的。

另外,(假设我们在这里谈论x86),堆栈指针在推送时递减。由于您分配的新堆栈指针是stack数组的基址(即最低)地址,因此任何推送都会将指针减少到此数组之外,也可能导致段错误。

答案 1 :(得分:1)

对于仍然想使用堆栈并在 printf 中遇到 SIGSEGV 的任何人,这里有一个重要的点:

您可以在printf下找到__GI___tcgetattr/path-to-glibc/sysdeps/unix/sysv/linux/tcgetattr.c的子程序之一。其中有一条指令:

movdqa (%rsp),%xmm0

并且根据 this 答案,它从源地址读取 16 字节对齐内存。所以在切换栈的时候,%rsp的地址要保持至少16字节地址对齐,否则会得到SIGSEGV。