Reading — step 1 of 5
Read
~1 min readOptimizations & Linking
Assembly, Linking, Loading
After codegen produces .s assembly:
Assembling (.s → .o):
- Parse asm → encode machine code.
- Resolve label addresses (within file).
- Emit ELF (Linux), Mach-O (Mac), COFF (Windows) object files.
- Done by
as(GNU assembler) or LLVM's integrated assembler.
Object file structure (ELF):
- Header.
- Section table: .text (code), .data (initialized), .bss (uninitialized), .rodata (constants).
- Symbol table: which names are defined/external.
- Relocation entries: where to patch addresses (e.g.,
call printf— printf's address unknown until link).
Linking (.o + libraries → executable):
- Resolve external references (call printf → libc).
- Combine sections from multiple .o files.
- Apply relocations (fill in addresses).
- Output: executable (also ELF).
Static linking: copy library code into executable. Big binaries, no runtime dependency. Dynamic linking: leave external references; resolved at load time.
foo.c → foo.o ─┐
├─→ ld → executable
bar.c → bar.o ─┤
│
libc.a / libc.so ─┘
Loading (executable → process):
- OS reads ELF header.
- mmap segments to memory (text RX, data RW, bss zero-filled).
- For dynamic linking:
ld-linux.soresolves shared libraries (PLT/GOT). - Set rsp + entry point in registers.
- Jump to
_start(ormainafter CRT init).
Symbol resolution order matters:
- ld searches static libs in order; some libs need to be specified twice.
- Modern: --start-group/--end-group lets ld retry until all resolved.
Common gotchas:
- Undefined references: missing library or typo'd symbol.
- Multiple definitions: same symbol in multiple .o files.
- Wrong arch: 32-bit object linked into 64-bit executable.
- Missing
extern "C"in C++ → name mangling mismatch.
Real-world simplification: GCC/Clang frontend wraps cpp + cc1 + as + ld in one command (gcc foo.c -o foo).
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…