This is a creation in Article, where the information may have evolved or changed.
Catalogue [−]
- Change the exported type to another package that is not accessible
- Access to private methods in other package
- Access to struct private fields in other package
- The more Hack method
- Reference documents
Students who are familiar with the object-oriented programming languages of C + +, Java, C #, and so on, are often plagued by access rights in the process of learning the go language, and are gradually able to understand the fact that:
identifier
the first letter of the go language is capitalized to determine whether it can be accessed by other package.
The official Go Language specification is:
An identifier is exported to permit access to it from the another package. An identifier is exported if both:
The first character of the identifier ' s name is a Unicode upper case letter (Unicode Class "Lu"); and
The identifier is declared in the package block or it is a field name or method name.
All other identifiers is not exported.
The access control method defined by this go language specification.
But is there any way to break this limit?
Breakthroughs can be discussed in two directions: changing exported
the type to another package is inaccessible, unexported
and the type becomes accessible to other package.
Change exported
type to other package not accessible
There is at least one way to make the exported function and type in the package inaccessible to other package, which is to define a package that puts the package under the package internal
internal
.
The go language itself does not have this limitation, which is go
achieved by command. This feature was first introduced in the Go 1.4 release, and the relevant details can be viewed in the documentation: design document
The rule is this:
An import of a path containing the element "internal" is disallowed if the importing code was outside the tree rooted at th E Parent of the "internal" directory.
That is, the internal
exported type under the package can only be accessed by the packages where internal is located (the parent of internal).
For example:
/a/b/c/internal/d/e/f
Can be /a/b/c
import, cannot be /a/b/g
import.
$GOROOT/src/pkg/internal/xxx
Only the standard library import ($GOROOT/src/) can be used.
$GOROOT/src/pkg/net/http/internal
can only be net/http
and net/http/*
import.
$GOPATH/src/mypkg/internal/foo
can only be $GOPATH/src/mypkg
import.
Access to private methods in other package
If you look at the code of the Go standard library, such as the Time/sleep.go file, you will find some strange functions, such as Sleep
:
1 |
func Sleep (d Duration) |
This function we often use, that is, time.Sleep
functions, but this function does not have a function body, and the same directory does not have assembly language code implementation, then, where the function is defined?
According to the specification, a function that only declares functions is implemented outside of go, as we call it external function
.
In fact, this "external function" is also implemented in the Go standard library, which is a unexported function in runtime:
123456789101112131415 |
//go:linkname timesleep time. Sleepfuncint64) {if ns <= 0 {return}t: = GETG (). TimerIf Nil New (timer) GETG (). Timer = t} ...} |
In fact, runtime defines a number of functions for other package, such as, sync
net
some functions in, you can grep linkname /usr/local/go/src/runtime/*.go
find these functions by command.
We will have two questions: First, why are these functions defined in the runtime package, but how is this mechanism implemented?
The benefit of defining the relevant functions runtime
is that they can access the types of unexported in the runtime package, such as getp
functions, which are equivalent to a "traitor" to the runtime package and access to the runtime through a "traitor". The private object of the package. At the same time, these "traitor" functions, although declared as unexported, can still be accessed in other package.
The second question, in fact, is the function of the Go go:linkname
command, which is formatted as follows:
1 |
Go:linkname LocalName Importpath.name |
The go documentation illustrates the role of this directive:
The//go:linkname directive instructs the compiler to use "Importpath.name" as the object file symbol name for the VARIABL E or function declared as "LocalName" in the source code. Because This directive can subvert the type system and package modularity, it's only enabled in files that has imported "Unsafe".
This instruction tells the compiler to localname
use importpath.name
the symbol name as the target file for a function or variable. Because this instruction destroys the modularity of the type system and the package, it can only be used in the case of import "unsafe".
importpath.name
This can be the format: This a/b/c/d/apkg.foo
function can be used in the package a/b/c/d/apkg
foo
.
For example, suppose our package layout is as follows:
1234567 |
a a. Go b b. go│└──internal. s└──main └──main. Go |
Package A defines a private method, plus a go:linkname
directive that package B can call a private method of package a . main.go tests access to the functions in b .
First look at a.go
the implementation in:
a.go
1234567891011121314151617 |
package a< span class= "keyword" >import (_ ) //go:linkname say A.say //go:nosplit func say (name string ) string {return "Hello," + name} //go:linkname say2 github.com/smallnest/private/b.hi //go:nosplit func say2 (name string ) string {return + name} |
It defines two methods, and the symbol names are respectively a.say
github.com/smallnest/private/b.Hi
.
The way the different symbol names are used affects the use in b .
b.go
12345678910111213141516 |
package bimport (_ _ "Github.com/smallnest /private/a ") //go:linkname say A.say func say (name string ) string func Greet (name string ) string { return Say (name)}func Hi (name string ) string |
In b , if you want to use symbols a.say
, you still need to go:linkname
tell the compiler that the symbol for this function is a.say
. For Hi
functions, we don't need go:linkname
directives, because the a.go
symbolic names we define in are exactly this package.funcname
.
Note that you need to introduce the package unsafe
, and you need to import package A in b.go .
You can main.go
call bin in:
12345678910111213 |
Package mainimport ("FMT""github.com/smallnest/private/b")func Main () {s: = B.greet ("World") fmt. PRINTLN (s) s = B.hi ("World") fmt. Println (s)} |
But if you do go run main.go
, you won't get the right result, but it will go wrong:
1234 |
go run main. Go# github.com/smallnest/private/b../b/b.go: tenfor"say". /b/b.go:for"Hi" |
Are we wrong in what we said earlier?
Here is a tip, you create an empty file under Package B, the W file name is arbitrary, as long as the file suffix .s
, and then run go run main.go
:
123 |
Main go run Main.gohello, Worldhi, world |
The reason is that when the go compiles -complete
, the compiler flag is enabled, which requires that all functions must contain the body of the function. Create an empty assembly language file to bypass this limitation.
Of course, in general we will not use the two types of breakthroughs listed in this article, only in rare cases, in order to better organize our code, we will have the choice to adopt these two methods. At the very least, as a go developer, you'll remember that there are two ways to break the limits of permissions in the Go Language specification.
Access to struct private fields in other package
An additional technique is available to access the private fields of other package structs.
Naturally, the private fields of the struct are not export, so the other package is not normally accessible. By using refect
, you can access the private fields of a struct:
12345678910111213141516171819 |
Import ("FMT""reflect" "github.com/smallnest/private/c") func Changefoo (f *c.foo) {V: = reflect. ValueOf (f) x: = V.elem (). Fieldbyname ("x") fmt. Println (X.int ())//panic:reflect:reflect. Value.setint using value obtained using unexported field//x.setint (. fmt). Println (X.int ()) Y: = V.elem (). Fieldbyname ("Y") y.setstring ("World") fmt. Println (F.Y)} |
However, you cannot set the value of a private field, otherwise it will be panic because the SetXXX
v.mustBeAssignable()
check field is first used for exported.
Of course, you can also get the address of the field by "pointer", get the data by the address, or set the data.
Or with the same example:
c.go
1234567891011121314 |
Package Ctypestructintstring}funcint { Return f.x}funcintstring) *foo {return &foo{x:x, y:y}} |
Access in Package D:
D.go
12345678910111213141516171819 |
Package DImport ("FMT""unsafe""github.com/smallnest/private/c") Func Changefoo (f *c.foo) {p: = unsafe. Pointer (f)//acquired or obtained through reflect/// This example is the first field, so offset=0uintptr(0) ptr2x : = (*int) (unsafe. Pointer (uintptr(p) + offset)) fmt. Println (*ptr2x) *ptr2x = the FMT. Println (f.x ())} |
The more Hack method
If you are not satisfied, then I will give a more hack method, but this is also a bit limited, that is, your waist call method should be called somewhere before.
This is Alan Pierce provides a way. Runtime/symtab.go saved the symbol table, through some tricks ( go:linkname
), can access its private methods, find the function that you want to call, and then you can call, Alan wrote the relevant code into a library, convenient to call: Go-forceexport.
Here's how to use it:
1234567 |
var func () (Int64int32"Time.now")ifnil { //Handle errors If care About name possibly being invalid.} //Calls the actual time.now function. SEC, Nsec: = TimeNow () |
In the process of using it, I found that only the corresponding method was called at some point, and the function information in the symbol table forceexport.GetFunc
would return the corresponding function.
In addition, this is a very hack way, does not guarantee that the go future version will still be able to use, only for playful use, use caution in the product code.
Reference documents
- https://golang.org/cmd/compile/
- https://github.com/golang/go/issues/15006
- Https://siadat.github.io/post/golinkname
- https://sitano.github.io/2016/04/28/golang-private/
- Https://golang.org/doc/go1.4#internalpackages
- http://www.alangpierce.com/blog/2016/03/17/adventures-in-go-accessing-unexported-functions/
- Https://groups.google.com/forum/#!topic/golang-nuts/ppGGazd9KXI