CS 240 Lab 9: x86 Stack

Peter Mawhorter

Outline

  • Questions (x86 assembly & stack)
  • x86 Stack
  • GO

Write your questions on the boards.

  • x86 Assembly code
  • Stack operations

x86 Stack

Address Calculation

  • Syntax is offset ( base, index, stride ), e.g.: 8(%rdi, %rsi, 4)
    • base and index are registers
    • offset must be number
    • stride is a number (only 1, 2, 4, or 8)
  • The computed address is base + offset + (index × stride)
    • base → pointer
    • offset → fixed offset from pointer
    • index → index within array = variable offset from pointer
    • stride → how big each index is = pointer scaling
  • C code ≈ base[index + offset]

scanf

  • Reads formatted input from the user
  • Format declares what we expect them to type, scanf converts text into other types accordingly
    • Can read characters with %c, integers with %d, floats with %f, and strings with %s.
    • When reading strings, you must* specify the max size.
  • You provide one additional argument per format specifier which is a pointer to write the result into
    • This is how all multi-output functions work in C
  • Return value specifies how many values were converted (or error if it’s negative)

scanf

int x, y;
printf("Enter two numbers separated by a comma: ");
scanf("%d,%d\n", &x, &y);
printf("Their sum is: %d\n", x + y);

Calls Scramble Registers

#include <stdio.h>

int doSomething(int a) {
    int x = a + 8;
    printf("My x is: %d\n", x);
    return x;
}

Explorer link (look at %ebx)

int main(int argc, char** av) {
    int x = argc;
    printf("x is: %d\n", x);
    int y = doSomething(x);
    printf("x is: %d\n", x);
    printf("y is: %d\n", y);
    return x;
}

Calls Scramble Registers

  • Each function uses the same 16 registers
    • Could be 1000s of functions in a program…
    • Using stack to save info is slow…
  • Convention:
    • Clean up after yourself in some cases (callee saved)
      • %rbx, %rsp, %rbp, plus %r12-15
    • Do whatever in other cases (caller saved)
      • Any other register

%rbp

long getAndSumValues() {
    long x;
    printf("Enter an integer: ");
    scanf(" %ld",&x);
    if (x == 0) {
        return 0;
    } else {
        return x + getAndSumValues();
    }
}

Explorer link

%rbp

  • The stack “base” pointer
    • Callee-saved, so we need to save it
    • Points to base (top) of our stack frame
  • Useful for accessing stuff we store on the stack, even when the stack pointer is moving up and down
    • You’ll see things like -4(%rbp) a lot
    • Optimization sometimes avoids it

Stack instructions

  • push → Move stack pointer %rsp down & store value
  • pop → Load value & move stack pointer %rsp up
  • call → Move %rsp down, store return address, and jump
  • ret → Load return address into %rip and move %rsp up
  • leave → Copies %rbp into %rsp and then pops into %rbp
  • sub, mov, etc. (Anything that stores to %rsp directly)

Drawing the Stack

Stack grows downwards

Starting state

A stack diagram showing %rsp set to 0x7fffff8 at the top of the stack, with no values in it yet. The stack is drawn as a series of rectangles stacked on top of each other, each much wider than it is tall. A label indicates that each rectangle represents an 8-byte value. The %rsp value’s position is indicated by a horizontal arrow in the left margin pointing to the upper-left corner of the top rectangle. 

push %rdi  # 0x4353

The same diagram with the value 0x4353 in the top rectangle, and the stack pointer %rsp now has a value of 0x7ffffff0. The arrow for the stack pointers is now pointing at the bottom-left corner of the top rectangle, which is also the top-left corner of the second rectangle. 

Drawing the Stack

push %rsi  # 0xf0

A stack diagram showing %rsp set to 0x7ffffe8 which is the bottom of the 2nd rectangle from the top. The value in that rectangle is 0xf0. 

pop %rax

The same diagram as before, except the stack pointer has moved back up to 0x7ffffff0. The value 0xf0 remains in the second rectangle down which is now below the stack pointer. 

Drawing the Stack

  • Stack grows downwards from top
  • Sometimes grows in multiples of 16 for alignment
  • Represents “notes as we go” that we come back to later
    • When calling a function, the current notes get buried
    • When returning, we go back to our old notes
    • If we didn’t write something down, it may be lost

Drawing the Stack

  • stack frame: region used by one function
    • Allocated by subtracting from %rsp and/or push, etc.
  • What to show:
    • Caller’s frame above
    • Return address in first slot
    • %rsp₀ points to first slot
    • %rsp points to bottom

A stack diagram which starts with three yellow slots at the top labeled “caller’s frame” below which are three blue slots labeled “our frame” and then two white slots labeled “unused area.” The first blue slot holds the “return addr” and is pointed to by “%rsp₀”, while the second and third blue slots hold other values (0x4353 and 0xf0). The last blue slot is pointed to by %rsp. 

Recursion

long getAndSumValues() {
    long x;
    printf("Enter an integer: ");
    scanf(" %ld",&x);
    if (x == 0) {
        return 0;
    } else {
        return x + getAndSumValues();
    }
}

Explorer link

Recursion

getAndSumValues:
        subq    $24, %rsp
        movl    $.LC0, %edi
        movl    $0, %eax
        call    printf
        leaq    8(%rsp), %rsi
        movl    $.LC1, %edi
        movl    $0, %eax
        call    __isoc99_scanf
        cmpq    $0, 8(%rsp)
        je      .L3
        call    getAndSumValues
        addq    8(%rsp), %rax
        jmp     .L2
.L3:
        movl    $0, %eax
.L2:
        addq    $24, %rsp
        ret

Recursion

getAndSumValues:
        subq    $24, %rsp
        #
        #
        #
        leaq    8(%rsp), %rsi
        #
        #
        call    __isoc99_scanf
        #
        #
        call    getAndSumValues
        addq    8(%rsp), %rax
        #
.L3:
        #
.L2:
        addq    $24, %rsp
        ret

Recursion

getAndSumValues:
        subq    $24, %rsp
        #
        #
        #
        leaq    8(%rsp), %rsi
        #
        #
        call    __isoc99_scanf
        cmpq    $0, 8(%rsp)
        je      .L3
        call    getAndSumValues
        addq    8(%rsp), %rax
        jmp     .L2
.L3:
        movl    $0, %eax
.L2:
        addq    $24, %rsp
        ret

Recursion

  • Multiple frames from one function
    • Return addresses may be identical
  • Values pile up on the stack
  • Get used up as we return

(This code has weird alignment)

A stack diagram with three call frames. 

Lab Work

  • More practice for x86 assignment
  • How to start it for real?
    • mv x86 x86-practice to move current x86 out of the way
    • cs240 start x86 to start it
      • Answer “no” when prompted to use existing repository
      • Enter your actual team members
      • Work in the new x86 folder it creates