Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

assembly code of the c function

Tags:

c

x86

assembly

I'm trying to understand the assembly code of the C function. I could not understand why andl -16 is done at the main. Is it for allocating space for the local variables. If so why subl 32 is done for main.

I could not understand the disassembly of the func1. As read the stack grows from higher order address to low order address for 8086 processors. So here why is the access on positive side of the ebp(for parameters offset) and why not in the negative side of ebp. The local variables inside the func1 is 3 + return address + saved registers - So it has to be 20, but why is it 24? (subl $24,esp)

#include<stdio.h>
int add(int a, int b){
 int res = 0;
 res = a + b;
 return res;
}
int func1(int a){
 int s1,s2,s3;
 s1 = add(a,a);
 s2 = add(s1,a);
 s3 = add(s1,s2);
 return s3;
}
int main(){
 int a,b;
 a = 1;b = 2;
 b = func1(a);
 printf("\n a : %d b : %d \n",a,b);
 return 0;
}

assembly code :

       .file   "sample.c"
        .text
.globl add
        .type   add, @function
add:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $16, %esp
        movl    $0, -4(%ebp)
        movl    12(%ebp), %eax
        movl    8(%ebp), %edx
        leal    (%edx,%eax), %eax
        movl    %eax, -4(%ebp)
        movl    -4(%ebp), %eax
        leave
        ret
        .size   add, .-add
.globl func1
        .type   func1, @function
func1:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $24, %esp
        movl    8(%ebp), %eax
        movl    %eax, 4(%esp)
        movl    8(%ebp), %eax
        movl    %eax, (%esp)
        call    add
        movl    %eax, -4(%ebp)
        movl    8(%ebp), %eax
        movl    %eax, 4(%esp)
        movl    -4(%ebp), %eax
        movl    %eax, (%esp)
        call    add
        movl    %eax, -8(%ebp)
        movl    -8(%ebp), %eax
        movl    %eax, 4(%esp)
        movl    -4(%ebp), %eax
        movl    %eax, (%esp)
                                      call    add
        movl    %eax, -12(%ebp)
        movl    -12(%ebp), %eax
        leave
        ret
        .size   func1, .-func1
        .section        .rodata
.LC0:
        .string "\n a : %d b : %d \n"
        .text
.globl main
        .type   main, @function
main:
        pushl   %ebp
        movl    %esp, %ebp
        andl    $-16, %esp
        subl    $32, %esp
        movl    $1, 28(%esp)
        movl    $2, 24(%esp)
        movl    28(%esp), %eax
        movl    %eax, (%esp)
        call    func1
        movl    %eax, 24(%esp)
        movl    $.LC0, %eax
        movl    24(%esp), %edx
        movl    %edx, 8(%esp)
        movl    28(%esp), %edx
        movl    %edx, 4(%esp)
        movl    %eax, (%esp)
        call    printf
        movl    $0, %eax
        leave
        ret
        .size   main, .-main
        .ident  "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5"
        .section        .note.GNU-stack,"",@progbits
like image 960
Angus Avatar asked Apr 01 '13 23:04

Angus


1 Answers

The andl $-16, %esp aligns the stack pointer to a multiple of 16 bytes, by clearing the low four bits.

The only places where positive offsets are used with (%ebp) are parameter accesses.

You did not state what your target platform is or what switches you used to compile with. The assembly code shows some Ubuntu identifier has been inserted, but I am not familiar with the ABI it uses, beyond that it is probably similar to ABIs generally used with the Intel x86 architecture. So I am going to guess that the ABI requires 8-byte alignment at routine calls, and so the compiler makes the stack frame of func1 24 bytes instead of 20 so that 8-byte alignment is maintained.

I will further guess that the compiler aligned the stack to 16 bytes at the start of main as a sort of “preference” in the compiler, in case it uses SSE instructions that prefer 16-byte alignment, or other operations that prefer 16-byte alignment.

So, we have:

In main, the andl $-16, %esp aligns the stack to a multiple of 16 bytes as a compiler preference. Inside main, 28(%esp) and 24(%esp) refer to temporary values the compiler saves on the stack, while 8(%esp), 4(%esp), and (%esp) are used to pass parameters to func1 and printf. We see from the fact that the assembly code calls printf but it is commented out in your code that you have pasted C source code that is different from the C source code used to generate the assembly code: This is not the correct assembly code generated from the C source code.

In func1, 24 bytes are allocated on the stack instead of 20 to maintain 8-byte alignment. Inside func1, parameters are accessed through 8(%ebp) and 4(%ebp). Locations from -12(%ebp) to -4(%ebp) are used to hold values of your variables. 4(%esp) and (%esp) are used to pass parameters to add.

Here is the stack frame of func1:

    - 4(%ebp) = 20(%esp): s1.
    - 8(%ebp) = 16(%esp): s2.
    -12(%ebp) = 12(%esp): s3.
    -16(%ebp) =  8(%esp): Unused padding.
    -20(%ebp) =  4(%esp): Passes second parameter of add.
    -24(%ebp) =  0(%esp): Passes first parameter of add.
like image 97
Eric Postpischil Avatar answered Oct 20 '22 13:10

Eric Postpischil