Section 4: Assembly and the Stack
#
In this section, we will look at more advanced C programs compiled to assembly language, and discuss the layout of the stack segment of memory.
You can find the code for this section here.
You'll want to use your assembly cheat sheet for this section!
Question 1: Mystery Program
#
Let's figure out what C program may have created the assembly code below. (As usual, there are multiple possible C programs.)
Remember from lecture that it is a good idea to work backwards from the ret
instruction.
func:
movl $42, -12(%rsp)
movl $0, -4(%rsp)
movl $0, -8(%rsp)
jmp .L2
.L3:
movl -8(%rsp), %eax
addl %eax, -4(%rsp)
addl $1, -8(%rsp)
.L2:
movl -8(%rsp), %eax
cmpl -12(%rsp), %eax
jl .L3
movl -4(%rsp), %eax
ret
main:
movl $0, %eax
call func
ret
.size main, .-main
.ident "GCC: (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0"
.section .note.GNU-stack,"",@progbits
QUESTION 1A. What is the size of the return value of the func
function? What type of variable might it return?
The return type is a 4-byte value (unsigned int, int, etc.), since the last mention of a register in the %rax
family is %eax
.
QUESTION 1B. What kind of control flow manipulation (loops, conditionals, etc.) could have been used to generate this assembly code?
A loop, since there is a jump back to a label that is above the jump instruction.
QUESTION 1C. What C program could have generated this assembly code? There are multiple possible C programs that could have done this!
The C code that generated the program is below (one of multiple possible programs that result in this assembly code):
#include <stdio.h>
int func() {
int a = 42;
int b = 0;
for (int i = 0; i < a; i++) {
b += i;
}
return b;
}
int main() {
return func();
}
The following problems are extra materials we have prepared that are
relevant to this point in the course, but we don't plan on covering in
section. Depending on how your section goes, we may refer to these
problems if we need extra examples. Otherwise, feel free to use these
as practice problems!
Consider the following C program. We will draw the layout of the stack at different points in the execution, using the assembly code in q2.s
as a reference.
#include <stdio.h>
#include <stdlib.h>
int add(int a, int b) {
int result = a + b;
// *** B ***
return result;
}
int main() {
int result;
long a = 7;
short b = 12;
// *** A ***
result = add(a, b);
// *** C ***
printf("Result of adding %ld and %d is %d.\n", a, b, result);
// *** D ***
return 0;
}
Use the following stack diagram template to help answer the following questions.

QUESTION 2A. What does the call stack look like after the line labeled A is executed (after the local variables are initialized)?
When main
begins executing, but before add
is called, main
allocates some space on the stack for local variables. Since the compiler can re-arrange the order that local variables are pushed onto the stack, we need to look at the assembly code to determine precisely in what order and where these local variables get pushed onto the stack.
In the assembly code, main
first allocates 24
bytes of local variables by subtracting from %rsp
in the sub $24, %rsp
instruction. Then, two local variables are allocated. One of 8 bytes (assembly quad) which contains the value 7, moved into address 8(%rsp)
, and one of 2 bytes (assembly word) which contains the value 12, moved into address 6(%rsp)
.
When these local variables are moved onto the stack, the stack diagram will look like this:

QUESTION 2B. What does the call stack look like after executing the line labeled B (where result
is initialized in add
)? Make sure to include labels and addresses in the stack diagram.
When add
is called, some additional values are pushed onto the stack! The first value added is the return address to main
, which indicates where control should return to after add
is done executing. This 8 byte value is pushed onto the stack and %rsp
is decreased by 8.
However, there are still more values moved onto the stack when calling a function! These additional values are add
's parameters, which get loaded from registers onto the stack (this occurs immeadiately after calling) add
. The first parameter to add
, a
, is stored in the %edi
register. The second parameter to add
, b
, is stored in the %esi
register.
When add
is finally called, it copies the parameters from the registers onto the stack. It first copies the parameter a
in register %edi
to the address -20(%rsp)
. It then copies the parameter b
in register %esi
to a lower address, address -24(%rsp)
.
add
also has its own local variable, the variable result
, so it needs to allocate space for result
. In the corresponding assembly code, add
moves the value in %eax
(the result of the addition) onto the stack into address -4(%rsp)
. This value is then moved from the stack into %eax
when it is returned. Therefore result
must be stored on the stack at -4(%rsp)
.
When these return address, parameters and local variables are moved onto the stack, the stack will look like this:

QUESTION 2C. What does the call stack look like after executing the line labeled C in the diagram (after returning from add
, but before calling printf
)?
After executing line C, the program has returned from add
. Any stack data allocated for add
is now recycled and can be reused for the program. The return value from add
(located in register %eax
) is moved into the local variable result
in main
's stack frame.
Since result
in main
was previously uninitialized, we did not know where it was located on the stack. Now we know that main
is located right at %rsp
. However, this value of %rsp
is not the same as it was when calling add
! This is because when we return from add
, we pop the saved return address of the stack and increase %rsp
.
When we return from add
and move result
onto the stack, the stack will look like this:

QUESTION 2D. What does the call stack look like after executing the line labeled D in the diagram (after returning from printf
, but right before we return from main
)?
After executing printf
, main
has no more instructions to execute, so it begins to recycle its own stack frame. In the assembly code, main
resets %rsp
to its initial value by adding 24 to it. All the local variables on main
's stack frame are recycled and can no longer be used. %rsp
is reset to its original value before main
was called.
This results in our stack looking identical to how it was when main
initially began execution! The stack has cleaned itself up so it looks like main
was never called!
Right before we are about to return from main
, the stack will look like this:

Acknowledgements: The material for Q3 was originally developed for Harvard's CS 61 course. We are grateful to Eddie Kohler for allowing us to use the material for CS 300.