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