CMSC216 Lab08: Caller / Callee Register Usage
- Due: 11:59pm Mon 28-Oct-2024 on Gradescope
- Approximately 1.00% of total grade
CODE DISTRIBUTION: lab08-code.zip
CHANGELOG: Empty
Table of Contents
1 Rationale
Processor registers are shared among all programs and functions within individual programs. Registers on most modern processor architectures are divided between Caller and Callee save registers according to the Operating System Application Binary Interfaces (ABI). This set of rules dictates which registers may change during a function call and which will remain stable across the call. These rules are adhered to by compilers but must be followed "by hand" when writing assembly code.
This exercise demonstrates several common mistakes associated with Caller/Calee Save registers. Completing the exercise will raise awareness of how to abide by the ABI and how to save/restore Callee save registers when they are needed.
Associated Reading / Preparation
Bryant and O'Hallaron Ch 3.7 on assembly procedure call conventions in x86-64. Specifically, Ch 3.7.5 discusses caller/callee save conventions and the code associated with them.
Grading Policy
Credit for this exercise is earned by completing the code/asnwers here
and submitting a Zip of the work to Gradescope. Students are
responsible to check that the results produced locally via make test
are reflected on Gradescope after submitting their completed
Zip. Successful completion earns 1 Engagement Point.
Lab Exercises are open resource/open collaboration and students are encouraged to cooperate on labs. Students may submit work as groups of up to 5 to Gradescope: one person submits then adds the names of their group members to the submission.
See the full policies in the course syllabus.
2 Codepack
The codepack for the lab contains the following files:
File | Description | |
---|---|---|
QUESTIONS.txt |
EDIT | Questions to answer: fill in the multiple choice selections in this file. |
add2strs_asm_a.s |
Study | A version, broken code to study to understand errors |
add2strs_asm_b.s |
Study | B version, broken code to study to understand errors |
add2strs_asm_c.s |
EDIT | C version, complete this version with correct code |
add2strs_reference.c |
Provided | C reference implementation of assembly code to write |
add2strs_main.c |
Provided | Main and utility function calling assembly function |
add2strs_clobber_asm.s |
Provided | Assembly code used to ensure reliable behavior across compiler versions |
Makefile |
Build | Enables make test and make zip |
QUESTIONS.txt.bk |
Backup | Backup copy of the original file to help revert if needed |
QUESTIONS.md5 |
Testing | Checksum for answers in questions file |
test_quiz_filter |
Testing | Filter to extract answers from Questions file, used in testing |
test_lab08.org |
Testing | Tests for this exercise |
testy |
Testing | Test running scripts |
3 Register Reference
This lab deals with the General Purpose Registers and the division between Caller / Calee save registers. The diagram below color codes registers according to which category they are in.
4 QUESTIONS.txt File Contents
Below are the contents of the QUESTIONS.txt
file for the exercise.
Follow the instructions in it to complete the QUIZ and CODE questions
for the exercise.
_________________ LAB08 QUESTIONS _________________ Exercise Instructions ===================== Follow the instructions below to experiment with topics related to this exercise. - For sections marked QUIZ, fill in an (X) for the appropriate response in this file. Use the command `make test-quiz' to see if all of your answers are correct. - For sections marked CODE, complete the code indicated. Use the command `make test-code' to check if your code is complete. - DO NOT CHANGE any parts of this file except the QUIZ sections as it may interfere with the tests otherwise. - If your `QUESTIONS.txt' file seems corrupted, restore it by copying over the `QUESTIONS.txt.bk' backup file. - When you complete the exercises, check your answers with `make test' and if all is well, create a zip file with `make zip' and upload it to Gradescope. Ensure that the Autograder there reflects your local results. - IF YOU WORK IN A GROUP only one member needs to submit and then add the names of their group. Code Overview ============= Survey the provided code and examine the following source files. ------------------------------------------------------------------------------------------------ FILE Description ------------------------------------------------------------------------------------------------ add2strs_main.c A main() and convert() function in C; these are used with the add2strs() add2strs_asm_a.s A broken version of the add2strs() function in assembly for study add2strs_asm_b.s A broken version of the add2strs() function in assembly for study add2strs_asm_c.s An empty version of add2strs() that needs to be Edited add2strs_reference.c A reference C implementation of the add2strs() function ------------------------------------------------------------------------------------------------ The primary objective of the lab is to learn about Caller/Callee register conventions and complete a working version of the function `add2strs()' in the file `add2strs_asm_c.s' which adheres to caller/callee register conventions. QUIZ add2strs_asm_a.s ===================== 1 ~ Type `make' which will build several executables based on different combinations of the source files ,---- | >> make | gcc -Wall -Werror -g -o add2strs_main_a add2strs_asm_a.s add2strs_main.c add2strs_clobber_asm.s | gcc -Wall -Werror -g -o add2strs_main_b add2strs_asm_b.s add2strs_main.c add2strs_clobber_asm.s | gcc -Wall -Werror -g -o add2strs_main_c add2strs_asm_c.s add2strs_main.c add2strs_clobber_asm.s `---- Note that there are 3 executables built based on the a/b/c versions of assembly files. Run the "A" version of the main program. NOTE: If you aren't sure how to run a an executable, now would be a great time to talk with a staff member about what the output of the `make' command above and which parts of it show the executables that are produced and how to run them. What is the result of running the A version of the program? - ( ) A segmentation fault occurs while the program runs - ( ) The program runs but prints the error message "Unable to convert string to number" - ( ) The program runs normally but produces obviously incorrect output - ( ) The program runs correctly to completion but returns a non-zero exit code 2 ~ To get more insight on what is happening, run the A version of the program under Valgrind to print messages about any memory errors occur. What does Valgrind report about the behavior of the program? - ( ) The `main()' function has a bad memory reference likely due to a faulty return value from the `addstrs()' function. - ( ) There is a bad memory reference by the assembly `addstrs()' function which is called from the C `main()' function - ( ) There is a bad memory reference during the C `convert()' function that is called by the assembly `addstrs()' function - ( ) Trick question: there are no memory problems reported by Valgrind 3 ~ Use GDB to step through the execution of `add2strs()' A version. Use the `nexti' command to step line by line through the function but step over any function calls: `convert()' is written in C so there is no reason to expect it is faulty. Look for obviously wrong assumptions in the code for `add2strs()' according to the comments given. Which of the following best summarizes the mistakes made in the A version which lead to its wrong behavior? - ( ) The stack is not aligned properly for the function call to succeed - ( ) The stack is not properly restored at the end of the function which causes problems on the return - ( ) Data is not properly loaded into argument registers for the first call to the `convert()' function - ( ) Data such as pointers are stored in Caller save registers that change when the `convert()' function is called QUIZ add2strs_asm_b.s ===================== 1 ~ Examine the B version of the assembly code in `add2strs_asm_b.s'. Which of the following best describes the different approach taken in the B version compared to the A version. - ( ) Callee Save Registers like %rbp and %rbx are used to preserve needed data across the call to `convert()' which clobbers Caller Save Registers - ( ) Adjustments are made to the how the argument registers are loaded for the first call to `convert()' so that the function runs correctly. - ( ) The stack is aligned differently for function calls in this version which is more correct than the alignment used in the A version. - ( ) Callee registers that are used are saved and then restored before returning from the function. 2 ~ Run the B version of the program which uses this version of the assembly code. Run the program under Valgrind as well. Which of the following best describe the results? - ( ) A segmentation fault still occurs but it happens on the second call to `convert()' during `add2strs()' - ( ) A segmentation fault still occurs but it happens during the `main()' function. - ( ) The program runs but it produces incorrect results and Valgrind reports a "Conditional move/jump depends on uninitialized data" - ( ) The program runs and produces incorrect results but Valgrind does not report any errors. 3 ~ Run the B version of the program under GDB. Set a breakpoint in `add2strs()' and step through the assembly noting its behavior. Again, use the nexti command to step over calls to `convert()'. Continue stepping through the return from `add2strs()' which will land back in `main()'. When the debugger shows the C code for `main()', change its display to the assembly instructions for that C code using the GDB command `layout asm'. Which of the following best describes the instructions that appear in `main()' immediately after `call add2strs'? - ( ) These instructions make use of the stack pointer `%rsp' which was not properly restored by `add2strs()' which will create problem for `main()' - ( ) These instructions use a Caller Save register like `%rdi' which was altered by `add2strs()' but not restored which will create problems for `main()' - ( ) These instructions use a Callee Save register like `%rbp' which was altered by `add2strs()' but not restored which will create problems for `main()' - ( ) These instructions perform a buffer overflow check and due to one occurring in `add2strs()', problems are created for `main()'. CODE add2strs_asm_c.s ===================== Fill in a completely correct definition for the `add2strs()' function in the file `add2strs_asm_c.s' which is currently mostly blank. Base your code on the B version but correct the problems you identified in the with that version. Some useful instructions for this purpose are noted below. ----------------------------------------------------------------------- INSTRUCTION EFFECT ----------------------------------------------------------------------- pushq %rxy Extends the stack and places the current 8-byte value of register %rxy in the stack to "save" the register pop %rxy Copies the 8-byte value pointed at by the Stack Pointer into register %rxy "restoring" it then shrinks the stack ----------------------------------------------------------------------- REMEMBER: For function calls to be compliant with the x86-64 standard, the Stack Pointer must be divisible by 16. Functions that call other functions typically expand the stack by the following number of bytes with a combination of `pushq / subq' instructions. ------------------------------------------------------------------------------------------------ PUSHQ / SUBQ GROWTH RETURN ADDRESSS TOTAL STACK GROWTH EXAMPLES ------------------------------------------------------------------------------------------------ 8 bytes 8 bytes 16 bytes subq $8,%rsp OR pushq %rbx 24 bytes 8 bytes 32 bytes pushq %rbx; pushq %rbp; subq $8,%rsp 40 bytes 8 bytes 48 bytes pushq %rbp; subq $32,%rsp ------------------------------------------------------------------------------------------------ FINAL NOTE: When writing longer assembly functions, one may "run out" of Caller Save registers. Even if there are no function calls, it is still common practice to push/pop Callee Save registers to allow their use during these longer functions. Just make sure to push/pop in corresponding order to respect stack semantics: ,---- | longfunc: | pushq %reg1 # callee save regs like rbx, rbp, r15 | pushq %reg2 | pushq %reg3 | ... | ... # code that needs to use callee save reg1,reg2,reg3 | | popq %reg3 # last in, first out stack semantics | popq %reg2 | popq %reg1 | ret `----
5 Submission
Follow the instructions at the end of Lab01 if you need a refresher on how to upload your completed lab zip to Gradescope.