c - 先前的堆栈变量

标签 c gcc assembly x86

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

我知道之前递归调用的值仍在堆栈中,但我无法访问这些值,因为它们“埋”在事件堆栈框架下?

我想从之前的栈中取出参数和局部变量,复制到copy_of_buried_arg和copy_of_buried_loc;

使用内联汇编使用GAS提取变量是一个要求,这是我目前所拥有的,我尝试了一整天,我似乎无法弄清楚,我在纸上画了堆栈并进行了计算但没有任何效果,我也尝试删除对 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;
}

最佳答案

我可以尝试提供帮助,但 GAS 中没有 Linux 或程序集。但是计算应该是相似的:

这是几次调用后的堆栈。典型的堆栈帧设置会创建堆栈帧的链接列表,其中 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

这是按以下顺序创建的:

  1. 先推送参数 last arg。
  2. 调用函数,推送返回地址。
  3. 推送即将成为旧的 EBP,保留之前的栈帧。
  4. 将 ESP(栈顶,包含旧的 EBP)移动到 EBP,创建新的栈帧。
  5. 减去局部变量的空间。

这对 32 位堆栈有影响,EBP+8 将始终是调用的第一个参数,EBP+12 是第二个参数,等等。 EBP-n 始终是局部变量的偏移量。

获取前一个locarg的代码是(在MASM中):

mov ecx,[ebp]              // get previous stack frame
mov edx,[ecx]+8            // get first argument
mov copy_of_buried_arg,edx // save it
mov dl,[ecx]-1             // get first char-sized local variable.
mov copy_of_buried_loc,dl  // save it

或者我对 GAS 的最佳猜测(我不知道,但知道它是倒退到 MASM):

movl (%ebp),ecx
movl 8(%ecx),edx
movl edx,copy_of_buried_arg
movb -1(%ecx),dl
movb dl,copy_of_buried_loc

在 Windows 上使用 VS2010 使用我的 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'

关于c - 先前的堆栈变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10406681/

相关文章:

c++ - 是否可以将数组数字作为函数执行?

c - scanf 是否在读取输入数据后清空缓冲区(在 C 中)?

c - -0x4(%rbp) 在 gdb 反汇编中意味着什么?

c++ - 为什么字符串连接宏不适用于这个 "+"案例?

Delphi/ASM 代码与 64 位不兼容?

c - 如何在 C 中使用数组

c++ - 如何禁用缩小转换警告?

c - 汇编 x86-32 和一些 c 函数

c - 如何将寄存器的值从 ARM 汇编返回到 C 函数?

c - 为什么 C 中函数的原型(prototype)和定义可能不同?