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

时间:2011-10-12 00:07:55

标签: 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)).

2 个答案:

答案 0 :(得分:4)

我不打算为你做整件事,但这里有一个详细的解释,说明如何完成所发生的事情。

在进入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来电firstCall; call指令推送返回地址,因此在输入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
    +-----------------------------------+

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

......等等。只需按照%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

答案 1 :(得分:2)

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