Linkers, Loaders & Object Formats

symbol resolution

A program is usually built from many object files and libraries, each one mentioning names — calling foo(), reading a global config, and so on. Symbol resolution is the matchmaking step where the linker takes every use of a name (every undefined reference) and pairs it with exactly one definition somewhere in the inputs. When it succeeds, every call and every variable access points at a real address; when it fails, you get the familiar 'undefined reference' or 'multiple definition' errors.

The linker walks its inputs and keeps a table of symbols. For each undefined GLOBAL symbol it must find one, and only one, strong definition. The guiding principle is the one-definition rule: across the whole link there should be exactly one strong definition of each global name. Weak definitions soften this — if there is no strong definition, a weak one is used; if there is a strong one, it wins and weaks are ignored. Archive (.a) libraries add a wrinkle: a static library is a bag of .o files, and the linker pulls in only those members that satisfy a still-unresolved symbol, processing inputs left to right, which is why link order on the command line can matter (a library must usually appear after the objects that use it). Dynamic libraries defer some of this: undefined symbols that will be supplied by a shared object are recorded and resolved later by the dynamic linker at load time.

It matters because resolution is where 'separate compilation' is finally stitched together into one coherent program — and where a large fraction of build failures live. A precise mental model: 'undefined reference to foo' means resolution found zero definitions for foo; 'multiple definition of foo' means it found two or more strong ones. A subtle real-world hazard is the One Definition Rule in C++ being violated silently when two translation units define the same inline entity differently — the linker may pick one arbitrarily, producing a program that builds but misbehaves.

$ gcc main.o -lm # works: -lm comes AFTER main.o $ gcc -lm main.o # may fail: by the time -lm is seen, # main.o's use of sqrt is not yet recorded # error: undefined reference to `sqrt'

For static libraries, order matters: the linker only pulls in archive members that satisfy symbols it has already seen as undefined.

'undefined reference' means zero definitions were found; 'multiple definition' means two or more strong ones were. A weak definition does not trigger 'multiple definition' — it just loses to any strong one, silently.

Also called
one-definition rulename resolution單一定義規則