0

I wrote a single c program that prints input to std output. Then I converted it to assembly language. By the way I am using AT&T Syntax.

This is the simple C code.

#include <stdio.h>

int main()
{

int c; 


  while ((c = getchar ()) != EOF) 
    {

    putchar(c);

}

    return 0;
}

int c is a local variable.

Then I converted it to assembly language.

.file   "question_1.c"
    .text
    .globl  main
    .type   main, @function 

//prolog

main:
    leal    4(%esp), %ecx
    andl    $-16, %esp
    pushl   -4(%ecx)
    pushl   %ebp
    movl    %esp, %ebp
    pushl   %ecx
    subl    $20, %esp    // we add 20 bytes to the stack
    jmp .L2
.L3:
    subl    $12, %esp
    pushl   -12(%ebp)
    call    putchar
    addl    $16, %esp
.L2:
    call    getchar
    movl    %eax, -12(%ebp)
    cmpl    $-1, -12(%ebp)
    jne .L3

//assumption this is the epilog
    movl    $0, %eax
    movl    -4(%ebp), %ecx
    leave
    leal    -4(%ecx), %esp
    ret
    .size   main, .-main
    .ident  "GCC: (Ubuntu 4.9.4-2ubuntu1) 4.9.4"
    .section    .note.GNU-stack,"",@progbits

normally in the epilog we are supposed to addl 20 because in the prolog we subl 20. So the is the stack frame still there? Or am I missing out a crucial point?

I also have a question regarding the main function. Normally functions are normally "called" but where does it happen in the assembly code?

Thank you in advance.

  • Regarding the latter, the run-time loader invokes `main` after it does its preamble work. You compiled *your* source file to assembly with your `main`. If you have the source to your run-time library, you can peruse it to find how and where *it* actually invokes the `main` you provided. – WhozCraig May 23 '18 at 16:43
  • 2
    This appears to be unoptimized code; if you turn optimization on the code may become much shorter and easier to understand. Anyway, the stack adjustment on exit is performed by the `leave` instruction. – zwol May 23 '18 at 16:53
  • @zwol: The `leave` in this example does not restore the original stack pointer. It “restores” a value written to `%ebp` after modifying the stack pointer. – Eric Postpischil May 23 '18 at 17:13
  • 1
    @EricPostpischil Yeah, I guess I didn't read it closely enough. The true restoration of the original stack pointer is done by the `leal -4(%ecx),%esp` right before `ret`, as you say in your answer. But the `leave` balances the `push %ebp; mov %esp,%ebp` at the top, and it's necessary because EBP is call-saved, and OP needs to understand that `leave` restores EBP and undoes any stack adjustments after the last time EBP was set, in particular, the `subl $20,%esp` that they are confused about is undone by the `leave`. – zwol May 23 '18 at 17:22
  • I just googled and leave is the same as move ebp, esp pop ebp so that now I understand thank you –  May 23 '18 at 17:41

1 Answers1

2

Just after the main label, leal 4(%esp), %ecx saves four plus the stack pointer in %ecx. At the end of the routine, leal -4(%ecx), %esp writes four less than the saved value to the stack pointer. This directly restores the original value, instead of doing it by adding the amount that was subtracted.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • but why add 4 when 20 was subtracted? –  May 23 '18 at 17:00
  • 1
    @momonosuke: At rhe moment the routine is entered, four plus the stack pointer is just above the return address that was pushed onto the stack by the call instruction. Thus, it is the value the stack pointer had at the moment the caller executed to the call. This value could be useful for addressing arguments the caller put on the stack. Ntoice the `andl` instruction. That aligns the stack pointer to a multiple of 16 bytes, as needed to conform to the ABI. That changes the stack pointer by a variable number of bytes, so you cannot undo it by adding a fized number. – Eric Postpischil May 23 '18 at 17:09