4.2 Calling and returning

LIFO is also the order of calling subroutines and returning from subroutines. Let us observe the following C code:

void h(void
{ 
} 
 
void g(void
{ 
  h(); 
} 
 
void f(void
{ 
  g(); 
} 
 
int main(void
{ 
  f(); 
  return 0; 
}

The order of calling and returning is as follows:

To accomplish this in assembly language programming, we can use the following instructions to “call” a subroutine:

# to call subroutine f 
pushl $L1 
jmp   f 
L1:

The first instruction “saves” the return address on the stack. The return address is represented by the label L1, which is defined after the jmp f instruction.

The second instruction jumps to the entry point of f so that the program continues execution at the subroutine.

At the end of a subroutine, we can use the following instructions to return to the caller:

# to return to my caller 
popl %ebx 
jmp *%ebx

The first instruction retrieves the return address from the stack, and put it in ebx.

The second instruction is a little more mysterious. It jumps to the location specified by ebx. The asterisk symbol (*) is not a typo, it is needed to indicate that this is an “indirect” jump via ebx. This is exactly what we need, as ebx contains the return address.