What Is a Linker?
A linker is a specialized tool that takes one or more object files (produced by compilers or assemblers) and combines them into a single executable program.
Think of a linker as the final assembler of a jigsaw puzzle — it connects all pieces (functions, variables, libraries) into a coherent whole.
Linkers are essential in:
- Multi-file projects
- Modular development
- Library integration
- System-level programming
1. Compilation and Linking Workflow
Here’s how a typical C program becomes an executable:
main.c → [Compiler] → main.o
utils.c → [Compiler] → utils.o
[Linker] → main.o + utils.o + stdlib → main.exe
In many languages (like C or C++), compilation and linking are distinct phases.
2. Role of the Linker
The linker is responsible for:
| Task | Description |
|---|---|
| Symbol Resolution | Maps function/variable names to memory addresses |
| Relocation | Adjusts addresses of code/data to fit final memory layout |
| Library Linking | Integrates standard or user-defined libraries |
| Entry Point Setup | Defines where the program starts (main in C/C++) |
| Executable Output Generation | Produces .exe, .out, or .elf binaries |
3. Types of Linkers
| Type | Description |
|---|---|
| Static Linker | Merges all code into one final executable (no external dependencies) |
| Dynamic Linker | Leaves external references unresolved and binds them at runtime |
| Incremental Linker | Re-links only modified parts of large projects |
| Link-Time Optimizer (LTO) | Performs whole-program optimization during linking |
4. Static Linking
In static linking:
- All required code (from object files and libraries) is copied into the final binary.
- Produces self-contained executables.
Pros:
- No runtime dependencies
- Faster startup
Cons:
- Larger file sizes
- Harder to update (must recompile to patch)
5. Dynamic Linking
In dynamic linking:
- External functions are not embedded, but referenced in external shared libraries (e.g.,
.dll,.so,.dylib) - Linking occurs at runtime
Pros:
- Smaller binaries
- Shared memory between programs
- Easier updates (just replace the shared library)
Cons:
- Requires external files at runtime
- Slightly slower startup
6. Symbol Resolution: How Names Become Addresses
During linking, symbolic names (like printf, main, global_var) are resolved into memory addresses.
Example:
In main.o:
extern int square(int);
In math.o:
int square(int x) {
return x * x;
}
The linker:
- Finds
squareinmath.o - Replaces the placeholder in
main.owith the correct address - Ensures both modules reference the same function
7. Relocation: Adjusting for Final Memory Layout
Each object file assumes it starts at address 0, which obviously isn’t possible in a multi-module program.
The linker:
- Moves functions/data to proper addresses in memory
- Updates all internal address references
This relocation step ensures your function calls and memory access work even when addresses change.
8. Linker Script
In low-level or embedded systems, developers may provide a linker script to control:
- Code and data placement
- Stack and heap configuration
- Memory segments and alignment
Example (GNU LD):
SECTIONS {
.text : { *(.text) }
.data : { *(.data) }
.bss : { *(.bss) }
}
This level of control is crucial in firmware, operating systems, and real-time systems.
9. Link-Time Optimization (LTO)
Modern compilers (like GCC and Clang) support LTO, which allows the compiler to defer optimization to the linker stage.
Advantages:
- Inlining across modules
- Dead code elimination at global scope
- Better call graph analysis
This is crucial for performance-critical software like:
- Operating systems
- Games
- Financial systems
10. Errors During Linking
Linking is a common stage where many build errors occur.
| Error Type | Example | Cause |
|---|---|---|
| Undefined reference | undefined reference to 'foo' | Missing object file or library |
| Multiple definitions | multiple definition of 'main' | Same symbol defined in multiple files |
| Mismatched types | conflicting types for 'bar' | Function declarations don’t match |
Fixing these often requires inspecting:
- Compiler flags (
-c,-Wall, etc.) - Linker inputs (
-lmath,-lm, etc.) - File inclusion order
11. Common Linkers and Tools
| Linker Tool | Platform | Notes |
|---|---|---|
| GNU ld | Unix/Linux | Most widely used linker |
| Gold | Google’s faster replacement for GNU ld | |
| LLD | LLVM’s modern linker (cross-platform) | |
| MSVC Linker | Microsoft toolchain for Windows | |
| ld.bfd | GNU binutils default linker |
12. Linker Flags and Options
Typical compiler commands invoke the linker implicitly:
gcc main.o utils.o -o program -lm
-o program: Output executable name-lm: Link the math library (libm.soorlibm.a)-L: Add library search path-static: Force static linking-shared: Create a shared object
13. Real-World Use Case: Combining C and Assembly
; file: asm_func.asm
global my_asm_add
my_asm_add:
add eax, ebx
ret
// file: main.c
extern int my_asm_add(int, int);
int main() {
return my_asm_add(5, 7);
}
Build process:
nasm -f elf32 asm_func.asm
gcc -m32 main.c asm_func.o -o program
The linker combines C and assembly via symbol resolution and relocation.
Summary
A linker is the unsung hero of software development — silently stitching together code fragments, libraries, and binaries into fully-formed executables. It ensures that all the functions you write (or borrow) are properly located, aligned, and callable.
Without a linker, your source code is just a pile of puzzle pieces — the linker completes the picture.
Related Keywords
- Object File
- Compiler
- Assembler
- Static Linking
- Dynamic Linking
- Shared Library
- Relocation
- Symbol Table
- Undefined Reference
- Linker Script
- Executable File
- Binary Format (ELF, PE, Mach-O)
- LTO (Link-Time Optimization)
- GNU ld
- GCC
- Cross-Linking
- Memory Segments
- Address Resolution
- Build System









