Open-Source Wikis

/

Go

/

Components

/

Linker

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:

  1. Reads object files (.a archives of .o files) and merges their symbol tables.
  2. Resolves cross-package references and applies relocations.
  3. Generates runtime-required metadata: the function PC table, source-line tables, GC pointer maps, type tables, init-task lists.
  4. Lays out the executable per the target binary format.
  5. Writes DWARF debug information (or strips it on -w).
  6. 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 tests

How 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| Final

Phases (roughly the entries in cmd/link/internal/ld/main.go's Main):

  1. Load. Read all .a archives, including runtime and the standard library, and parse them into loader.Loader. The Go-specific object format is described in src/cmd/internal/goobj/.
  2. Reachability and dead-code elimination. Mark every symbol reachable from the entry point and init tasks. Unreachable symbols are dropped.
  3. 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.
  4. Layout. Assign each retained symbol to a section (.text, .rodata, .data, .noptrdata, .bss, .gosymtab, .gopclntab, …) and compute addresses.
  5. Relocations. Walk every symbol's relocations and patch them with the final addresses.
  6. 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. See src/runtime/symtab.go.
  7. GC pointer maps. Emit per-function pointer maps so the GC can scan stack frames precisely.
  8. DWARF. Generate sections that debuggers (delve, gdb, lldb) read.
  9. 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/.
  10. 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=pie on 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 linking

cmd/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.go consume 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.go and emit handlers in cmd/link/internal/ld/data.go and the per-arch dirs.
  • Performance work: cmd/link/internal/benchmark/ has a small framework. The linker's -cpuprofile flag also helps.
  • Compiler — what produces the object files the linker reads.
  • Assembler — how .s files become objects.
  • Runtime — what at runtime depends on linker-generated tables.
  • src/cmd/link/doc.go — user-visible flags.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Linker – Go wiki | Factory