Fundamentals 13 min read

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.

Go Programming World
Go Programming World
Go Programming World
Exploring Go's //go:linkname Directive and Its Pull, Push, and Handshake Modes

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() uint32

When 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) int

Key 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) string

This 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) bool

To compile such code, the -checklinkname=0 linker flag must be provided:

$ go run -ldflags=-checklinkname=0 main.go
TooLarge(1e6+1): true

Alternatively, 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.

GoUnsafepull-modepush-modecompiler-directivehandshake-modeLinkname
Go Programming World
Written by

Go Programming World

Mobile version of tech blog https://jianghushinian.cn/, covering Golang, Docker, Kubernetes and beyond.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.