【问题标题】:Assistance to draw a stack using C code and assembly code协助使用 C 代码和汇编代码绘制堆栈
【发布时间】:2011-12-05 17:51:27
【问题描述】:

我正在尝试绘制一个堆栈,因为它会出现在 secondCall 函数中的“返回计数”行之前。我正在尝试绘制它,以便它显示三个活动函数 main、firstCall 和 secondCall 的所有三个帧(或激活记录)。

有人能帮我完成堆栈图吗? 我正在尝试绘制基(ebp)和堆栈(esp)指针的位置,因为它们在调用下一个函数之前在每个堆栈帧中。

C代码如下:

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


int secondCall(int a, int b) {
  int count;
  count = write(STDOUT_FILENO, &"hello\n", 6);
  count += write(STDOUT_FILENO, &"jbnd007\n", 8);
  count += a + b;
  return count;
}
int firstCall(void) {
  int local;
  local = secondCall(4, 2);
  return local;
}
int main(int argc, char** argv) {
  int result;
  result = firstCall();
  return (EXIT_SUCCESS);
}

汇编代码如下:

    .file   "A3Program2.c"
    .section    .rodata
.LC0:
    .string "hello\n"
.LC1:
    .string "jbnd007\n"
    .text
.globl secondCall
    .type   secondCall, @function
secondCall:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $40, %esp
    movl    $6, 8(%esp)
    movl    $.LC0, 4(%esp)
    movl    $1, (%esp)
    call    write
    movl    %eax, -12(%ebp)
    movl    $8, 8(%esp)
    movl    $.LC1, 4(%esp)
    movl    $1, (%esp)
    call    write
    addl    %eax, -12(%ebp)
    movl    12(%ebp), %eax
    movl    8(%ebp), %edx
    leal    (%edx,%eax), %eax
    addl    %eax, -12(%ebp)
    movl    -12(%ebp), %eax
    leave
    ret
    .size   secondCall, .-secondCall
.globl firstCall
    .type   firstCall, @function
firstCall:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $40, %esp
    movl    $2, 4(%esp)
    movl    $4, (%esp)
    call    secondCall
    movl    %eax, -12(%ebp)
    movl    -12(%ebp), %eax
    leave
    ret
    .size   firstCall, .-firstCall
.globl main
    .type   main, @function
main:
    pushl   %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    subl    $16, %esp
    call    firstCall
    movl    %eax, 12(%esp)
    movl    $0, %eax
    leave
    ret
    .size   main, .-main
    .ident  "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5"
    .section    .note.GNU-stack,"",@progbits

我现在的堆栈图是:

+------------------------------+ high address
| original position of stack pointer
+------------------------------+
| saved value of ebp <- ebp (base pointer when in main)
+------------------------------+
| alignment spacing (don’t really know how big until runtime)
+------------------------------+
|
+------------------------------+
|
+------------------------------+
|
+------------------------------+
...
Each line represents 4 bytes (from lowest address (left) to highest address (right)).

【问题讨论】:

  • 这是一个开始;你为什么停在那里?有什么问题?
  • 我不知道下一步该做什么

标签: c assembly stack x86


【解决方案1】:

我不会为你做所有的事情,但这里有一个关于如何完成所发生的事情的详细说明。

在进入main 时,堆栈如下所示:

    : (whatever)                        :
    +-----------------------------------+
    | return address (in main's caller) | <- %esp
    +-----------------------------------+

标准序言代码:

pushl   %ebp
movl    %esp, %ebp

    : (whatever)                        :
    +-----------------------------------+
    | return address (in main's caller) |
    +-----------------------------------+
    | saved %ebp                        | <- new %ebp = %esp
    +-----------------------------------+

这通过将底部 4 位归零来将堆栈向下对齐到 16 字节边界 %esp:

andl    $-16, %esp

    : (whatever)                        :
    +-----------------------------------+
    | return address (in main's caller) |
    +-----------------------------------+
    | saved %ebp                        | <- new %ebp
    +-----------------------------------+
    : some unknown amount of space      :
    : (0, 4, 8 or 12 bytes)             : <- %esp
    +-----------------------------------+

...这是你要去的地方。继续:

这会从堆栈指针中减去 16 个字节,从而为 main 创建 16 个字节的保留空间以供使用:

subl    $16, %esp

    : (whatever)                        :
    +-----------------------------------+
    | return address (in main's caller) |
    +-----------------------------------+
    | saved %ebp                        | <- %ebp
    +-----------------------------------+
    : some unknown amount of space      :
    : (0, 4, 8 or 12 bytes)             :
    +-----------------------------------+
    | 16 bytes of reserved  space       |
    |                                   |
    |                                   |
    |                                   | <- %esp
    +-----------------------------------+

现在main 打电话给firstCallcall 指令压入返回地址,所以在刚刚进入firstCall 之后,堆栈将如下所示:

call    firstCall

    : (whatever)                        :
    +-----------------------------------+
    | return address (in main's caller) |
    +-----------------------------------+
    | saved %ebp                        | <- %ebp
    +-----------------------------------+
    : some unknown amount of space      :
    : (0, 4, 8 or 12 bytes)             :
    +-----------------------------------+
    | 16 bytes of reserved space        |
    |                                   |
    |                                   |
    |                                   |
    +-----------------------------------+
    | return address (in main)          | <- %esp
    +-----------------------------------+

由于firstCall末尾的ret指令,返回main时会再次弹出返回地址。

...等等。只需按照 %esp 的操作,以同样的方式继续跟踪代码即可。

可能需要解释的另一件事是leave,它出现在 各种例程的结尾代码。这就是main 的工作原理:

就在leave 接近main 的末尾之前,堆栈看起来像这样(我们从firstCall 返回 并在保留空间中存储了一个值):

    : (whatever)                        :
    +-----------------------------------+
    | return address (to main's caller) |
    +-----------------------------------+
    | saved %ebp                        | <- %ebp
    +-----------------------------------+
    : some unknown amount of space      :
    : (0, 4, 8 or 12 bytes)             :
    +-----------------------------------+
    | %eax returned by firstCall        |
    | (and 12 bytes that were never     |
    |  used)                            |
    |                                   | <- %esp
    +-----------------------------------+

leave 等价于movl %ebp, %esp,后跟popl %ebp。所以:

movl   %ebp, %esp   ; (first part of "leave")

    : (whatever)                        :
    +-----------------------------------+
    | return address (in main's caller) |
    +-----------------------------------+
    | saved %ebp                        | <- %esp = current %ebp
    +-----------------------------------+ 
    : some unknown amount of space      :  }
    : (0, 4, 8 or 12 bytes)             :  }
    +-----------------------------------+  } all of this stuff is
    | %eax returned by firstCall        |  }  irrelevant now
    | (and 12 bytes that were never     |  }
    |  used)                            |  }
    |                                   |  }
    +-----------------------------------+

popl   %ebp         ; (second part of "leave")

    : (whatever)                        :
    +-----------------------------------+
    | return address (in main's caller) | <- %esp  (%ebp has now been restored to the
    +-----------------------------------+            value it had on entry to "main")
      (and now-irrelevant stuff below)           

最后ret 弹出返回地址并在内部继续执行 不管叫什么main

【讨论】:

  • 兄弟你是个传奇!剩下的就交给我了!
【解决方案2】:

gdb 中的return count 行中断,然后使用x/30xw $esp 之类的内容打印堆栈。您可以在进入要记录的堆栈部分之前先中断并记下$esp,以便获得比我疯狂猜测的 30 个单词更精确的计数。

【讨论】:

  • gdb是在Linux下运行的调试程序。
  • 提供终端会话记录。似乎对您如何向我进行了非常完整的解释。
猜你喜欢
  • 2015-12-23
  • 1970-01-01
  • 2019-10-01
  • 1970-01-01
  • 2017-11-18
  • 2014-05-29
  • 1970-01-01
  • 2018-05-07
  • 2011-08-27
相关资源
最近更新 更多