Assistance to draw a stack using C code and assembly code
I am trying to draw a stack as it would appear just before the “return count” line in the secondCall function. I am trying to draw it so that it would show all three frames (or activation records) for the three active functions, main, firstCall and secondCall.
Will someone help me complete the stack diagram? I am trying to draw the positions of the base (ebp) and stack (esp) pointers as they were in each stack frame before the call to the next function.
The C code is as follows:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int secondCall(int a, int b) {
int count;
count = write(STDOUT_FILENO, &"hellon", 6);
count += write(STDOUT_FILENO, &"jbnd007n", 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);
}
The Assembly code is as follows:
.file "A3Program2.c"
.section .rodata
.LC0:
.string "hellon"
.LC1:
.string "jbnd007n"
.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
The stack drawing right now I have is :
+------------------------------+ 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)).
I'm not going to do the whole thing for you, but here's a detailed explanation of how to follow through what happens.
On entry to main
the stack looks like this:
: (whatever) :
+-----------------------------------+
| return address (in main's caller) | <- %esp
+-----------------------------------+
Standard prologue code:
pushl %ebp
movl %esp, %ebp
: (whatever) :
+-----------------------------------+
| return address (in main's caller) |
+-----------------------------------+
| saved %ebp | <- new %ebp = %esp
+-----------------------------------+
This aligns the stack down to a 16-byte boundary by zeroing the bottom 4 bits of %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
+-----------------------------------+
...which is where you got to. Continuing:
This subtracts 16 bytes from the stack pointer, which creates 16 bytes of reserved space for main
to use:
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
+-----------------------------------+
Now main
calls firstCall
; the call
instruction pushes the return address, so at the point just after firstCall
has been entered, the stack will look like this:
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
+-----------------------------------+
The return address will be popped off again when returning to main
due to the ret
instruction at the end of firstCall
.
...and so on. Just keep tracing through the code in the same way, following what %esp
is doing.
The other thing that perhaps needs explanation is the leave
which appears in the epilogue code of the various routines. So here's how that works for main
:
Just before leave
near the end of main
, the stack looks like this (we've returned from firstCall
and stored a value in the reserved space):
: (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
is equivalent to movl %ebp, %esp
followed by popl %ebp
. So:
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)
And finally the ret
pops the return address and execution continues inside whatever called main
.
Break at the return count
line in gdb
and then just print the stack using something like x/30xw $esp
. You could break earlier and note $esp
before entering the portion of the stack you want to log in order to get a more precise count than my wild guess of 30 words.
上一篇: 有关基址指针和堆栈指针的问题
下一篇: 协助使用C代码和汇编代码绘制堆栈