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.

链接地址: http://www.djcxy.com/p/80382.html

上一篇: 有关基址指针和堆栈指针的问题

下一篇: 协助使用C代码和汇编代码绘制堆栈