Part IV: The Assembly-C Interface

Assembly Doesn't Live in Isolation

Every assembly program you've written in this book has lived in a clean void: pure assembly, no dependencies, system calls only. That's the right way to learn. But it's not how assembly is actually used in the real world.

In practice, assembly lives with C.

The hand-optimized cryptographic routine that processes 100GB of TLS traffic lives in a .s file, but it's called from C code in OpenSSL. The spinlock in the Linux kernel that protects the scheduler's runqueue is ARM64 assembly called from C. The SIMD-accelerated matrix multiplication in PyTorch is written in C++ with inline assembly inserts. The OS kernel project you've been building (MinOS) will eventually need to call C library functions or be called by C code.

This is the chapter where assembly becomes genuinely useful rather than an exercise.

The ABI as the Contract

The Application Binary Interface (ABI) is the contract between compiled code components. It specifies:

  • Which registers hold arguments, and in what order
  • Where the return value goes
  • Which registers must be preserved across function calls (callee-saved)
  • How the stack must be aligned
  • How structs are laid out in memory and passed between functions
  • How the dynamic linker resolves symbols at runtime

The ABI is not optional. Violate it in one function and you silently corrupt the state of another function's stack frame. The resulting bug will manifest 100 calls later with a segfault that has nothing apparent to do with your assembly. The ABI is the law; assembly is how you speak that law directly.

What Part IV Covers

Chapter 20 — The assembly-C interface itself: calling C functions (printf, malloc, fopen) from assembly; writing assembly functions callable from C; passing structs; the red zone; variadic functions. A complete working mixed project.

Chapter 21 — Reading compiler output: how to use gcc -S, Compiler Explorer (godbolt.org), and AT&T vs. Intel syntax. What -O0, -O1, -O2, -O3 do to your code. The patterns to recognize: function prologue, local variable layout, if-else, loops, switch tables, virtual dispatch.

Chapter 22 — Inline assembly: GCC extended syntax, output/input/clobber constraints, and when to use inline assembly (CPUID, RDTSC, atomics, I/O ports). When NOT to use it (compiler intrinsics are usually better). Common mistakes.

Chapter 23 — Linking, loading, and ELF: how source becomes an executable; ELF sections and segments; the linker's job (symbol resolution + relocation); static vs. dynamic linking; the loader's job; linker scripts for bare-metal code (the MinOS connection).

Chapter 24 — Dynamic linking in depth: the PLT/GOT mechanism traced to machine code; lazy binding; RELRO; LD_PRELOAD for interposition; dlopen/dlsym for runtime loading; GOT overwrite security implications (preview of Chapter 36).

Why This Matters for Security Research

Understanding the assembly-C interface is prerequisite for exploit development. Buffer overflows (Chapter 35-37) exploit the stack frame layout. ROP chains depend on knowing the calling convention. PLT/GOT overwrite attacks (Chapter 36) require understanding dynamic linking at the instruction level. LD_PRELOAD is both a debugging tool and a privilege escalation vector.

You can't exploit what you don't understand. Part IV builds the understanding.

Chapters in This Part