c - 协助使用C代码和汇编代码绘制堆栈

标签 c assembly stack x86

我正在尝试绘制一个堆栈,因为它会出现在 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)).

最佳答案

我不会为你做所有事情,但这里有一个关于如何跟进发生的事情的详细解释。

进入 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 的工作原理:

就在 main 结束附近的 leave 之前,堆栈看起来像这样(我们从 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 的内容。

关于c - 协助使用C代码和汇编代码绘制堆栈,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7733937/

相关文章:

c - 对名为 'full_adder(BIT A, BIT B, BIT Ci, BIT *Co)' 的函数的 undefined reference

c - fminunc如何优化梯度下降中的学习率(步长比例)值?

linux - 来自内核模块的调试堆栈

c - c中的堆栈内存布局

algorithm - 有没有一种算法可以在恒定时间内返回堆栈中第二大的元素?

c - 在 ANSI C 中将命令行参数传递到程序的不同部分

c - 需要 C 语言编译帮助

c++ - 将这些 C++ 行转换为汇编/mips 时,我做错了什么?

assembly - 有什么方法可以缩短 AArch64 汇编中的机器代码 Hello World 吗?

linux - 程序集写入标准输出