Exploring Go's //go:linkname Directive and Its Pull, Push, and Handshake Modes
This article explains the Go //go:linkname compiler directive, showing how it can link exported and unexported symbols across packages using Pull, Push, and Handshake modes, provides code examples, discusses safety considerations, and offers guidance for using the directive in Go 1.23 and later.
In Go, functions and variables are exported based on the case of their first letter, which strictly separates exported (public) and unexported (private) symbols. The //go:linkname compiler directive acts as a backdoor, allowing developers to link directly to any symbol—whether exported or not—across packages.
//go:linkname Directive Overview
The //go:linkname directive tells the compiler to link a local name to a symbol in another package, even if that symbol is unexported. Its syntax is:
//go:linkname localname [importpath.name]For example, to declare a FastRand function that links to the private runtime.fastrand function:
//go:linkname FastRand runtime.fastrand
func FastRand() uint32When FastRand() is called, the runtime implementation is executed without an explicit body in the current package.
Pull Mode
In Pull (拉取) mode, the target package does not need any special handling; the source package simply uses //go:linkname to link to the target’s unexported symbol. Example:
//go:linkname Add github.com/jianghushinian/blog-go-example/directive/linkname/bar.add
func Add(a, b int) intKey points for Pull mode:
The target package must be imported explicitly (except for the runtime package).
The unsafe package must be imported because the operation is considered unsafe.
After linking, the function can be used like any normal exported function.
Push Mode
In Push (推送) mode, the target package renames its unexported function to an exported name in the consumer package. Example:
//go:linkname div github.com/jianghushinian/blog-go-example/directive/linkname/foo.Div
func div(a, b int) int { return a / b }The consumer package declares the Div function without an implementation, and the linkname directive connects it to the actual implementation in the target package.
Handshake Mode
Handshake mode combines Pull and Push: both packages use //go:linkname to agree on the linking. The target marks its function as linkable, and the consumer declares a matching function. Example:
//go:linkname hello
func hello(name string) string { return "Hello " + name + "!" } //go:linkname Hello github.com/jianghushinian/blog-go-example/directive/linkname/bar.hello
func Hello(name string) stringThis mode is the recommended approach from Go 1.23 onward because it makes the linking intent explicit on both sides.
Built‑in Package Limitations and Go 1.23 Changes
Starting with Go 1.23, Pull and Push modes are discouraged, especially when linking to symbols in the standard library. Attempting to link to fmt.tooLarge without special flags results in a compilation error:
//go:linkname TooLarge fmt.tooLarge
func TooLarge(x int) boolTo compile such code, the -checklinkname=0 linker flag must be provided:
$ go run -ldflags=-checklinkname=0 main.go
TooLarge(1e6+1): trueAlternatively, moving the wrapper function into the consumer package avoids the need for the flag.
Conclusion
The //go:linkname directive is a powerful but unsafe tool that allows Go developers to bypass the language’s export restrictions and link to private symbols. It supports three modes—Pull, Push, and Handshake—with Handshake being the preferred method in modern Go versions. Use it cautiously and only when you fully understand the implications.
References
cmd/compile Documentation: https://pkg.go.dev/cmd/[email protected]#hdr-Linkname_Directive
cmd/link: lock down future uses of linkname #67401: https://github.com/golang/go/issues/67401
The hall of shame: https://github.com/golang/go/blob/go1.24.1/src/runtime/malloc.go#L996-L1010
Go 1.23 Release Notes: https://go.dev/doc/go1.23#linker
Function declarations: https://go.dev/ref/spec#Function_declarations
突破限制,访问其它Go package中的私有函数: https://colobu.com/2017/05/12/call-private-functions-in-other-packages/
GitHub example code: https://github.com/jianghushinian/blog-go-example/tree/main/directive/linkname
For further questions, you can reach the author via WeChat, email, or GitHub.
Go Programming World
Mobile version of tech blog https://jianghushinian.cn/, covering Golang, Docker, Kubernetes and beyond.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.