Topic domain
Systems Programming
Close-to-the-metal code and the contracts it depends on.
- Articles
- 80
- Clusters
- 8
- Foundational
- 24
- Intermediate
- 32
- Advanced
- 24
Data layout, linkage, and calling conventions.
- Struct Padding and the Alignment Rule Behind sizeof
- Integer Object Representation and Conversion at the Boundary
- Function Call ABI: Register Assignment on x86-64
- Linkage, Symbol Visibility, and the Weak Definition
- Variadic Calls and the Default Argument Promotions
- Bitfield Allocation and Packed Structures
Unsafe Code
10Invariants the compiler cannot check.
- What the Unsafe Keyword Actually Disables
- Undefined Behaviour as a Compiler Permission
- Pointer Provenance and the Cost of Integer Round-Trips
- Strict Aliasing and the Cases Where Type Punning Is Legal
- Aliasing Rules for References and the Role of UnsafeCell
- Uninitialised Memory, Padding, and the Cost of Assuming Zero
Executables, sections, and parsing.
- ELF Program Headers and the Mapping a Loader Performs Before _start
- Section Headers, String Tables, and the Bookkeeping a Linker Needs
- Relocation Entries and the Arithmetic a Linker Performs
- The PLT and GOT Protocol That Defers Symbol Resolution
- Mach-O Load Commands and the Segments a dyld Image Is Built From
- PE/COFF Relative Virtual Addresses and the Import Directory
Files, sockets, and process control.
- What a File Descriptor Is: An Index Into a Process Table
- Readiness Notification: What select, poll, and epoll Actually Report
- Short Counts: Why read and write Return Less Than Asked
- fork and exec: Which Descriptors Survive and Why
- Exit Status, Zombies, and What waitpid Can Tell a Parent
- Signals: Delivery, Interruption, and the Async-Signal-Safe Set
Targets, sysroots, and emulation.
- Reading a Target Triple as an ABI Contract
- Build, Host, and Target: Three Machines in One Toolchain
- What a Sysroot Actually Restricts in a Cross Build
- Why a Cross Toolchain Bootstraps in a Fixed Order
- User-Mode Emulation and binfmt_misc for Foreign Binaries
- Floating-Point ABI Variants and the Linker Errors They Cause
Atomics, fences, and lock-free design.
- Atomicity and Ordering Are Separate Guarantees in the C Memory Model
- Compare-and-Swap and Why the Retry Loop Must Re-read
- Acquire and Release: What Each Ordering Forbids
- Cache Line Transfer, False Sharing, and the Cost of Contention
- Spinlock Design from Test-and-Set to Ticket Locks
- Seqlocks: Consistent Reads Without Reader-Side Writes
Ownership, leaks, and deterministic cleanup.
- Release Order at Scope Exit and the LIFO Cleanup Rule
- Failure Atomicity and Cleanup on the Error Path
- Move Semantics and the State Left Behind in the Source
- Atomic Reference Counts and the Cycle They Cannot Collect
- Leak Detection by Reachability Rather Than Allocation Count
- File Descriptor Lifetime, Inheritance, and Table Exhaustion
Core dumps, strace, and sanitizers.
- What an ELF Core File Preserves of a Process
- From Page Fault to Signal: How SIGSEGV and SIGBUS Differ
- How strace Sees Every System Call Without Linking Into the Program
- DWARF Line Tables and How an Address Becomes a Source Line
- Stack Unwinding Without Frame Pointers: Call Frame Information
- Build Identifiers and Separate Debug Information
All Systems Programming articles
- Oct 13, 2026 Variadic Calls and the Default Argument Promotions What a va_list actually points at, why float and char arguments are widened before a variadic call, and how the absence of type information turns a wrong format into undefined behaviour.
- Sep 27, 2026 Linkage, Symbol Visibility, and the Weak Definition What the linker actually does with an external declaration, how internal and external linkage differ, and the resolution order that makes weak and common symbols dangerous.
- Sep 24, 2026 Signals: Delivery, Interruption, and the Async-Signal-Safe Set A signal handler runs on the thread it interrupted, with that thread half-finished, which is why only a small set of functions may be called from one. Delivery, masking, and the self-pipe pattern follow from the handler running on an interrupted thread.
- Aug 10, 2026 DWARF Line Tables and How an Address Becomes a Source Line The line-number program is a bytecode that reconstructs a table of address ranges to file, line, and column, and its sequence structure explains why lookups are not simple binary searches.
- Jul 31, 2026 Position-Independent Code, the PLT, and the GOT Why a shared object cannot know its load address, how the global offset table turns an absolute address into a load-time fixup, and what lazy binding defers.
- Jul 24, 2026 Atomic Reference Counts and the Cycle They Cannot Collect The instruction cost of shared ownership, why the count must be atomic, and the reachability problem that makes a pure reference-counting collector leak.
- Jul 23, 2026 What Sanitizers and Miri Cannot Prove Instrumentation detects violations on the paths an execution took, and an interpreter sees only the code it can run, so both produce evidence of bugs rather than evidence of their absence.
- Jun 26, 2026 Stack Unwinding Without Frame Pointers: Call Frame Information How a debugger or profiler recovers caller frames from a stripped, optimised stack by interpreting DWARF CFI rules instead of walking a saved frame pointer chain.
- Jun 3, 2026 DWARF Debug Information: Abbreviation Tables and the Line Number State Machine How debug sections compress a typed tree of entries into a byte stream, why attribute forms matter, and how a line table is reconstructed by running a small virtual machine over its opcodes.
- May 16, 2026 Compare-and-Swap and Why the Retry Loop Must Re-read How a conditional store becomes a universal synchronisation primitive, why weak compare-and-exchange may fail spuriously, and what makes a CAS loop terminate.
- May 9, 2026 User-Mode Emulation and binfmt_misc for Foreign Binaries How a binary for another architecture starts on a host kernel at all, which parts of its execution the emulator rewrites, and the behaviours that stop being faithful once it does.
- Apr 24, 2026 Short Counts: Why read and write Return Less Than Asked A successful read or write may transfer fewer bytes than requested, and the reasons differ by object type. The cases differ by object type, and each needs a loop that distinguishes end-of-file, interruption, and a partial transfer.
- Apr 18, 2026 UNIX Domain Sockets and Passing File Descriptors A socket in the filesystem namespace carries data without a network stack, and its control-message channel can hand a descriptor to another process. Address formats, the credential channel, and what a descriptor transfer actually copies.
- Mar 22, 2026 Remote Debugging Through gdbserver and a Target Sysroot The debugger runs on one machine and the process on another, with only a byte stream between them. Which memory reads the host performs, why the target libraries must exist locally, and what breaks once addresses stop matching.
- Feb 12, 2026 From Page Fault to Signal: How SIGSEGV and SIGBUS Differ The kernel path that turns a bad memory access into a signal, why a missing mapping and an unbacked page produce different signals, and what siginfo tells a handler.
- Jan 29, 2026 Struct Padding and the Alignment Rule Behind sizeof How alignment requirements, member order, and trailing padding determine a structure size, and why reordering members can shrink it without changing any field.
- Jan 19, 2026 What Makes a Safe Wrapper Around Unsafe Code Sound A safe function that can cause undefined behaviour from any call site destroys the guarantee for the entire crate graph, and the obligations that prevent it are structural rather than local.
- Jan 5, 2026 Integer Object Representation and Conversion at the Boundary How signed and unsigned values are stored, what integer promotion silently does to an operand, and which conversions are defined, implementation-defined, or undefined.
- Nov 6, 2025 Ownership Across the FFI Boundary and Who Frees What Two runtimes with two allocators meet at a foreign function call, and every pointer that crosses must have an unambiguous owner, layout, and deallocation routine.
- Oct 28, 2025 Why a Cross Toolchain Bootstraps in a Fixed Order The compiler needs a C library to link, and the C library needs a compiler to build. How a three-stage bootstrap breaks that cycle and which artefacts appear at each stage.
- Oct 17, 2025 mmap and the Page-Fault Path: What a Mapping Really Does A memory mapping installs page-table entries lazily, so the first touch of each page faults into the page cache. The fault path, the shared and private cases, and the failure modes that follow from the split between mapping and page tables.
- Oct 1, 2025 Cross-Compiling Out-of-Tree Kernel Modules A module is linked against a kernel build tree rather than a C library, which changes which compiler builds the tools, what a version magic string enforces, and why a matching configuration matters more than a matching triple.
- Sep 11, 2025 Build, Host, and Target: Three Machines in One Toolchain Why a cross build needs three machine names rather than two, which of them selects the compiler, and how a compiler that runs somewhere other than where it was built gets produced.
- Sep 2, 2025 Progress Guarantees and the Lock-Free Hierarchy The difference between obstruction-free, lock-free, and wait-free, why lock-free does not mean no locks, and how helping makes a single-word operation wait-free.
- Aug 1, 2025 Undefined Behaviour as a Compiler Permission Why the optimiser treats undefined behaviour as a promise about what never happens, and how that promise lets it delete checks, fold comparisons, and reorder loads.
- Jul 24, 2025 Memory Reclamation for Lock-Free Structures Hazard pointers, epoch-based reclamation, and reference counting compared by what they cost on the read path, when memory is actually returned, and which failure they tolerate.
- Jul 16, 2025 What a Sysroot Actually Restricts in a Cross Build A sysroot rewrites some search paths and leaves others untouched. The layout it expects, the linker scripts that depend on it, and the routes by which host headers still reach the compiler.
- Jul 14, 2025 The ABA Problem in Treiber Stacks Why a single compare-and-swap suffices to push onto a Treiber stack but not to pop from it, what the address comparison cannot detect, and which remedies actually close the window.
- Jul 10, 2025 PE/COFF Relative Virtual Addresses and the Import Directory How the Windows loader resolves addresses without a link-time base, what an RVA is relative to, and why the import table is a tree of small structures with no fixed total size.
- Jul 2, 2025 Stack Unwinding and DWARF Call Frame Information How a frame is described by bytecode in .eh_frame, what the unwinder reconstructs at each step, and why a missing unwind table turns an exception into a terminate call.
- Jun 23, 2025 Arena Allocation and the Cost of Releasing Everything at Once Bump allocation, region lifetimes, and why destroying a whole arena is cheaper than freeing its objects, together with the reuse patterns that keep it from growing.
- Jun 13, 2025 Strict Aliasing and the Effective Type of an Object Why two pointers of different types cannot in general name the same memory, which union and character-type accesses are permitted, and what an optimiser does with the rule.
- Jun 8, 2025 Bitfield Allocation and Packed Structures How bit-fields are packed into storage units on the two major ABIs, why the standard leaves the bit order open, and what packed structs cost at every member access.
- May 10, 2025 Build Identifiers and Separate Debug Information How a 160-bit hash in a note section binds a stripped binary to its symbols, and how debuggers find the matching file through debuglink paths and symbol servers.
- May 10, 2025 Symbol Versioning and the Minimum glibc a Binary Requires Versioned symbols record the library release that first defined each function a binary calls. How that floor is written into the file, why it cannot be lowered after linking, and how to check it before shipping.
- Apr 1, 2025 What a File Descriptor Is: An Index Into a Process Table The descriptor is a small integer indexing a per-process array of pointers, and that indirection explains dup, fork, close, and the shared-offset bugs that follow from each.
- Mar 26, 2025 Section Headers, String Tables, and the Bookkeeping a Linker Needs What the section table adds that segments do not carry, how names are resolved through a string table by offset, and why a stripped binary can still run without it.
- Mar 21, 2025 Move Semantics and the State Left Behind in the Source What transferring ownership actually changes in memory, why a moved-from object must still be destructible, and how unique ownership differs from a shallow copy.
- Mar 12, 2025 Finalizers and the Gap Between Reclaimable and Released Why a finalizer runs at an unpredictable time, how resurrection and finalizer ordering break cleanup, and what a disposal protocol adds that collection cannot.
- Feb 11, 2025 Uninitialised Memory, Padding, and the Cost of Assuming Zero Uninitialised bytes are not merely arbitrary values, they are a distinct state the optimiser tracks, and the distinction changes what a byte copy, a comparison, and a length assignment may legally do.
- Jan 17, 2025 The PLT and GOT Protocol That Defers Symbol Resolution How a call to an external function becomes a jump through a writable table, what the resolver stub pushes, and what is given up when binding happens eagerly instead.
- Dec 18, 2024 Function Call ABI: Register Assignment on x86-64 How the System V AMD64 ABI classifies arguments into eightbyte chunks, why some structures travel in registers and others on the stack, and what the callee must preserve.
- Dec 17, 2024 Leak Detection by Reachability Rather Than Allocation Count How heap profilers decide that a block is leaked: root sets, conservative scanning, and why a pointer held in a register or a stale stack slot changes the answer.
- Nov 18, 2024 Readiness Notification: What select, poll, and epoll Actually Report All three interfaces answer the same question about whether an operation would block, but they differ in how much work the kernel does per call and in what a returned descriptor guarantees.
- Nov 11, 2024 Cancellation Points and the Half-Finished Cleanup State How asynchronous cancellation, unwinding, and cooperative cancellation interact with cleanup code, and why a cleanup routine must be reentrant against itself.
- Oct 24, 2024 fork and exec: Which Descriptors Survive and Why Between fork and exec a child inherits every open descriptor, and only a per-entry flag can prevent it. The inheritance rules, the leak they cause, and the atomicity problems that follow from closing descriptors after the fact.
- Oct 22, 2024 Definedness Tracking: What Valgrind Memcheck Knows That Shadow Bytes Do Not Memcheck runs the program on a synthetic CPU and carries a definedness bit per bit of data, catching reads of uninitialised memory that address-based checks cannot see.
- Oct 18, 2024 Parsing Untrusted Binaries Without Trusting Their Length Fields Why every offset in an executable is an attacker-controlled integer, how a bounded cursor converts that into a single failure path, and which structural checks the formats leave to the reader.
- Oct 17, 2024 Configure Tests That Execute Cannot Survive a Cross Build Run-time feature checks answer questions about whichever machine runs them, and caching makes the wrong answers outlive the build. Which tests can be converted, and which must be replaced by a declaration.
- Sep 14, 2024 Aliasing Rules for References and the Role of UnsafeCell A shared reference is a promise of immutability that the optimiser acts on, and UnsafeCell is the single opt-out that makes interior mutability representable without lying to the compiler.
- Sep 2, 2024 Reading a Target Triple as an ABI Contract What each field of a target triple selects, how GCC and Clang disagree about defaults, and why two triples naming the same CPU can produce objects that refuse to link.
- Aug 19, 2024 Pointer Provenance and the Cost of Integer Round-Trips A pointer carries more than an address, and converting it to an integer and back can discard the derivation the optimiser needs to justify its transformations.
- Aug 12, 2024 Strict Aliasing and the Cases Where Type Punning Is Legal The effective type rules permit far more access through differing types than the folklore suggests, and the sanctioned escape hatches are narrower than the compiler flags that disable the analysis.
- Jul 11, 2024 Release Order at Scope Exit and the LIFO Cleanup Rule Why destructors, defer statements, and scope guards run in reverse order of acquisition, and which ordering guarantees survive partial construction and unwinding.
- Jun 29, 2024 pidfd: A Process Handle That Cannot Be Reused A process id is recycled the moment a process is reaped, so any operation addressed by pid can hit an unrelated process. A pidfd refers to the process object itself and closes the race.
- Jun 27, 2024 io_uring: Shared Rings, Submission Entries, and Completion Ordering How a submission queue and a completion queue are mapped into the process, what the two barriers protect, and why a batched interface changes the cost of a system call.
- May 31, 2024 ELF Program Headers and the Mapping a Loader Performs Before _start How PT_LOAD entries describe the bytes the kernel maps, why file offsets and virtual addresses must be congruent modulo the page size, and where the process stack layout comes from.
- May 21, 2024 Inline Assembly Constraints and What a Clobber List Does Not Say A compiler knows nothing about the instructions inside an assembly block, so every register, flag, and memory effect has to be declared, and an undeclared effect is silently miscompiled.
- May 1, 2024 Seqlocks: Consistent Reads Without Reader-Side Writes How an even/odd sequence counter lets readers detect a concurrent update, why the read side must be free of side effects, and what the retry cost looks like under write-heavy load.
- Apr 26, 2024 Acquire and Release: What Each Ordering Forbids The precise prohibitions that acquire and release each impose, why the pair cannot order a store against a later load, and when a standalone fence is the only option.
- Apr 26, 2024 Atomicity and Ordering Are Separate Guarantees in the C Memory Model Why making a shared object atomic removes lost updates but not stale reads, how the C memory model defines a data race, and what happens-before does and does not cover.
- Apr 26, 2024 Relocation Entries and the Arithmetic a Linker Performs How REL and RELA entries encode the patch a linker must apply, why the addend is sometimes stored in the section and sometimes in the entry, and what R_X86_64_RELATIVE actually computes.
- Apr 17, 2024 What the Unsafe Keyword Actually Disables The unsafe marker gates a short, enumerable list of operations rather than switching off the type system, and the obligations it transfers reach past the block that names it.
- Apr 8, 2024 Position-Independent Executables and the Cost of PC-relative Addressing Why a PIE carries relocation tables that a fixed-address executable does not, how RIP-relative instructions encode the same reference, and what the GOT still has to mediate.
- Mar 12, 2024 How strace Sees Every System Call Without Linking Into the Program The ptrace stop protocol behind syscall tracing, how entry and exit are paired from register state, and why tracing multiplies the cost of a syscall-heavy workload.
- Feb 24, 2024 Data Race Detection with Vector Clocks and Happens-Before Order How ThreadSanitizer replaces happens-before with vector clocks per memory access, why the shadow word costs eight bytes per four, and where the ordering it assumes is wrong.
- Feb 8, 2024 Mach-O Load Commands and the Segments a dyld Image Is Built From How the Mach-O header addresses its command list, why __PAGEZERO occupies the low address space, and how chained fixups replaced the older rebase and bind opcode streams.
- Jan 24, 2024 What an ELF Core File Preserves of a Process How the kernel encodes a dead process as an ELF object: loadable segments for memory, note records for registers and mappings, and what the dump deliberately leaves out.
- Jan 19, 2024 Deterministic Replay: Recording a Thread Schedule and Playing It Back What must be captured to reproduce an execution exactly, why recording the interleaving is not enough, and how replay trades observability for the ability to repeat a failure.
- Jan 11, 2024 File Descriptor Lifetime, Inheritance, and Table Exhaustion What a descriptor actually indexes, how the process and system tables share file descriptions, and why closing in the wrong process leaves the resource open.
- Oct 12, 2023 Symbol Versioning and the Two Hash Tables Behind Every Lookup How a symbol name acquires a version suffix at runtime, what SysV and GNU hash tables each optimise for, and why a lookup that succeeds on one distribution can fail on another.
- Aug 30, 2023 Exit Status, Zombies, and What waitpid Can Tell a Parent A terminated process leaves a status word and an entry that must be reaped. Status encoding, the reason the record survives termination, and the four ways a parent can wait without deadlocking.
- Jul 21, 2023 Litmus Tests and Store Buffering How a five-line litmus test distinguishes total store order from sequential consistency, why the x86 store buffer permits a forbidden outcome, and what each outcome costs to rule out.
- May 30, 2023 Cross-Language Calls and the Structure-Passing Contract What a foreign function interface has to agree on beyond the function name, from name mangling and by-value aggregates to error conventions and memory ownership.
- Apr 17, 2023 AddressSanitizer Shadow Memory and the Cost of Byte-Granular Checks How one byte of shadow per eight bytes of address space turns every load and store into a checked access, and what redzones, quarantine, and poisoning can and cannot detect.
- Jan 30, 2023 Cache Line Transfer, False Sharing, and the Cost of Contention Why an atomic operation on an uncontended location is cheap and on a contended one is not, what coherence protocol traffic each attempt generates, and how padding and sharding change the shape of the cost.
- Dec 30, 2022 Spinlock Design from Test-and-Set to Ticket Locks How a test-and-set lock starves its waiters, why test-and-test-and-set helps, and how a ticket lock turns a race into a queue without a fairness guarantee.
- Dec 8, 2022 Ordered Shutdown, Exit Handlers, and What the Kernel Reclaims Why exit handlers run in reverse order with no error reporting, what survives a call to _exit, and how to drain work before the kernel releases descriptors and mappings.
- Nov 30, 2022 Failure Atomicity and Cleanup on the Error Path How partial construction is made safe: commit-or-rollback discipline, errdefer-style compensation, and the invariants a constructor must restore before returning an error.
- Jul 28, 2022 Floating-Point ABI Variants and the Linker Errors They Cause Soft, softfp, and hard float on 32-bit ARM decide where arguments travel rather than what arithmetic runs, and only the static linker notices when a build mixes them.