golang/go
Linker
cmd/link (in src/cmd/link/) is Go's own linker. It takes the object archives the compiler and assembler produce and emits a final executable. Most of the time, Go uses internal linking — its own ELF/Mach-O/PE writer — but for cgo and certain build modes it can switch to external linking, where it cooperates with the host system linker (ld, lld, link.exe).
Purpose
The linker:
- Reads object files (
.aarchives of.ofiles) and merges their symbol tables. - Resolves cross-package references and applies relocations.
- Generates runtime-required metadata: the function PC table, source-line tables, GC pointer maps, type tables, init-task lists.
- Lays out the executable per the target binary format.
- Writes DWARF debug information (or strips it on
-w). - Optionally invokes the host linker (external mode) for cgo objects.
Directory layout
src/cmd/link/
├── doc.go # Top-level docs (mirrors 'go tool link -help')
├── main.go # Tiny entry; calls into ld.Main
└── internal/
├── ld/ # The bulk of the linker (architecture-independent)
├── loader/ # Symbol loading + relocation database
├── sym/ # Symbol type + reloc-type definitions
├── loadelf/ # Read ELF object files (cgo)
├── loadmacho/ # Read Mach-O object files (cgo on macOS)
├── loadpe/ # Read PE object files (cgo on Windows)
├── loadxcoff/ # Read XCOFF (AIX)
├── amd64/ # AMD64-specific linker
├── arm/, arm64/ # ARM linkers
├── ppc64/, mips/, mips64/, riscv64/, s390x/, loong64/
├── x86/ # 386 linker
├── wasm/ # WebAssembly linker
├── benchmark/ # Microbenchmarks
└── dwtest/ # DWARF generation testsHow linking works
graph TD
Inputs[Object archives .a + runtime + std] --> Loader[loader: read symbols + relocs]
Loader --> SymTab[Global symbol table]
SymTab --> Mark[Reachability: walk from entry]
Mark --> Reloc[Apply relocations]
Reloc --> Layout[Section layout per format]
Layout --> Pcdata[Generate PC tables, GC maps, funcdata]
Pcdata --> Dwarf[DWARF debug info]
Dwarf --> Write[Write ELF / Mach-O / PE / Wasm]
Write -->|external mode| HostLink[Invoke host linker]
HostLink --> Final[Final executable]
Write -->|internal mode| FinalPhases (roughly the entries in cmd/link/internal/ld/main.go's Main):
- Load. Read all
.aarchives, includingruntimeand the standard library, and parse them intoloader.Loader. The Go-specific object format is described insrc/cmd/internal/goobj/. - Reachability and dead-code elimination. Mark every symbol reachable from the entry point and
inittasks. Unreachable symbols are dropped. - Type and reflect bookkeeping. Collect type descriptors, methods, and per-package type lists. The runtime depends on these tables to implement
reflect, type assertions, and interface dispatch. - Layout. Assign each retained symbol to a section (
.text,.rodata,.data,.noptrdata,.bss,.gosymtab,.gopclntab, …) and compute addresses. - Relocations. Walk every symbol's relocations and patch them with the final addresses.
- PC tables. Build the
pclntab— a packed structure of PC-to-line, PC-to-file, frame info, and inline tree info. The runtime uses this for tracebacks, panic stacks, and profilers. Seesrc/runtime/symtab.go. - GC pointer maps. Emit per-function pointer maps so the GC can scan stack frames precisely.
- DWARF. Generate sections that debuggers (delve, gdb, lldb) read.
- Write. Serialize sections into the target binary format. Per-format writers live in
cmd/link/internal/ld/elf.go,macho.go,pe.go,xcoff.go,wasm/. - External linking (optional). If the build uses cgo or
-linkmode=external, write a partial object and shell out to the host linker.
Build modes
The linker supports multiple "build modes" via -buildmode:
| Mode | Effect |
|---|---|
exe |
Standard executable (default) |
pie |
Position-Independent Executable |
shared |
Shared .a for Go consumers |
c-archive |
Static archive callable from C |
c-shared |
Shared library callable from C |
plugin |
Loadable Go plugin (plugin.Open) |
See go help buildmode for the platform support matrix and cmd/link/doc.go for the implementation hints.
Linkmode
Two linkmodes:
-linkmode=internal— Go writes the binary itself. Smallest, fastest, default for most platforms when cgo is not used.-linkmode=external— Go writes object files and invokes the host linker. Required when:- cgo is enabled and any C library uses thread-local storage,
- building
-buildmode=pieon some platforms, - linking against system libraries that need their own dynamic linker.
cmd/link chooses the default based on GOOS/GOARCH and cgo presence. Override with -ldflags '-linkmode=...'.
Key abstractions
| Abstraction | Where | Purpose |
|---|---|---|
loader.Loader |
cmd/link/internal/loader/loader.go |
All loaded symbols + relocations |
loader.Sym |
same | A symbol identifier (an integer) |
sym.SymKind |
cmd/link/internal/sym/symkind.go |
STEXT, SRODATA, SBSS, ... |
sym.Reloc |
cmd/link/internal/sym/reloctype.go |
One relocation entry |
ld.Link |
cmd/link/internal/ld/link.go |
Linker context for one invocation |
ld.Symbol (legacy) |
various | Older shape, mostly migrated |
goobj.Reader |
cmd/internal/goobj/ |
Reads Go-format objects |
Key source files
| File | Purpose |
|---|---|
src/cmd/link/main.go |
Tiny entry, calls ld.Main |
src/cmd/link/doc.go |
User-facing flag documentation |
src/cmd/link/internal/ld/main.go |
Main(): the phase pipeline |
src/cmd/link/internal/ld/lib.go |
Loading + cgo glue |
src/cmd/link/internal/ld/data.go |
Sections, layout, relocations (large) |
src/cmd/link/internal/ld/pcln.go |
pclntab generation |
src/cmd/link/internal/ld/dwarf.go |
DWARF generation |
src/cmd/link/internal/ld/elf.go, macho.go, pe.go, xcoff.go |
Format writers |
src/cmd/link/internal/loader/loader.go |
Symbol/reloc database |
Useful flags
go build -ldflags='-X pkg/path.Var=value' # set string variable at link time
go build -ldflags='-s -w' # strip symbol table + DWARF (smaller binary)
go build -ldflags='-buildid=ID' # set the build ID (used by build cache)
go build -ldflags='-extldflags="-Wl,-z,now"' # pass flags to host linker
go build -ldflags='-linkmode=external' # force external linking
go build -trimpath # strip absolute paths from binary
go tool link -v=2 ... # verbose linkingcmd/link is built into pkg/tool/<host>/link. Run go tool link -help for the full list.
Integration points
- Object format (
src/cmd/internal/goobj/) is shared between compiler, assembler, and linker. - Runtime (runtime) reads
pclntab, GC pointer maps, and type tables that the linker generated. - DWARF readers (delve, gdb, addr2line) parse the linker's DWARF output.
runtime.Callers+runtime/symtab.goconsume the linker's PC tables.- Plugins (
plugin.Open) load shared objects produced by-buildmode=plugin.
Entry points for modification
- Adding a new architecture: copy an existing arch dir (
amd64/,arm64/) and adapt. Most arch-specific work is relocation handling. - New relocation type: add to
cmd/internal/objabi/reloctype.goand emit handlers incmd/link/internal/ld/data.goand the per-arch dirs. - Performance work:
cmd/link/internal/benchmark/has a small framework. The linker's-cpuprofileflag also helps.
Where to read next
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.