Angelos Mavros
Angelos Mavros

Reputation: 29

understanding a x86 assembly program

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

Answers (1)

fuz
fuz

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

Related Questions