Suppose there is an internal package that provides a method as follows.
This method is used internally, it has no export properties, so it can’t be imported
by other external packages, so since this is the case, is there any way to call this method outside the package? The answer is yes, except that this hack blocks at least 80% of Gopher’s knowledge, and it is go:linkname
.
1. go:linkname Basics
Before understanding go:linkname
, it is necessary to understand the internal package internal
, which is unique to Golang. go 1.4 “Internal” Packages were added in Go 1.14.
An import of a path containing the element “internal” is disallowed if the importing code is outside the tree rooted at the parent of the “internal” directory.
The simple understanding is that this particular internal
package can only be imported by a specific external package.
- Package
/a/b/c/internal/d/e/f
can only be imported by/a/b/c
, not by/a/b/d
. - Package
$GOROOT/src/pkg/internal/xxx
, can only be imported by$GOROOT/src/
. - Package
$GOROOT/src/pkg/net/http/internal
can only be imported bynet/http
andnet/http/*
. - Package
$GOPATH/src/mypkg/internal/foo
can only be imported by$GOPATH/src/mypkg
.
How can I directly reference the internal.print
method without violating this principle?
The //go:linkname
directive instructs the compiler to use importpath.name
as the object file symbolic name of a variable or function declared as localname
in the source code. Since this directive can break the type system and package modularity, it is only enabled in files that have unsafe
imported. As follows.
This completes the process of go:linkname
pointing the method implementation to an unexported method implementation of an external package. In simple terms, this means that go:linkname [local] [target]
binds the specific implementation target
to the current local
method. When run directly, it prompts a missing body
error. This is because go build
adds the -complete
parameter to check for completeness, and apparently this Print
method has no body. Therefore, you need to tell the compiler to bypass this restriction by adding the xxx.s
file to the calling directory. Finally the whole file directory is as follows.
The output after running is as follows.
2. go:linkname advanced 1: random numbers
Both runtime.fastrand
and math.Rand
are pseudo-random number generators. But the difference is that runtime.fastrand
is in the context of the current goroutine. Therefore, it does not require locking during frequent calls, so its performance is much better than that of `math. Here are the performance tests of both.
The performance data obtained from benchmarking shows that runtime.Rand
crushes math/rand
in terms of performance.
|
|
3. go:linkname Advanced 2: Timestamp
time.Now()
and runtime.nanotime1()
both get timestamps, but time.Now()
has underlying calls to runtime.walltime1 and runtime.nanotime to get the timestamp and program runtime respectively. And the latter only needs to get the timestamp separately. Therefore, in some scenarios, such as statistical time consumption, then you can directly get better performance with nanotime1()
. The following is the benchmarking code.
As you can see, runtime.nanotime1()
crushes time.Now()
in terms of performance.
|
|
4. Summary
With an understanding of how go:linkname
works, we can optimize our code for specific scenarios and improve performance bottlenecks. When reading the golang source code, you can also see a lot of go:linkname
directives, understanding this directive helps us better understand the underlying logic of golang code.