ABI in Five Minutes¶
New to the series? This is the gentlest door. When you're ready for the full track, start at Part 0 — Product Contract or Part 1 — Foundations.
What you'll learn on this page
- What an ABI is, in one sentence, with no jargon.
- Why an app can crash after a library upgrade even though nobody recompiled it.
- The difference between a change that is safe, risky, or breaking.
No prior knowledge needed. This page is a five-minute on-ramp; every idea here is developed properly later in the series.
ELF/Linux first
Like the rest of this series, the examples use the Linux model
(libfoo.so.1). Windows and macOS have the same problem with different
spellings.
A story: the upgrade that crashed¶
You ship an app. It uses a library called libfoo, so at install time your app
links against libfoo.so.1. Everything works.
Months later, the vendor ships libfoo.so.2. A package update drops the new file
onto the machine. You did not rebuild your app — you didn't even touch it.
The next time it runs, it crashes.
Why? Your app was compiled against the old libfoo. During compilation, the
compiler copied hard facts out of the library's headers and baked them into your
app's machine code: how big a Foo struct is, which slot a function lives in,
which CPU register holds which argument. Those numbers became constants inside
your binary. Nobody re-checks them at runtime.
When libfoo.so.2 changed one of those facts — say, it added a field to Foo,
so Foo is now bigger — your app is still using the old size. It reads and
writes the wrong bytes. The result is a crash, or worse, quietly corrupted data.
That mismatch is an ABI break.
API vs ABI, in one sentence each¶
- An API (Application Programming Interface) is the source-level contract — the function names and types you write against in the headers. Break it and the compiler complains: the build fails, points at a line, you fix it.
- An ABI (Application Binary Interface) is the binary-level contract between already-compiled programs — the exact sizes, layouts, symbol names, and calling conventions. Break it and nobody complains until it crashes at runtime, sometimes not even then.
The whole reason ABI is scary: an API break is loud and immediate; an ABI break is silent and delayed.
A taste: safe, risky, breaking¶
Three changes a library author might make, and what each one does to apps built against the old version:
| Change | What happens to old apps | Verdict |
|---|---|---|
| Add a brand-new function | Nothing — they never called it. | 🟢 safe |
| Make an existing function no longer promise "won't throw" | Still links, but a thrown exception may now escape somewhere the old code didn't expect. | 🟡 risky |
| Add a field to a struct they pass by value | The old size is baked in; they read/write the wrong bytes → crash or corruption. | 🔴 breaking |
That's the entire job of an ABI checker: look at two versions of a library and sort every change into one of those buckets — before you ship the new one.
Where to go next¶
You now have the one idea the whole series builds on: the compiler bakes the library's promises into the caller and never re-checks them.
- Part 0 — Compatibility as a Product Contract — why "is this a break?" depends on what you promised, not just what changed. Start here if you like framing before mechanism.
- Part 1 — Foundations — the build/link/load pipeline, what a symbol is, and where each kind of break happens. Start here if you want the mechanics first.
- ABI Cheat Sheet — the one-screen summary of verdicts and common changes, to keep open while you read.