CPSC 355: Tutorial 7
Memory & The Stack
PhD Student
Outline
- We've been doing most of our work so far with registers...
- What happens when we run out of registers?
- We need to start thinking about how to use memory to overcome the limitations of using registers alone
Memory
Addresses are 64-bit numbers that reference a particular byte in memory:
0xAA |
0x00 |
0xFE |
. . . |
0x00 |
0x00 |
0x0000000000000000 |
---|
0x0000000000000001 |
0x0000000000000002 |
0xFFFFFFFFFFFFFFFE 0xFFFFFFFFFFFFFFFF |
Memory
Address
Memory
In general, this is a very simplified view of memory
- Clearly we don't have 2^64 bytes = over 16 exabytes of RAM, what happens when we access memory that doesn't exist?
- What about memory that doesn't belong to us? Can we read/write to that memory?
- What about instructions in memory? Should I be able to jump to random locations in memory and execute code there?
- What if I have memory that isn't being used, can I swap it out to disk?
Short answer: Don't worry about these questions for now
Memory
We have a few instructions to read and write to memory
Store Register in Memory:
- str - stores a register in memory
- strh - stores the lower 16-bits of a register in memory, a halfword
- strb - stores the lower 8-bits of a register in memory, a byte
Memory
For example:
0x00000000 |
---|
0x00000001 |
0x00000002 |
0x00000003 |
0x00000004 |
0x00000005 |
0x00000006 |
0x00000007 |
0x00000008 |
Memory
Address
mov x1, wzr
ldr w2, =0xBEEF4DAD
str w2, [x1]
0xAD
0x4D
0xEF
0xBE
But what about?
mov x1, wzr
ldr x2, =0xBEEF4DAD
str x2, [x1]
0x00
0x00
0x00
0x00
Memory
For bytes:
0x00000000 |
---|
0x00000001 |
0x00000002 |
0x00000003 |
0x00000004 |
0x00000005 |
0x00000006 |
0x00000007 |
0x00000008 |
Memory
Address
mov x1, wzr
ldr w2, =0xBEEF4DAD
strb w2, [x1]
0xAD
0x4D
For halfwords
mov x1, wzr
ldr x2, =0xBEEF4DAD
strh x2, [x1]
Memory
Reading can be a little trickier due to the sign bit
Read a register from memory:
- ldr - loads a register from memory
- ldrh - loads the lower 16-bits of a register from memory
- ldrb - loads the lower 8-bits of a register from memory
- ldrsw - loads a word into a register with sign extend
- ldrsh - loads the lower 16-bits of register from memory, with sign extend
- ldrsb - loads the lower 8-bits of a register from memory, with sign extend
Memory
For example:
0x00000000 |
---|
0x00000001 |
0x00000002 |
0x00000003 |
0x00000004 |
0x00000005 |
0x00000006 |
0x00000007 |
0x00000008 |
Memory
Address
mov x1, wzr
ldr x2, [x1]
0xFE
0xFF
0xFF
0xFF
0x00
0x00
0x00
0x00
x2 = 0x00000000FFFFFFFE
= 4294967294
But what about:
mov x1, wzr
ldrsw x2, [x1]
x2 = 0xFFFFFFFFFFFFFFFE
= -2
Memory
Similarly, for half words
0x00000000 |
---|
0x00000001 |
0x00000002 |
0x00000003 |
0x00000004 |
0x00000005 |
0x00000006 |
0x00000007 |
0x00000008 |
Memory
Address
mov x1, wzr
ldrh x2, [x1]
0xFE
0xFF
0xFF
0xFF
0x00
0x00
0x00
0x00
x2 = 0x000000000000FFFE
= 65534
With sign extension
mov x1, wzr
ldrsh x2, [x1]
x2 = 0xFFFFFFFFFFFFFFFE
= -2
Memory
Similarly, for bytes
0x00000000 |
---|
0x00000001 |
0x00000002 |
0x00000003 |
0x00000004 |
0x00000005 |
0x00000006 |
0x00000007 |
0x00000008 |
Memory
Address
mov x1, wzr
ldrb x2, [x1]
0xFE
0xFF
0xFF
0xFF
0x00
0x00
0x00
0x00
x2 = 0x000000000000FFFE
= 65534
With sign extension
mov x1, wzr
ldrsb x2, [x1]
x2 = 0xFFFFFFFFFFFFFFFE
= -2
Memory
Which parts of memory can I use? If you try to compile any run any of the code snippets from before, they'll crash.
Operating system code occupies lower memory, so you shouldn't be able to write to it, which means your process will crash when you do so.
You have options though!
Memory
OS |
Program |
Heap |
Free memory |
Stack |
low
high
The stack is used for local variables, static memory allocation, return addresses. We'll use it for assignment 3.
Heap is for dynamically allocated memory
Memory
OS |
Program |
Heap |
Free memory |
Stack |
low
high
Before we talk about the stack, let's talk about how a program sits in memory
.text |
.data |
other segments |
Programs are broken into segments
Memory
Before we talk about the stack, let's talk about how a program sits in memory
.text |
.data |
other segments |
Programs are broken into segments
.text segment contains code, it's readable and executable, not writeable
.data segment data, it's readable and writeable, not executable
This is for Linux executables, although similar concepts apply for Windows executables
Memory
Before we talk about the stack, let's talk about how a program sits in memory
.text |
.data |
other segments |
.data
temp: .dword 1
.text
ent_num_str: .string "Enter a number:"
ent_exp_str: .string "Enter an exponent:"
inv_exp_str: .string "Invalid exponent"
output_str: .string "%ld^%ld=%ld\n"
scan_str: .string "%ld"
.balign 4
.global main
main:
// code ...
ldr x22, =temp
str x21, [x22]
Remember the exponentiation example from a few tutorials back?
Memory
.data
temp: .dword 0x00000000FFFFFFFE
.text
.balign 4
.global main
main:
stp x29, x30, [sp, -16]! // Save FP and LR to the stack
mov x29, sp // Set FP to the stack addr
ldr x19, =temp // Load the address of the label temp into x19
ldr w20, [x19]
ldrsw x20, [x19]
ldrh w20, [x19]
ldrsh x20, [x19]
ldrb w20, [x19]
ldrsb x20, [x19]
exit: ldp x29, x30, [sp], 16 // Restore the stack
ret // return to OS
Memory
Inspecting memory in gdb
- x/x $x20 - read value at address $x20, display as word
- x/xh $x20 - read value at address $x20, display as half word
- x/xb $x20 - read value at address $x20, display as byte
- Don't use the .data section for assignment 3
- Don't use the heap for assignment 3
- Do use the stack for assignment 3
Advice
This isn't advice, this is a rule
Assignment 3
#include <stdio.h>
#define SIZE 50
int main()
{
int v[SIZE], i=0, j=0, temp=0;
for(i=0; i < SIZE; i++) {
v[i] = rand() & 0xFF;
printf("v[%d]: %d\n", i, v[i]);
}
// sort the array using insertion sort
for(i = 1; i < SIZE; i++) {
temp = v[i];
for(j = i; j > 0 && temp < v[j-1]; j--) {
v[j] = v[j - 1];
}
v[j] = temp;
}
printf("\nSorted array: \n");
for(i = 0; i < SIZE; i++)
printf("v[%d] = %d\n", i, v[i]);
return 0;
}
The Stack
OS |
Program |
Heap |
Free memory |
Stack |
low
high
Used to store local variables and arrays whose sizes are known at run time
local vars, return addrs, frame pointers etc... |
The Stack
high
The register $sp keeps track of what address is currently at the top of the stack.
Free memory |
local vars, return addrs, frame pointers etc... |
$sp
The stack grows upwards as we allocate more data on it (i.e. $sp shrinks)
The Stack
high
We've actually already been using this
Free memory |
local vars, return addrs, frame pointers etc... |
stp x29, x30, [sp, -16]!
Add -16 to $sp before store
Then STore the Pair x29, x30
The Stack
high
We've actually already been using this
Free memory |
local vars, return addrs, frame pointers etc... |
stp x29, x30, [sp, -16]!
$sp
Free memory |
x29 ($fp) |
x30 ($lr) |
local vars, return addrs, frame pointers etc... |
$sp
The Stack
Additionally
ldp x29, x30, [sp], 16
Free memory |
x29 ($fp) |
x30 ($lr) |
local vars, return addrs, frame pointers etc... |
$sp
Free memory |
x29 ($fp) |
x30 ($lr) |
local vars, return addrs, frame pointers etc... |
$sp
x29 and x30 are restored from the stack
add 16 to $sp after load
Next Day
- Frame Pointers
- Creating variables on the stack
CPSC 355: Tutorial 7
By Joshua Horacsek
CPSC 355: Tutorial 7
- 1,649