Memory & The Stack
PhD Student
Addresses are 64-bit numbers that reference a particular byte in memory:
0xAA |
0x00 |
0xFE |
. . . |
0x00 |
0x00 |
0x0000000000000000 |
---|
0x0000000000000001 |
0x0000000000000002 |
0xFFFFFFFFFFFFFFFE 0xFFFFFFFFFFFFFFFF |
Memory
Address
In general, this is a very simplified view of memory
Short answer: Don't worry about these questions for now
We have a few instructions to read and write to memory
Store Register in 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
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]
Reading can be a little trickier due to the sign bit
Read a register from 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
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
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
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!
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
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
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
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?
.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
Inspecting memory in gdb
Advice
This isn't advice, this is a rule
#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;
}
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... |
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)
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
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
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