Chapter 20 Exercises: Calling C from Assembly and Assembly from C
Exercise 1: Calling printf — Argument Setup
For each printf call, write the x86-64 NASM assembly to set up the arguments and call printf. You do not need to write full functions — just the setup + call.
a) printf("Hello, World!\n");
b) printf("Count: %d\n", 42);
c) printf("%s = %d\n", "answer", 42); (two non-format args)
d) printf("Hex: 0x%016lX\n", 0xDEADBEEFCAFEBABEULL);
e) printf("Float: %.4f\n", 3.14159); (floating-point — set AL correctly!)
f) printf("%d + %d = %d\n", 10, 20, 30); (three integer args)
For each, state: which registers are used for each argument, and what is the value of AL?
Exercise 2: malloc + free
Write an assembly function alloc_and_init(size_t size, uint8_t init_val) that:
1. Calls malloc(size)
2. If malloc returns NULL, return NULL
3. Fills all size bytes with init_val
4. Returns the pointer
Use callee-saved registers appropriately. Which registers will you use and why?
Exercise 3: ABI Compliance Check
The following assembly function has 3 ABI violations. Identify each one:
; Claimed to implement: int64_t multiply(int64_t a, int64_t b)
multiply_buggy:
push rbp
mov rbp, rsp
mov r12, rdi ; a
mov r13, rsi ; b (oops — not saving/restoring R12, R13)
imul r12, r13 ; product in R12
; Violation 1: ???
; Violation 2: ???
; ... do some other work ...
mov rax, r12
mov rsp, rbp ; restore RSP from RBP
pop rbp
; Violation 3: ???
ret
Rewrite the function correctly.
Exercise 4: Struct Passing
Given this C struct and function:
struct RGB { uint8_t r, g, b, padding; }; // 4 bytes total
uint32_t rgb_to_packed(struct RGB color);
a) How is struct RGB passed to rgb_to_packed? (Which register, and which bytes?)
b) Write NASM assembly to call rgb_to_packed({255, 128, 0, 0}) from assembly.
c) Where will the return value be?
Exercise 5: Large Struct Return
struct Matrix4x4 { double m[16]; }; // 16 * 8 = 128 bytes
struct Matrix4x4 identity_matrix(void);
a) Is this struct passed in registers? Why or why not?
b) Write assembly to call identity_matrix() and store the result. Show how to allocate the return buffer and pass the hidden pointer.
c) After the call, how do you access m[0] and m[15] of the returned matrix?
Exercise 6: Red Zone
a) Write a leaf function fast_max(int64_t a, int64_t b) that uses the red zone to store a temporary value (even if not strictly necessary — as practice).
b) What would happen if you called another function from within fast_max while using the red zone?
c) Why is the red zone prohibited in kernel code?
d) The red zone is 128 bytes. If a leaf function needs 200 bytes of local storage, what must it do?
Exercise 7: Variadic Function Analysis
For each variadic call, determine the value of AL and which registers hold which arguments:
a) printf("%d %d %d", 1, 2, 3) — 3 integers
b) printf("%f %f", 1.0, 2.0) — 2 doubles
c) printf("%d %f %d %f", 1, 2.0, 3, 4.0) — mixed int and double
d) printf("%s %d %f %s", "a", 42, 3.14, "b") — mixed types
Exercise 8: Calling puts and fgets
a) Write assembly to call puts("Hello, puts!"); (puts adds a newline automatically)
b) Write assembly to call fgets(buffer, 100, stdin) where buffer is a 100-byte stack-allocated array. Show how to allocate the buffer and pass stdin (the C global FILE*).
For (b), to access stdin, use:
extern stdin
; ...
mov rdi, buffer_ptr
mov rsi, 100
mov rdx, [rel stdin] ; load the FILE* pointer
call fgets
Exercise 9: C++ extern "C"
a) Why does C++ mangle function names? Give an example of what void foo(int x, double y) might mangle to.
b) What does extern "C" { void foo(int x, double y); } do in a C++ header?
c) If you have an assembly function fast_sort declared as global fast_sort in NASM, what declaration do you need in a C++ file to call it?
d) If a C++ class method void Widget::update(int x) is mangled to _ZN6Widget6updateEi, how would you call it from assembly?
Exercise 10: The Complete Mixed Project
Extend the complete project from the chapter (asm_strlen, asm_checksum, asm_strcmp) by adding:
a) int asm_toupper(char c) — returns uppercase version of ASCII character (capitalize if lowercase, else return unchanged)
b) void asm_str_toupper(char *s) — convert entire string to uppercase in place (calls asm_toupper or does it inline)
c) int asm_count_char(const char *s, char c) — count occurrences of character c in string s
Write the NASM implementation and C declarations for each function. Include test calls in main.c.
Exercise 11: Calling fopen, fwrite, fclose
Write a complete assembly function write_log_entry(const char *filename, const char *message) that:
1. Opens filename for append ("a" mode)
2. If open fails (returns NULL), returns -1
3. Writes message to the file
4. Closes the file
5. Returns 0 on success
Use:
- fopen(filename, "a") → FILE*
- fwrite(message, 1, strlen(message), fp) → bytes written
- fclose(fp) → 0 on success
Exercise 12: ABI in Practice — Callee-Saved Register Audit
The following function uses several registers. Identify which ones must be saved and restored in a proper function prologue/epilogue per System V AMD64:
complex_function:
; Uses: RAX, RCX, RDX, RSI, RDI, R8, R9, R10, R11, R12, R13, RBX, RBP
; (all of the above are used at some point in this function)
; This function also calls another function.
a) Which registers are caller-saved (C can trash them)? b) Which registers must this function save and restore? c) What is the minimal save/restore set if we use only RAX, RBX, and R12 in the body?
Exercise 13: Stack Frame Diagram
Draw the complete stack frame (showing all stored values and their offsets from RBP) for a function with: - Standard prologue (push rbp; mov rbp, rsp) - Local variables: 2 × int64_t (16 bytes) - Saves RBX, R12, R13 (3 × 8 = 24 bytes) - Stack alignment padding (if needed)
Show the final RSP value and the stack layout from RSP to RBP + 8.
Exercise 14: GDB Debugging Mixed Code
Given the functions from the chapter, describe the GDB commands needed to:
a) Set a breakpoint at asm_strlen
b) After hitting the breakpoint, print the string argument
c) Step through the assembly one instruction at a time
d) Check the return value after RET
e) Verify RBX is unchanged after a function that saves/restores it
Exercise 15: ARM64 Interface (Bonus)
The AAPCS64 (ARM64 calling convention) differs from System V AMD64. Port the asm_strlen function from this chapter to ARM64 NASM/GNU AS format callable from C:
a) What register holds the string pointer argument in AAPCS64?
b) What register holds the return value?
c) Write the ARM64 version of asm_strlen
d) Is extern "C" still needed for ARM64 C++ interoperability?