Assembly Language:
Function Calls
Goals of this Lecture
• Help you learn:
• Function call problems:
• Calling and returning
• Passing parameters
• Storing local variables
• Handling registers without interference
• Returning values
• IA-32 solutions to those problems
• Pertinent instructions and conventions
Function Call Problems
1. Calling and returning
• How does caller function jump to callee function?
• How does callee function jump back to the right place in caller
function?
2. Passing parameters
• How does caller function pass parameters to callee function?
3. Storing local variables
• Where does callee function store its local variables?
4. Handling registers
• How do caller and callee functions use same registers without
interference?
5. Returning a value
• How does callee function send return value back to caller function?
3
1
Problem 1: Calling and Returning
How does caller function jump to callee function?
• I.e., Jump to the address of the calleeʼs first instruction
How does the callee function jump back to the right place in
caller function?
• I.e., Jump to the instruction immediately following the
most-recently-executed call instruction
Attempted Solution: Use Jmp Instruction
• Attempted solution: caller and callee use jmp instruction
P: # Function P R: # Function R
… …
jmp R # Call R jmp Rtn_point1 # Return
Rtn_point1:
…
Attempted Solution: Use Jmp Instruction
• Problem: callee may be called by multiple callers
P: # Function P R: # Function R
… …
jmp R # Call R jmp ??? # Return
Rtn_point1:
…
Q: # Function Q
…
jmp R # Call R
Rtn_point2:
…
6
2
Attempted Solution: Use Register
• Attempted solution 2: Store return address in register
P: # Function P R: # Function R
movl $Rtn_point1, %eax …
jmp R # Call R jmp *%eax # Return
Rtn_point1:
…
Q: # Function Q
movl $Rtn_point2, %eax
Special form of jmp
jmp R # Call R
instruction; we will not use
Rtn_point2:
…
7
Attempted Solution: Use Register
• Problem: Cannot handle nested function calls
P: # Function P R: # Function R
movl $Rtn_point1, %eax …
jmp Q # Call Q jmp *%eax # Return
Rtn_point1:
…
Q: # Function Q
movl $Rtn_point2, %eax
Problem if P calls Q, and Q
calls R
jmp R # Call R
Rtn_point2: Return address for P to Q
… call is lost
jmp %eax # Return
8
IA-32 Solution: Use the Stack
• May need to store many return addresses
• The number of nested functions is not known in advance
• A return address must be saved for as long as the
function invocation continues, and discarded thereafter
• Addresses used in reverse order
• E.g., function P calls Q, which then calls R
EIP for Q
• Then R returns to Q which then returns to P
EIP for P
• Last-in-first-out data structure (stack)
• Caller pushes return address on the stack
• … and callee pops return address off the stack
• IA 32 solution: Use the stack via call and ret
9
3
IA-32 Call and Ret Instructions
• Ret instruction “knows” the return address
P: # Function P R: # Function R
… …
1
call R ret
2
call Q
…
Q: # Function Q
…
call R
…
ret
10
IA-32 Call and Ret Instructions
• Ret instruction “knows” the return address
P: # Function P R: # Function R
… …
call R ret
call Q
…
3
4
5
Q: # Function Q
…
call R
…
6
ret
11
Implementation of Call
• ESP (stack pointer register)
0
points to top of stack
Instruction
Effective Operations
pushl src subl $4, %esp
movl src, (%esp)
popl dest movl (%esp), dest
addl $4, %esp
ESP
12
4
Implementation of Call
• EIP (instruction pointer
0
register) points to next
instruction to be executed
Note: can’t really access EIP
Instruction
Effective Operations
directly, but this is implicitly
pushl src subl $4, %esp what call is doing
movl src, (%esp)
popl dest movl (%esp), dest
addl $4, %esp
call addr pushl %eip
jmp addr ESP
before
Call instruction pushes return call
address (old EIP) onto stack
13
Implementation of Call
0
Instruction
Effective Operations
pushl src subl $4, %esp
movl src, (%esp)
popl dest movl (%esp), dest
addl $4, %esp
call addr pushl %eip
jmp addr ESP Old EIP
after
call
14
Implementation of Ret
0
Instruction
Effective Operations
pushl src subl $4, %esp Note: can’t really access EIP
movl src, (%esp) directly, but this is implicitly
popl dest movl (%esp), dest
what ret is doing.
addl $4, %esp
call addr pushl %eip
jmp addr ESP Old EIP
ret pop %eip before
ret
Ret instruction pops stack, thus
placing return address (old EIP)
into EIP 15
5
Implementation of Ret
0
Instruction
Effective Operations
pushl src subl $4, %esp
movl src, (%esp)
popl dest movl (%esp), dest
addl $4, %esp
call addr pushl %eip
jmp addr
ret pop %eip ESP
after
ret
16
Problem 2: Passing Parameters
• Problem: How does caller function pass parameters to
callee function?
int add3(int a, int b, int c)
{
int d;
d = a + b + c;
return d;
}
int f(void)
{
return add3(3, 4, 5);
}
17
Attempted Solution: Use Registers
• Attempted solution: Pass parameters in registers
f: add3:
movl $3, %eax …
movl $4, %ebx # Use EAX, EBX, ECX
movl $5, %ecx …
call add3 ret
…
18
6
Attempted Solution: Use Registers
• Problem: Cannot handle nested function calls
f: add3:
movl $3, %eax …
movl $4, %ebx movl $6, %eax
movl $5, %ecx call g
call add3 # Use EAX, EBX, ECX
… # But EAX is corrupted!
…
ret
• Also: How to pass parameters that are longer than 4 bytes?
19
IA-32 Solution: Use the Stack
• Caller pushes parameters before 0
executing the call instruction
ESP before
pushing
params
20
IA-32 Parameter Passing
• Caller pushes parameters in the 0
reverse order
• Push Nth param first
• Push 1st param last
• So first param is at top of
the stack at the time of the Call
ESP Param 1
before Param …
call
Param N
21
7
IA-32 Parameter Passing
• Callee addresses params relative to 0
ESP: Param 1 as 4(%esp)
ESP Old EIP
after Param 1
call
Param …
Param N
22
IA-32 Parameter Passing
• After returning to the caller…
0
ESP Param 1
after Param …
return
Param N
23
IA-32 Parameter Passing
• … the caller pops the parameters 0
from the stack
ESP
after
popping
params
24
8
IA-32 Parameter Passing
For example:
f: add3:
… …
# Push parameters movl 4(%esp), wherever
pushl $5 movl 8(%esp), wherever
pushl $4 movl 12(%esp), wherever
pushl $3 …
call add3 ret
# Pop parameters
addl $12, %esp
25
Base Pointer Register: EBP
• Problem:
• As callee executes, ESP may 0
change
• E.g., preparing to call
another function
• Error-prone for callee to
reference params as offsets
relative to ESP
ESP Old EIP
after Param 1
• Solution:
call
Param …
• Use EBP as fixed reference
point to access params
Param N
EBP
26
Using EBP
• Need to save old value of EBP
• Before overwriting EBP register
0
• Callee executes “prolog”
pushl %ebp
movl %esp, %ebp ESP Old EBP
Old EIP
Param 1
Param …
Param N
EBP
27
9
Base Pointer Register: EBP
• Callee executes “prolog”
0
pushl %ebp
movl %esp, %ebp
ESP, Old EBP
EBP
Old EIP
• Regardless of ESP, callee can Param 1
reference param 1 as 8(%ebp), Param …
param 2 as 12(%ebp), etc.
Param N
28
Base Pointer Register: EBP
• Before returning, callee must 0
restore ESP and EBP to their
ESP
old values
• Callee executes “epilog”
movl %ebp, %esp EBP Old EBP
popl %ebp Old EIP
Param 1
ret
Param …
Param N
29
Base Pointer Register: EBP
• Callee executes “epilog”
0
movl %ebp, %esp
popl %ebp
ret
ESP, Old EBP
EBP Old EIP
Param 1
Param …
Param N
30
10
Base Pointer Register: EBP
• Callee executes “epilog”
0
movl %ebp, %esp
popl %ebp
ret
ESP Old EIP
Param 1
Param …
Param N
EBP
31
Base Pointer Register: EBP
• Callee executes “epilog”
0
movl %ebp, %esp
popl %ebp
ret
ESP Param 1
Param …
Param N
EBP
32
Problem 3: Storing Local Variables
• Where does callee function store its local variables?
int add3(int a, int b, int c)
{
int d;
d = a + b + c;
return d;
}
int foo(void)
{
return add3(3, 4, 5);
}
33
11
IA-32 Solution: Use the Stack
• Local variables:
• Short-lived, so donʼt need a
permanent location in
memory
int add3(int a, int b, int c)
• Size known in advance, so {
int d;
donʼt need to allocate on the d = a + b + c;
heap
return d;
}
• So, the function just uses
int foo(void)
the top of the stack
{
• Store local variables on the return add3(3, 4, 5);
top of the stack
}
• The local variables disappear
after the function returns
34
IA-32 Local Variables
• Local variables of the callee
are allocated on the stack
0
• Allocation done by moving the ESP Var 2
stack pointer
Var 1
• Example: allocate memory for EBP Old EBP
two integers
Old EIP
• subl $4, %esp
Param 1
• subl $4, %esp
Param …
• (or equivalently, subl $8, %esp)
Param N
• Reference local variables as
negative offsets relative to EBP
• -4(%ebp)
• -8(%ebp)
35
IA-32 Local Variables
For example:
add3:
…
# Allocate space for d
subl $4, %esp
…
# Access d
movl whatever, -4(%ebp)
…
ret
36
12
Problem 4: Handling Registers
• Problem: How do caller and callee functions use
same registers without interference?
• Registers are a finite resource!
• In principle: Each function should have its own set of
registers
• In reality: All functions must use the same small set of
registers
• Callee may use a register that the caller also is
using
• When callee returns control to caller, old register
contents may be lost
• Caller function cannot continue where it left off
37
IA-32 Solution: Define a Convention
• IA-32 solution: save the registers on the stack
• Someone must save old register contents
• Someone must later restore the register contents
• Define a convention for who saves and restores
which registers
38
IA-32 Register Handling
0
• Caller-save registers
• EAX, EDX, ECX
ESP Var 2
• If necessary…
Var 1
• Caller saves on stack before call
• Caller restores from stack after call
Saved EBX,
ESI,EDI
• Callee-save registers
EBP Old EBP
• EBX, ESI, EDI
Old EIP
• If necessary…
Param 1
• Callee saves on stack after prolog
Param …
• Callee restores from stack before
epilog
Param N
• Caller can assume that values in EBX, Saved EAX,
ESI, EDI will not be changed by callee
EDX,ECX
39
13
Problem 5: Return Values
• Problem: How does callee
function send return value
back to caller function?
int add3(int a, int b, int c)
• In principle:
{
int d;
• Store return value in stack d = a + b + c;
frame of caller
return d;
}
• Or, for efficiency:
int foo(void)
• Known small size => store {
return value in register
return add3(3, 4, 5);
• Other => store return value in }
stack
40
IA-32 Return Values
IA-32 Convention:
• Integral type or pointer:
• Store return value in EAX
int add3(int a, int b, int c)
• char, short, int, long, pointer
{
int d;
d = a + b + c;
• Floating-point type:
return d;
• Store return value in floating- }
point register
int foo(void)
• (Beyond scope of course)
{
return add3(3, 4, 5);
• Structure:
}
• Store return value on stack
• (Beyond scope of course)
41
Stack Frames
Summary of IA-32 function handling:
• Stack has one stack frame per active function invocation
• ESP points to top (low memory) of current stack frame
• EBP points to bottom (high memory) of current stack
frame
• Stack frame contains:
• Return address (Old EIP)
• Old EBP
• Saved register values
• Local variables
• Parameters to be passed to callee function
42
14
A Simple Example
int add3(int a, int b, int c)
{
int d;
d = a + b + c;
return d;
}
/* In some calling function */
…
x = add3(3, 4, 5);
…
43
Trace of a Simple Example 1
x = add3(3, 4, 5); Low memory
ESP
EBP
High memory 44
Trace of a Simple Example 2
x = add3(3, 4, 5); Low memory
# Save caller-save registers if necessary
pushl %eax
pushl %ecx
pushl %edx
ESP Old EDX
Old ECX
Old EAX
EBP
High memory 45
15
Trace of a Simple Example 3
x = add3(3, 4, 5); Low memory
# Save caller-save registers if necessary
pushl %eax
pushl %ecx ESP 3
pushl %edx 4
# Push parameters
pushl $5 5
pushl $4 Old EDX
pushl $3 Old ECX
Old EAX
EBP
High memory 46
Trace of a Simple Example 4
x = add3(3, 4, 5); Low memory
# Save caller-save registers if necessary
pushl %eax ESP Old EIP
pushl %ecx 3
pushl %edx 4
# Push parameters
pushl $5 5
pushl $4 Old EDX
pushl $3 Old ECX
# Call add3
call add3 Old EAX
EBP
High memory 47
Trace of a Simple Example 5
int add3(int a, int b, int c) { Low memory
int d;
d = a + b + c;
return d;
}
ESP Old EBP
# Save old EBP Old EIP
pushl %ebp
Prolog 3
4
5
Old EDX
Old ECX
Old EAX
EBP
High memory 48
16
Trace of a Simple Example 6
int add3(int a, int b, int c) { Low memory
int d;
d = a + b + c;
return d;
}
ESP Old EBP
# Save old EBP EBP Old EIP
pushl %ebp
# Change EBP Prolog 3
movl %esp, %ebp 4
5
Old EDX
Old ECX
Old EAX
High memory 49
Trace of a Simple Example 7
int add3(int a, int b, int c) { Low memory
int d;
d = a + b + c; ESP Old EDI
return d; Old ESI
} Old EBX
EBP Old EBP
# Save old EBP Old EIP
pushl %ebp
# Change EBP 3
movl %esp, %ebp 4
# Save caller-save registers if necessary 5
pushl %ebx Unnecessary here; add3 will not
pushl %esi Old EDX
pushl %edi change the values in these registers Old ECX
Old EAX
High memory 50
Trace of a Simple Example 8
int add3(int a, int b, int c) { Low memory
int d; ESP
d = a + b + c; Old EDI
return d; Old ESI
} Old EBX
EBP Old EBP
# Save old EBP Old EIP
pushl %ebp
# Change EBP 3
movl %esp, %ebp 4
# Save caller-save registers if necessary 5
pushl %ebx
pushl %esi Old EDX
pushl %edi Old ECX
# Allocate space for local variable Old EAX
subl $4, %esp
High memory 51
17
Trace of a Simple Example 9
int add3(int a, int b, int c) { Low memory
int d; ESP 12
d = a + b + c; Old EDI
return d; Old ESI
} Old EBX
EBP Old EBP
# Save old EBP Old EIP
pushl %ebp
# Change EBP 3
movl %esp, %ebp 4
# Save caller-save registers if necessary 5
pushl %ebx
pushl %esi Old EDX
pushl %edi Old ECX
# Allocate space for local variable Old EAX
subl $4, %esp Access params as positive
# Perform the addition offsets relative to EBP
movl 8(%ebp), %eax
addl 12(%ebp), %eax Access local vars as negative
addl 16(%ebp), %eax
movl %eax, -16(%ebp) offsets relative to EBP
High memory 52
Trace of a Simple Example 10
int add3(int a, int b, int c) { Low memory
int d; ESP 12
d = a + b + c; Old EDI
return d; Old ESI
} Old EBX
EBP Old EBP
# Copy the return value to EAX Old EIP
movl -16(%ebp), %eax
# Restore callee-save registers if necessary 3
movl -12(%ebp), %edi 4
movl -8(%ebp), %esi 5
movl -4(%ebp), %ebx
Old EDX
Old ECX
Old EAX
High memory 53
Trace of a Simple Example 11
int add3(int a, int b, int c) { Low memory
int d; 12
d = a + b + c; Old EDI
return d; Old ESI
} Old EBX
ESP Old EBP
# Copy the return value to EAX EBP Old EIP
movl -16(%ebp), %eax
# Restore callee-save registers if necessary 3
movl -12(%ebp), %edi 4
movl -8(%ebp), %esi 5
movl -4(%ebp), %ebx
# Restore ESP Old EDX
movl %ebp, %esp Old ECX
Epilog Old EAX
High memory 54
18
Trace of a Simple Example 12
int add3(int a, int b, int c) { Low memory
int d; 12
d = a + b + c; Old EDI
return d; Old ESI
} Old EBX
Old EBP
# Copy the return value to EAX ESP Old EIP
movl -16(%ebp), %eax
# Restore callee-save registers if necessary 3
movl -12(%ebp), %edi 4
movl -8(%ebp), %esi 5
movl -4(%ebp), %ebx
# Restore ESP Old EDX
movl %ebp, %esp Old ECX
# Restore EBP Epilog Old EAX
popl %ebp
EBP
High memory 55
Trace of a Simple Example 13
int add3(int a, int b, int c) { Low memory
int d; 12
d = a + b + c; Old EDI
return d; Old ESI
} Old EBX
Old EBP
# Copy the return value to EAX Old EIP
movl -16(%ebp), %eax
# Restore callee-save registers if necessary ESP 3
movl -12(%ebp), %edi 4
movl -8(%ebp), %esi 5
movl -4(%ebp), %ebx
# Restore ESP Old EDX
movl %ebp, %esp Old ECX
# Restore EBP Old EAX
popl %ebp
# Return to calling function
ret
EBP
High memory 56
Trace of a Simple Example 14
x = add3(3, 4, 5); Low memory
12
Old EDI
# Save caller-save registers if necessary
pushl %eax Old ESI
pushl %ecx Old EBX
pushl %edx Old EBP
# Push parameters
pushl $5 Old EIP
pushl $4 3
pushl $3 4
# Call add3
call add3 5
# Pop parameters ESP Old EDX
addl $12, %esp Old ECX
Old EAX
EBP
High memory 57
19
Trace of a Simple Example 15
x = add3(3, 4, 5); Low memory
12
Old EDI
# Save caller-save registers if necessary
pushl %eax Old ESI
pushl %ecx Old EBX
pushl %edx Old EBP
# Push parameters
pushl $5 Old EIP
pushl $4 3
pushl $3 4
# Call add3
call add3 5
# Pop parameters ESP Old EDX
addl %12, %esp Old ECX
# Save return value
movl %eax, wherever Old EAX
EBP
High memory 58
Trace of a Simple Example 16
x = add3(3, 4, 5); Low memory
12
Old EDI
# Save caller-save registers if necessary
pushl %eax Old ESI
pushl %ecx Old EBX
pushl %edx Old EBP
# Push parameters
pushl $5 Old EIP
pushl $4 3
pushl $3 4
# Call add3
call add3 5
# Pop parameters Old EDX
addl %12, %esp Old ECX
# Save return value
movl %eax, wherever Old EAX
# Restore caller-save registers if necessary ESP
popl %edx
popl %ecx
popl %eax
EBP
High memory 59
Trace of a Simple Example 17
x = add3(3, 4, 5); Low memory
# Save caller-save registers if necessary
pushl %eax
pushl %ecx
pushl %edx
# Push parameters
pushl $5
pushl $4
pushl $3
# Call add3
call add3
# Pop parameters
addl %12, %esp
# Save return value
movl %eax, wherever
# Restore caller-save registers if necessary ESP
popl %edx
popl %ecx
popl %eax
# Proceed! EBP
… 60
High memory
20
Summary
• Calling and returning
• Call instruction: push EIP onto stack and jump
• Ret instruction: pop stack to EIP
• Passing parameters
• Caller pushes onto stack
• Callee accesses as positive offsets from EBP
• Caller pops from stack
61
Summary (cont.)
• Storing local variables
• Callee pushes on stack
• Callee accesses as negative offsets from EBP
• Callee pops from stack
• Handling registers
• Caller saves and restores EAX, ECX, EDX if necessary
• Callee saves and restores EBX, ESI, EDI if necessary
• Returning values
• Callee returns data of integral types and pointers in EAX
62
21