Reputation: 29
I compiled a program in C which just sums all integers up to input. For example if input is 4 the output is 4+3+2+1=10.
I am having a bit of a trouble understanding the assembly x86 version of this program.
I wrote all the comments myself, please be so kind to indicate what I got right/wrong and how you would describe what each lines does. Through your comments I will be able to absorb a DEEPER understand of what the cpu exactly does, as for the moment I can't say that I fully understand what is going on here. Anyway, here it is. All comments are welcome.
.LC0:
.byte 0x25,0x64,0x0 ; 2 digits / integers that our program will output
main:
pushl %ebp ; we save %ebp for later usage
movl %esp,%ebp ; we set register %ebp to point to the stack frame
subl $12,%esp ; subtracts 18 bytes from the stack pointer (esp). This allocates 18 bytes of space on the stack to be used for variables.
movl $0,-12(%ebp)
leal -4(%ebp),%eax ; subtracks -4 from the memory address of ebp and stores it at register eax
pushl %eax ; we store register eax for later usage
pushl $.LC0
call __isoc99_scanf ; reads from io port / waiting for key input
movl $1,-8(%ebp)
leal 8(%esp),%esp ; adds +8 to stack pointer memory address
.L2:
movl -4(%ebp),%edx
cmpl -8(%ebp),%edx ; compares our input number with an incremented number
jl .L3 ; if incremented number is equal or bigger than input number goto .L3
movl -8(%ebp),%edx
addl %edx,-12(%ebp)
incl -8(%ebp)
jmp .L2 ; loop / another addition to our input
.L3:
pushl -12(%ebp)
pushl $.LC0 ; we push the argument to print function
call printf ; prints result on screen
xorl %eax,%eax ; sets %eax to zero
leave ; leave copies the frame pointer to the stack point and releases the stack space formerly used by a procedure for its local variables. leave pops the old frame pointer into (E)BP, thus restoring the caller's frame.
ret ; returns to address located on the top of the stack```
Upvotes: 1
Views: 305
Reputation: 93127
Your analysis is mostly correct with some small mistakes which I'll try to point out. I've taken the liberty to break your comments so they can be read without scrolling.
Note that if you intend to pass off compiler output as hand written assembly, your teacher is very likely to catch this. Do not attempt to cheat on your assignments like this.
As an initial note: your function seems to have three variables. These are stored at -4(%ebp)
, -8(%ebp)
, and -12(%ebp)
. This is also why the C compiler emitted code to decrease the stack pointer by 12, allocating just enough storage for these variables.
.byte 0x25,0x64,0x0 ; 2 digits / integers that our program will output
This is the string "%d"
that is passed to scanf
.
subl $12,%esp ; subtracts 18 bytes from the stack pointer (esp).
; This allocates 18 bytes of space on the stack to
; be used for variables.
Note that the dollar sign merely indicates an immediate operand. Unlike with other assemblers, it does not indicate a hexadecimal number in AT&T syntax. As you've already seen with the .byte
directive, this is instead done with the 0x
prefix.
leal -4(%ebp),%eax ; subtracks -4 from the memory address of ebp
; and stores it at register eax
This explanation is confusing. What happens is that the leal
instruction takes a memory operand and stores the effective address of the operand into the register operand. So in this case, it computes the address of -4(%ebp)
(which is the content of ebp
plus 4
) and stores it into eax
. So eax = ebp + 4
. This is used to obtain the address of a piece of stack memory for use with scanf
.
pushl %eax ; we store register eax for later usage
pushl $.LC0
This does not save eax
for later use. Rather, eax
is pushed on the stack as an argument to the following scanf
call. Likewise, the following instruction pushes the address of string .LC0
onto the stack, preparing a scanf call like scanf("%d", &x)
where x
is the variable at -4(%ebp)
.
call __isoc99_scanf ; reads from io port / waiting for key input
This is just a call to the scanf
function in its C99 standard variant. The glibc has some logic to redirect calls to scanf
to different functions depending on which C standard revision you picked, so it picks this weirdly named symbol for -std=c99
.
Note that scanf
is a libc function. It does not perform any port IO but instead may or may not ask the operating system for additional input. Where this input comes from depends on what is attached to the standard input of your process.
leal 8(%esp),%esp ; adds +8 to stack pointer memory address
Just like any other general purpose register, the stack pointer does not have a memory address. However, it does have a value, representing an address. So it might be more accurate to say “increases the stack pointer by 8, popping the arguments off the stack.”
.L2:
movl -4(%ebp),%edx
cmpl -8(%ebp),%edx ; compares our input number with an incremented number
jl .L3 ; if incremented number is equal or bigger
;than input number goto .L3
movl -8(%ebp),%edx
addl %edx,-12(%ebp)
incl -8(%ebp)
jmp .L2 ; loop / another addition to our input
.L3:
Try to work out what this loop does. An easy way to do this is to write down each instruction as pseudo code and then refactor until it looks like something reasonable. Though if you got this from C code you wrote, you probably already know what it does.
pushl -12(%ebp)
pushl $.LC0 ; we push the argument to print function
call printf ; prints result on screen
Once again, this does something like printf("%d", z)
where z
is the variable at -12(%ebp)
.
ret ; returns to address located on the top of the stack
It might be more accurate to say “returns from the function.” This is because leave
has just cleared the stack frame, so the top of the stack holds the return address. The value returned from your function is what is held in eax
as per calling convention. This is also why eax
was cleared earlier: to return zero.
Upvotes: 2