首页 > 其他分享 >Golang 的骚操作:go:linkname

Golang 的骚操作:go:linkname

时间:2023-09-25 15:37:35浏览次数:40  
标签:outer Golang inner func go linkname main

背景

  1. 在看源码时,一些源码方法没有方法体,难道说明这些方法为空?例如:time.Now调用的 now(), time.Sleep , reflect.makechan
// Provided by package runtime.
func now() (sec int64, nsec int32, mono int64)

func Sleep(d Duration)

func makechan(typ *rtype, size int) (ch unsafe.Pointer)
  1. 在写代码时,如果我们想使用别的包下没有导出的方法或者变量时,怎么操作

go:linkname 的用法

实际上,上述提到的三个没有方法体的方法,其实现都在 src/runtime包下

  1. time.now timestub.go 文件中
//go:linkname time_now time.now
func time_now() (sec int64, nsec int32, mono int64) {
    sec, nsec = walltime()
    return sec, nsec, nanotime()
}
  1. time.Sleep time.go 文件中
// timeSleep puts the current goroutine to sleep for at least ns nanoseconds.
//go:linkname timeSleep time.Sleep
func timeSleep(ns int64) {
   if ns <= 0 {
      return
   }

   gp := getg()
   t := gp.timer
   if t == nil {
      t = new(timer)
      gp.timer = t
   }
   t.f = goroutineReady
   t.arg = gp
   t.nextwhen = nanotime() + ns
   if t.nextwhen < 0 { // check for overflow.
      t.nextwhen = maxWhen
   }
   gopark(resetForSleep, unsafe.Pointer(t), waitReasonSleep, traceEvGoSleep, 1)
}
  1. reflect.makechan
//go:linkname reflect_makechan reflect.makechan
func reflect_makechan(t *chantype, size int) *hchan {
    return makechan(t, size)
}

我们可以看到具体实现都具有 go:linkname, 可以推测就是这个东西把两个不同包下的函数链接到一起了

官方介绍

//go:linkname localname [importpath.name]

这个特殊的指令的作用域并不是紧跟的下一行代码,而是同一个包下生效。//go:linkname告诉 Go 的编译器把本地的(私有)变量或者方法localname链接到指定的变量或者方法importpath.name。简单来说,localname import.name指向的变量或者方法是同一个。因为这个指令破坏了类型系统和包的模块化原则,只有在引入 unsafe 包的前提下才能使用这个指令。

使用实例

文件结构如下图所示, inner 包模拟 go 源码包/外部引入包,outer包需要调用inner包中的方法, main.go 调试

 tree
.
├── go.mod
├── inner
│   └── inner.go
├── main
├── main.go
└── outer
    ├── 1.s
    └── outer.go

空body方法复现:

模拟方法无 body方法,真实实现在另外一个包中的案例

outer.go

package outer

import (
    _ "github.com/he2121/demos/linkname_example/inner"    // 真实方法在 inner 包,必须引用这个编译器才能找到链接
)

func Hello()

如果出现:missing function body 在包内加一个 .s文件。因为go build默认加会加上-complete参数,加这个 .s 可绕开这个限制。

inner.go

package inner

import (
    _ "unsafe"
)

//go:linkname hello github.com/he2121/demos/linkname_example/outer.Hello
func hello()  {
    println("hello")
}

main.go

package main

import "github.com/he2121/demos/linkname_example/outer"

func main()  {
    outer.Hello()
}
// output:
// hello

这就是 go 源码中这么多没有 body 的方法的原因了,一般到 src/runtime包中搜索就能发现 //go:linkname的实现。

实际使用

实现中,如果我们有一些骚操作需要使用源代码/第三方包中的未导出变量/方法,就可使用 //go:linkname实现, 如下例子

outer.go: 使用 inner 包中的未导出变量/方法

package outer

import (
    _ "unsafe"

    _ "github.com/he2121/demos/linkname_example/inner"
)

//go:linkname A github.com/he2121/demos/linkname_example/inner.a
var A int

//go:linkname Hello github.com/he2121/demos/linkname_example/inner.hello
func Hello()

inner.go

package inner

var a = 100

func hello()  {
    println("hello")
}

main.go

 
package main

import "github.com/he2121/demos/linkname_example/outer"

func main()  {
    outer.Hello()
    println(outer.A)
}
// output
// hello
// 100

引用自:https://segmentfault.com/a/1190000041056240

标签:outer,Golang,inner,func,go,linkname,main
From: https://www.cnblogs.com/zhanchenjin/p/17728016.html

相关文章

  • go 定义类型
    定义变量方式定义变量时需要注意在最外层一定要使用var的方式定义变量不能使用':='的方式,只有在func里面可以使用':='定义变量var(strstring="holle"sumint=1enablebool=false)functest1(){varstr2string="你好"str1:="......
  • golang 1.18 workspace mode
    why?为什么需要workspace历史发展和版本依赖的管理GOPATH最开始的模式开发者需要设置一个环境变量GOPATH,用于指定项目的工作空间。GOPATH是一个目录路径,其中包括了三个重要的子目录:src、bin和pkg通过goget命令,GOPATH/src下的相应目录中缺点:必须指定GOPATH......
  • Django celery 定时任务与周期任务的创建-暂停-开始-删除
    发开阶段遇到了需要定时任务以及周期任务才能进行的事情,这里进行记录一下,防止下次我再写的时候写不明白。首先在你们项目里面创建以下文件:celery:importosos.environ.setdefault("DJANGO_SETTINGS_MODULE","settings")fromceleryimportCeleryfromquality_control.ce......
  • CAP项目集成带身份和证书验证的MongoDB
    大家好,我是Edison。最近,在使用CAP事件总线时,碰到了这样一个需求:微服务采用的是MongoDB,而且还是带身份验证和SSL根证书验证的。由于目前网上能找到的资料,都是不带身份验证的MongoDB,现在网络信息安全越来越被重视,那么就需要自己研究一番了。CAP.MongoDB组件CAP是一个开源的事件......
  • 【主流技术】MongoTemplate与Spring Boot项目集成分享(附CURD技巧)
    目录前言一、表结构特点1.1Json格式1.2实体映射二、条件构造2.1Criteria与Query的区别2.2简单条件2.3复杂条件三、如何选用接口3.1MongoRepository3.2MongoTemplate3.3两者对比四、常见API4.1增·insert4.2删·delete4.3改·update4.3.1update()与save()的区别4.3.2update......
  • 每日一库:使用Viper处理Go应用程序的配置
    在开发Go应用程序时,处理配置是一个常见的需求。配置可能来自于配置文件、环境变量、命令行参数等等。Viper是一个强大的库,可以帮助我们处理这些配置。什么是Viper?Viper是一个应用程序配置解决方案,用于Go应用程序。它支持JSON、TOML、YAML、HCL、envfile和Javaproperties配置文......
  • django快速建站
    #pipinstalldjango#pipinstallrequestsimportosimporttimedefcreatefile(filepath,filetext):  ifnotos.path.exists(filepath):    withopen(filepath,'w',encoding='utf-8')asfile:      file.write(filetext)ifn......
  • 使用 goland 的模板提高编码效率
    整体步骤来自chatgpt概述我觉得编译器有几个很提效的工具:快捷键、代码补全和代码模板。前两个没啥可说的,今天想分享的是代码模板。在Goland里被称之为LiveTemplates。在代码里输入forr,随后会出现如下的可选项,选中按下回车后,会自动生活一个forrange的遍历模板,通过ta......
  • golang 的循环导入
    内容来自对chatgpt的咨询循环导入概念在Go语言中,循环导入是一个需要避免的问题。它发生在两个或更多的包彼此导入对方,形成一个导入循环,导致编译器无法处理。例如,假设你有两个包,包A和包B。包A导入了包B,然后包B又导入了包A,这就形成了一个循环导入。在这种情况下,编译器将无法......
  • MongoDB の 安装与基本使用
    安装mongo建议使用docker直接一键安装dockerrun--namemongo_zdp-p27017:27017-dmongo:latestGUI工具,我使用过的有两个,一个是navicate,一个是nosqlbooster。下载地址如下https://nosqlbooster.com/downloadsmysql和mogodb名称的对比mysqlMongoDB数据......