Breaking limits to access private functions in other go package

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed.

Catalogue [−]

    1. Change the exported type to another package that is not accessible
    2. Access to private methods in other package
    3. Access to struct private fields in other package
    4. The more Hack method
    5. 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:

identifierthe 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/fCan be /a/b/c import, cannot be /a/b/g import.
    • $GOROOT/src/pkg/internal/xxxOnly the standard library import ($GOROOT/src/) can be used.
    • $GOROOT/src/pkg/net/http/internalcan only be net/http and net/http/* import.
    • $GOPATH/src/mypkg/internal/foocan 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.nameThis 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

    1. https://golang.org/cmd/compile/
    2. https://github.com/golang/go/issues/15006
    3. Https://siadat.github.io/post/golinkname
    4. https://sitano.github.io/2016/04/28/golang-private/
    5. Https://golang.org/doc/go1.4#internalpackages
    6. http://www.alangpierce.com/blog/2016/03/17/adventures-in-go-accessing-unexported-functions/
    7. Https://groups.google.com/forum/#!topic/golang-nuts/ppGGazd9KXI
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.