What is //go:linkname, and what changed about it in Go 1.23?
//go:linkname localname importpath.name tells the compiler to use importpath.name as the object-file symbol for localname. It needs import _ "unsafe". It comes in two forms:
- Push: the package that defines the symbol marks it with a linkname, which exports it on purpose (the runtime does this for
sync,timeandreflect). - Pull: a package declares a function without a body and links it to another package's unexported symbol. This is how many libraries reached into runtime internals such as
runtime.nanotimeorruntime.fastrand.
package fastclock
import _ "unsafe" // required for //go:linkname
//go:linkname nanotime runtime.nanotime
func nanotime() int64 // allowed: runtime explicitly keeps this symbol linkable
// Pulling a standard-library internal that is NOT marked as linkable
// is rejected by the linker since Go 1.23.
// Escape hatch (not for production): go build -ldflags=-checklinkname=0
Go 1.23 change: the linker now rejects pull-only linknames to standard-library internals unless the defining package marks the symbol with a push linkname. Widely used symbols (the "hall of shame" of popular modules that depend on them) were marked to keep existing code working, but new internals are off limits.
What interviewers want: linkname bypasses the type system and the compatibility promise. The symbol can change signature or disappear in any release and break your build or, worse, corrupt memory. Prefer public APIs, for example math/rand/v2 instead of fastrand, or time.Now(), which already uses a monotonic clock.
More on Modules, Packages & Tooling
- Q440What are module graph pruning and lazy module loading (Go 1.17), and why did go.mod files get so much longer?
- Q441Explain the build cache, the module cache and test result caching. When does go test print "(cached)" and how do you defeat it?