2
votes

I am learning GNU assembly from Jonathan Bartlett's "Programming from ground up" book.

While going through the topic of a function call and stack, I'm unable to understand its working.

Below is what's written in the book:

Before executing a function, a program pushes all of the parameters for the function onto the stack in the reverse order that they are documented. Then the program issues a call instruction indicating which function it wishes to start. The call instruction does two things. First it pushes the address of the next instruction, which is the return address, onto the stack. Then it modifies the instruction pointer (%eip) to point to the start of the function. So, at the time the function starts, the stack looks like this (the "top" of the stack is at the bottom on this example):

Parameter #N
...
Parameter 2
Parameter 1
Return Address <--- (%esp)

Each of the parameters of the function have been pushed onto the stack, and finally the return address is there. Now the function itself has some work to do.

The first thing it does is save the current base pointer register, %ebp, by doing pushl %ebp. The base pointer is a special register used for accessing function parameters and local variables. Next,it copies the stack pointer to %ebp by doing movl %esp, %ebp. This allows you to be able to access the function parameters as fixed indexes from the base pointer. You may think that you can use the stack pointer for this. However, during your program you may do other things with the stack such as pushing arguments to other functions. Copying the stack pointer into the base pointer at the beginning of a function allows you to always know where your parameters are (and as we will see, local variables too), even while you may be pushing things on and off the stack. %ebp will always be where the stack pointer was at the beginning of the function, so it is more or less a constant reference to the stack frame (the stack frame consists of all of the stack variables used within a function, including parameters, local variables, and the return address).

At this point, the stack looks like this:

Parameter #N <--- N*4+4(%ebp)
...
Parameter 2 <--- 12(%ebp)
Parameter 1 <--- 8(%ebp)
Return Address <--- 4(%ebp)
Old %ebp <--- (%esp) and (%ebp)

As you can see, each parameter can be accessed using base pointer addressing mode using the %ebp register.

Can I get a concise intro on what the author wants to tell after the second paragraph. I am clearly confusing among the %esp, %ebp registers and %ebp's working here. Any help is highly appreciated.

1
It's 32-bit x86 with the calling convention used on Linux that you're asking about, so I edited your question title so it will look right to people that know assembly. "GNU assembly" is a syntax; you could write code using other calling conventions with it. (The book you're reading is good, BTW. Keep reading it.)Peter Cordes
@Yatendra: can you say something about what you do understand? The block you quoted (especially the "diagrams") would make an excellent answer to this question, except that you say you don't understand something about it. You know that push does %esp -= 4 and then stores to (%esp), right?Peter Cordes
@PeterCordes Thanq so much for your suggestions and edits. In the second diagram, there is this "Old %ebp <--- (%esp) and (%ebp)". What does it mean ?Yatendra Rathore

1 Answers

3
votes

In the second diagram, there is this "Old %ebp <--- (%esp) and (%ebp)". What does it mean ?

It means the saved %ebp value (the caller's value that your function saves/restores) is pointed to by both %esp and your new %ebp at that point.

You just ran push %ebp, which did esp -= 4 and stored %ebp to (%esp). This saves your caller's %ebp so you can restore it later.

Then you ran mov %esp, %ebp to set up %ebp as a frame pointer. So %ebp = %esp, and they're both pointing at the last thing you pushed.