Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/58.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_Gcc_Assembly_X86 - Fatal编程技术网

C 以前的堆栈变量

C 以前的堆栈变量,c,gcc,assembly,x86,C,Gcc,Assembly,X86,我有这个问题,我递归调用C中的一个函数,C是词汇范围的,所以我只能访问当前堆栈帧。我想从上一个堆栈帧中提取参数和局部变量,该堆栈帧是在当前堆栈帧上im时在上一个函数调用下创建的 我知道上一个递归调用的值仍然在堆栈上,但我无法访问这些值,因为它们“隐藏”在活动堆栈框架下 我想从上一个堆栈中提取参数和局部变量,并将它们复制到copy_of_burned_arg和copy_of_burned_loc 这是一个要求,使用内联汇编使用气体提取变量,这是我迄今为止,我尝试了一整天,我似乎无法理解,我画了纸上

我有这个问题,我递归调用C中的一个函数,C是词汇范围的,所以我只能访问当前堆栈帧。我想从上一个堆栈帧中提取参数和局部变量,该堆栈帧是在当前堆栈帧上im时在上一个函数调用下创建的

我知道上一个递归调用的值仍然在堆栈上,但我无法访问这些值,因为它们“隐藏”在活动堆栈框架下

我想从上一个堆栈中提取参数和局部变量,并将它们复制到copy_of_burned_arg和copy_of_burned_loc

这是一个要求,使用内联汇编使用气体提取变量,这是我迄今为止,我尝试了一整天,我似乎无法理解,我画了纸上的堆栈,并做了计算,但没有工作,我还尝试删除调用printf,使堆栈将更干净,但我无法找出正确的算法。这是到目前为止的代码,我的函数在第二次迭代时停止

#include <stdio.h>

char glo = 97;   // just for fun 97 is ascii lowercase 'a'
int copy_of_buried_arg;
char copy_of_buried_loc;

void rec(int arg) {
  char loc;

  loc = glo + arg * 2; // just for fun, some char arithmetic
  printf("inside rec() arg=%d loc='%c'\n", arg, loc);

  if (arg != 0) {
    // after this assembly code runs, the copy_of_buried_arg and
    // copy_of_buried_loc variables will have arg, loc values from
    // the frame of the previous call to rec().
    __asm__("\n\
            movl 28(%esp), %eax #moving stack pointer to old ebp (pointing it to old ebp)\n\
            addl $8, %eax       #now eax points to the first argument for the old ebp \n\
            movl (%eax), %ecx   #copy the value inside eax to ecx\n\ 
            movl %ecx, copy_of_buried_arg   # copies the old argument\n\
    \n\

");

    printf("copy_of_buried_arg=%u copy_of_buried_loc='%c'\n",
       copy_of_buried_arg, copy_of_buried_loc);
  } else {
      printf("there is no buried stack frame\n");// runs if argument = 0 so only the first time
  }

  if (arg < 10) {
    rec(arg + 1);
  }
}

int main (int argc, char **argv) {
  rec(0);

  return 0;
}
#包括
char glo=97;//只是为了好玩97是ascii小写字母“a”
嵌入参数的整数副本;
已埋的字符副本;
无效记录(内部参数){
char-loc;
loc=glo+arg*2;//只是为了好玩,一些字符算法
printf(“内部rec()arg=%d loc='%c'\n',arg,loc”);
如果(arg!=0){
//运行此程序集代码后,_的副本_将_arg和
//_-loc变量的副本_将具有来自的arg、loc值
//上一次调用rec()的帧。
__asm\uuuu(“\n\
movl 28(%esp),%eax#将堆栈指针移动到旧ebp(将其指向旧ebp)\n\
addl$8,%eax#现在eax指向旧ebp的第一个参数\n\
movl(%eax),%ecx#将eax内的值复制到ecx\n\
movl%ecx,复制_参数的_#复制旧参数\n\
\n\
");
printf(“copy_of_burned_arg=%u copy_of_burned_loc='%c'\n”,
埋置参数的副本,埋置位置的副本);
}否则{
printf(“没有隐藏的堆栈帧”);//在参数=0时运行,因此仅第一次运行
}
如果(arg<10){
rec(arg+1);
}
}
int main(int argc,字符**argv){
rec(0);
返回0;
}

我可以尝试提供帮助,但没有Linux或GAS中的汇编。但计算结果应该类似:

这是经过几次调用后的堆栈。典型的堆栈帧设置会创建堆栈帧的链接列表,其中EBP是当前堆栈帧,并指向上一个堆栈帧的旧值

      +-------+
ESP-> |loc='c'|     <- ESP currently points here.
      +-------+
EBP-> |oldEBP |--+  <- rec(0)'s call frame
      +-------+  |
      |retaddr|  |  <- return value of rec(1)
      +-------+  |
      |arg=1  |  |  <- pushed argument of rec(1)
      +-------+  |
      |loc='a'|  |  <- local variable of rec(0)
      +-------+  |
   +--|oldEBP |<-+  <- main's call frame
   |  +-------+
   |  |retaddr|     <- return value of rec(0)
   |  +-------+ 
   |  |arg=0  |     <- pushed argument of rec(0)
   |  +-------+
  \|/ 
to main's call frame
或者我对GAS的最佳猜测(我不知道,但知道它是向后到MASM的):

在Windows上使用VS2010使用my MASM输出代码:

inside rec() arg=0 loc='a'
there is no buried stack frame
inside rec() arg=1 loc='c'
copy_of_buried_arg=0 copy_of_buried_loc='a'
inside rec() arg=2 loc='e'
copy_of_buried_arg=1 copy_of_buried_loc='c'
inside rec() arg=3 loc='g'
copy_of_buried_arg=2 copy_of_buried_loc='e'
inside rec() arg=4 loc='i'
copy_of_buried_arg=3 copy_of_buried_loc='g'
inside rec() arg=5 loc='k'
copy_of_buried_arg=4 copy_of_buried_loc='i'
inside rec() arg=6 loc='m'
copy_of_buried_arg=5 copy_of_buried_loc='k'
inside rec() arg=7 loc='o'
copy_of_buried_arg=6 copy_of_buried_loc='m'
inside rec() arg=8 loc='q'
copy_of_buried_arg=7 copy_of_buried_loc='o'
inside rec() arg=9 loc='s'
copy_of_buried_arg=8 copy_of_buried_loc='q'
inside rec() arg=10 loc='u'
copy_of_buried_arg=9 copy_of_buried_loc='s'
在我的编译器(gcc 3.3.4)中,我最终得出以下结论:

#include <stdio.h>

char glo = 97;   // just for fun 97 is ascii lowercase 'a'
int copy_of_buried_arg;
char copy_of_buried_loc;

void rec(int arg) {
  char loc;

  loc = glo + arg * 2; // just for fun, some char arithmetic
  printf("inside rec() arg=%d loc='%c'\n", arg, loc);

  if (arg != 0) {
    // after this assembly code runs, the copy_of_buried_arg and
    // copy_of_buried_loc variables will have arg, loc values from
    // the frame of the previous call to rec().
    __asm__ __volatile__ (
            "movl 40(%%ebp), %%eax #\n"
            "movl %%eax, %0 #\n"
            "movb 31(%%ebp), %%al #\n"
            "movb %%al, %1 #\n"
            : "=m" (copy_of_buried_arg), "=m" (copy_of_buried_loc)
            :
            : "eax"
    );

    printf("copy_of_buried_arg=%u copy_of_buried_loc='%c'\n",
       copy_of_buried_arg, copy_of_buried_loc);
  } else {
      printf("there is no buried stack frame\n");// runs if argument = 0 so only the first time
  }

  if (arg < 10) {
    rec(arg + 1);
  }
}

int main (int argc, char **argv) {
  rec(0);

  return 0;
}
ebp
(40和31)开始的偏移量最初设置为任意猜测值(例如0),然后通过观察拆卸和一些简单计算进行细化

请注意,当函数递归调用自身时,它使用额外的12+4=16字节堆栈进行对齐和参数调用:

        subl    $12, %esp
        movl    8(%ebp), %eax
        incl    %eax
        pushl   %eax
        call    _rec
        addl    $16, %esp
还有4个字节的返回地址

然后,函数对旧的
ebp
及其局部变量使用4+8=12字节:

_rec:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
因此,每次递归调用,堆栈总的增长量为16+4+12=32字节

现在,我们知道如何通过
ebp
获取本地
arg
loc

        movl    8(%ebp), %eax ; <- arg
        addl    %eax, %eax
        addb    _glo, %al
        movb    %al, -1(%ebp) ; <- loc

movl8(%ebp),%eax;您不能将上一次调用的局部变量的地址传递给下一个函数吗?在这里,混用汇编语言可能不会带来任何好处。在调试器中运行它并从中获取数字如何?@GregHewgill我们应该使用汇编从上一个堆栈中挖掘变量,这是一项要求,我看到了。在问题中指出这类事情会很有用。@Alex我该怎么做呢。事实上,我也有这个问题,是否有一个程序以图形方式显示堆栈?我猜想任何类型的堆栈保护都会使这种方法完全不可移植。@tbert:堆栈保护与此有什么关系?OP的代码是不可移植的,因为当使用不同的优化选项或不同版本的编译器进行编译时,它会崩溃。我很轻,我只是做了一个噩梦,人们对此提出了大量问题,他们认为将信息传递给函数的进一步递归迭代是一种可行的技术。如果讲师不确定有一长串的警告与此一起出现,那么我会确保互联网上的某个地方有人说“警告清空”。请注意,至少x86-64参数实际到达寄存器(大部分)中,而不是堆栈中,所以这是不可能的。@perh问题没有提到64位。此外,如果寄存器不足以保存其中的所有内容,那么堆栈上将不可避免地存储一些内容。
        subl    $12, %esp
        movl    8(%ebp), %eax
        incl    %eax
        pushl   %eax
        call    _rec
        addl    $16, %esp
_rec:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
        movl    8(%ebp), %eax ; <- arg
        addl    %eax, %eax
        addb    _glo, %al
        movb    %al, -1(%ebp) ; <- loc