Understanding x86 assembly code from C code
C code:
#include <stdio.h>
main() {
int i;
for (i = 0; i < 10; i++) {
printf("%s\n", "hello");
}
}
ASM:
.file "simple_loop.c"
.section .rodata
.LC0:
.string "hello"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushl %ebp # push ebp onto stack
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp # setup base pointer or stack ?
.cfi_def_cfa_register 5
andl $-16, %esp # ?
subl $32, %esp # ?
movl $0, 28(%esp) # i = 0
jmp .L2
.L3:
movl $.LC0, (%esp) # point stack pointer to "hello" ?
call puts # print "hello"
addl $1, 28(%esp) # i++
.L2:
cmpl $9, 28(%esp) # if i < 9
jle .L3 # goto l3
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
So, I am trying to improve my understanding of x86 assembly code. In the above code, I noted that as I understand it, I understand it. As for the content question, can anyone share some light? Also, if any of my comments are off, please let me know.
andl $-16, %esp # ?
subl $32, %esp # ?
This reserves some space on the stack. First, the instruction andl
rounds the register %esp
to the next lowest multiple of 16 bytes (Exercise: Learn the binary value -16
should figure out why). The command then subl
moves the stack pointer a bit (32 bytes), saving some more space (which it will use later). I suspect this rounding is done to make register access a %esp
little more efficient (but you will need to check your processor datasheets to figure out why).
movl $.LC0, (%esp) # point stack pointer to "hello" ?
This pushes the address of the string "hello"
onto the stack (this instruction does not change the value of the register itself %esp
). Obviously, your compiler considers it more efficient to push data onto the stack directly rather than using an instruction push
.
source to share