Before you can read the parent note without tripping, you must own about ten little words:
source file, compiler, object file, symbol, defined vs undefined, relocation,
archive, symbol index, linker, executable. We'll define each in plain words, draw the
picture it stands for, and say why the topic can't live without it. Each one leans on the one
before, so read top to bottom.
Picture it as a recipe card written in English-ish words. The computer cannot run it directly;
it's just letters. That's the whole reason the next tool exists.
Look at the figure: the recipe card (add.c, black) goes into the translator box and out comes
a strip of numbers (the object file, red) — the same idea expressed in a language the chip
can execute.
Why "incomplete"? Imagine add.c calls a function helper() that lives in a different file.
When the compiler translates add.c alone, it has no idea what numeric address helper will
end up at. So it writes a placeholder: "call the function at ▢ — fill in later." That blank is the
key to everything.
In the figure, one object file has two lists. On the left, names it defines (its body exists
here, marked with T = text/code). On the right, names it uses but does not define (marked
U = undefined) — those are the ▢ blanks from step 3, each labelled with the name it's waiting
for. The red add is the symbol we'll trace through the whole topic.
This exact T / U distinction is what nm libmath.a prints in the parent's Example 2. Now you
know what those letters mean before you ever see them.
Picture a shoebox. You drop add.o, sub.o, mul.o into it and tape it shut as libmath.a.
Nothing is combined or changed — each .o sits inside, whole, retrievable one at a time.
Without the index, the linker would have to open every .o in the box and scan it to find who
defines add. With the index, it looks up add once and grabs the right member directly — like a
book's index versus flipping through every page.
Follow the figure left to right — this is the linker's single pass. It starts with main.o,
which needsadd (red, added to a "wanted" list). It then reaches the box libmath.a, looks
add up in the index, pulls onlyadd.o out, and copies its code into the growing program.
sub.o, which nobody wanted, is left in the box. That selective grab is the "Selectivity"
goal from the parent note.
Once every U is matched and every blank filled, no requests remain unanswered → the result is a
complete executable, a file the operating system can actually run. Because add's code was
copied in, you can now delete libmath.a and the program still runs — the parent's "static
guarantee."
Read it as a flow: text becomes an object file, which exposes symbols and goes into an archive;
the index plus the symbols let the linker resolve names; relocation finishes the job — and all of
that is the static-library topic.
Related build-system context worth peeking at once these click:
Make and build dependency graphs · Compilation pipeline — preprocess, compile, assemble, link.
And the Hinglish twin of the parent: 5.3.03 Static libraries — .a - .lib, creation and linking (Hinglish).