Runtime Bootstrap

Updated

September 8, 2026

Runtime Bootstrap

Overview

Before main.main, the OS jumps into runtime startup: init the scheduler, allocator, GC metadata, threads, and package init graph. Every Go binary embeds this runtime.

Series-style depth: Understanding the Go Runtime — Bootstrap.

Diagram: from exec to main

  OS loader
     │
     v
  runtime entry (asm)
     │
     v
  init scheduler + heap + main G
     │
     v
  package init() order
     │
     v
  main.main

What gets set up

Subsystem Early job
Allocator arenas, spans ready for first new
Scheduler GOMAXPROCS Ps, main M, main G
GC metadata, write barrier off until needed
Signals platform signal plumbing
Netpoll poll descriptor (platform)

Package init order

imported packages first (dependency order)
  → file-level var initializers
  → init() functions
  → main.main

Do not rely on clever cross-package init races; make dependencies explicit.

Main goroutine

main.main runs on the main goroutine. When it returns, the runtime shuts down the process (other Gs are not gracefully joined unless you waited).

Experiment

go build -o /tmp/hello .
# optional: go tool objdump / nm to see runtime symbols
go tool nm /tmp/hello | rg 'runtime\\.(main|schedinit|newproc)' | head
package main

import "fmt"

func init() { fmt.Println("init") }
func main() { fmt.Println("main") }

What to notice: init always before main; runtime symbols dominate even tiny binaries.

Try next: Compare binary size CGO_ENABLED=0 hello vs one that imports net/http.