golang go:linkname 的使用

这两天看go的源码,突然发现很多函数都是汇编写的go去调用的时候通过在函数上添加  //go:linkname xxx   yyy  等形式进行调用。

由于go是按照首字母大小写决定是否可以被外部包引用的。所以,如果我们想方位某个包中的私有成员,就需要用到go:linkname了,也就是说我们可以通过 //go:linkname localname linkname 这种方式将本地的私有函数/变量,提供给外部使用。但是经过试验,常量是不可以的。同时还有一个  //go:nosplit 在源码中也是经常出现的,其实就是告诉编译器,下面的函数不会产生堆栈溢出,不需要插入堆栈溢出检查。

具体的使用有大致有两种

1. 通过  //go:linkname localname linkname   将函数 localname 连接到 当前包(a),然后在使用的时候(b)同样需要 通过此命令连接到之前的符号。也就是 a.go 在函数上加 //go:linkname say a.say 此时无论使用方采用首字母大写关联还是小写进行管理,均不能直接使用。  之后 b.go 中 

//go:linkname sayTest a.say 
func sayTest(name string) string
func Greet(name string) string {
	return sayTest(name)
}

 

2. 不需要在使用的时候在用 go:linkname 去关联,只需要在定义处直接 关联到目标包的函数即可,则目标包只需要定义一个关联时候定义的 函数体即可直接使用  func Hi(name string) string 

 

 

我们先看一下代码的结构

具体代码:

a/a.go

package a

import _ "unsafe"

//go:linkname say a.say
//go:nosplit
func say(name string) string {
	return "say:hello," + name
}

//go:linkname say1  a.say1
//go:nosplit
func say1(name string) string {
	return "say1:hello," + name
}

//将 say2 连接到 对应的域名下的函数
//go:linkname say2 SourceCodeTest/go-linkname-test/b.Hi
//go:nosplit  不得包含堆栈溢出检查  因为只是一个简单的字符串拼合,不需要移除检测
func say2(name string) string {
	return "say2:hi," + name
}

//go:linkname aaaStr SourceCodeTest/go-linkname-test/b.AAAStr
var aaaStr = "1111"
//go:linkname name SourceCodeTest/go-linkname-test/b.Name
const name  = "33333"

b/b.go

package b

import (
	_ "SourceCodeTest/go-linkname-test/a"
	_ "unsafe"
)

var AAAStr = ""

const Name = "222"

//go:linkname sayTest a.say
func sayTest(name string) string

//及时是 大写的 但是在main包和其他任何包中,是不能直接调用的,因为
//go:linkname sayHi a.say1  
func sayHi(name string) string
func SayHii(name string) string {
	return sayHi(name)
}

func Greet(name string) string {
	return sayTest(name)
}

/*

*如果不写此函数,a.go中的 //go:linkname say2 SourceCodeTest/go-linkname-test/b.Hi 并不会报错
 */
func Hi(name string) string

c/c.go

package c

import (
	"SourceCodeTest/go-linkname-test/b"
)

func SayHii(name string) string {
	return b.Hi(name)
}

main.go

package main

import (
	"SourceCodeTest/go-linkname-test/b"

	"SourceCodeTest/go-linkname-test/c"
	"fmt"
)

func main() {
	s := b.Greet("world")
	fmt.Println(s)
	s = b.Hi("world")
	fmt.Println(s)
	s = b.SayHii("world")
	fmt.Println(s)
	s = c.SayHii("world")
	fmt.Println(s)
	fmt.Println(b.AAAStr)
	fmt.Println(b.Name)
}

运行结果:

通过以上测试我们就可以知道,函数,变量可以通过这种方式打破go原本的包之间的调用规则,但是常量是不行的。至于b/b.s的文件,没有则会报错,源码中类似的文件则为 汇编语言定义的函数,此处我们只放一个空文件,应对go语言编译检测即可。需要注意的点是,是用 go:linkname需要引入 unsafe 包  import _ "unsafe"  在要使用私有变量的包中(b)同时还要引入 调用的包,只是我们并不显示调用,所以用“  _ ” 忽略掉即可。

参考:https://colobu.com/2017/05/12/call-private-functions-in-other-packages/

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值