教程集 www.jiaochengji.com
教程集 >  Golang编程  >  golang教程  >  正文 代码包中如何只有函数声明,引用其他包的函数

代码包中如何只有函数声明,引用其他包的函数

发布时间:2023-01-12   编辑:jiaochengji.com
教程集为您提供代码包中如何只有函数声明,引用其他包的函数等资源,欢迎您收藏本站,我们将为您提供最新的代码包中如何只有函数声明,引用其他包的函数资源

最近在学习golang反射,为了了解下原理,就深入了解了reflect源代码

看到了有一些函数,点击跳入源代码时,只在reflect包中找到了一个函数声明

比如以下函数

// resolveNameOff resolves a name offset from a base pointer.

// The (*rtype).nameOff method is a convenience wrapper for this function.

// Implemented in the runtime package.

func resolveNameOff(ptrInModule unsafe.Pointer, off int32) unsafe.Pointer

看到这种用法,我尝试在自己的代码里面也只声明一个函数,只是无情的被提示missing function body.看了上述函数的注释,是在runtime包中实现的,也在runtime中找到了一个同名函数resolveNameOff,同时又发现了一个类似名称的函数reflect_resolveNameOff

// reflect_resolveNameOff resolves a name offset from a base pointer.
//go:linkname reflect_resolveNameOff reflect.resolveNameOff
func reflect_resolveNameOff(ptrInModule unsafe.Pointer, off int32) unsafe.Pointer {
    return unsafe.Pointer(resolveNameOff(ptrInModule, nameOff(off)).bytes)
}

在注释中发现了reflect.resolveNameOff,因为reflect包中只有函数声明,注释中有说明了在runtime中实现,个人感觉reflect中resolveNameOf是与runtime.reflect_resolveNameOff函数关联着的,但是不清楚它们是如何关联的.

还是在注释中找到了线索

注释内容 **go:linkname reflect_resolveNameOff reflect.resolveNameOff**

搜索关键字go:linkname,最后搜索出一篇英文博客,很好的解释了只有函数声明的实现规则

博客名字翻译过来大意是golang中如何调用第三方包的私有函数

golang是通过大小写确定函数对外的可见性,大写字母开头可导出,小写开头的为私有函数,只能在包内使用,写代码时候编译器会检测是否引用了第三方的私有函数

有两种方法能够绕过这种检测

  • 最老的一种,现在已经不再使用,原文是assembly level implicit linkage to needed symbols, referred as assembly stubs` 不太会翻译,个人理解是使用汇编文件.已经不再使用这种方式,目前通过go:linkname实现
  • go编译器级别的支持,通过go:linkname ,从11.11.14开始

汇编文件

这种方法很简单,使用汇编文件,通过jmp跳转到需要的函数去.

eg: src/os/signal/sig.s

// Assembly to get into package runtime without using exported symbols.

//  build amd64 amd64p32 arm arm64 386 ppc64 ppc64le

#include "textflag.h"

#ifdef GOARCH_arm
#define JMP B
#endif
#ifdef GOARCH_ppc64
#define JMP BR
#endif
#ifdef GOARCH_ppc64le
#define JMP BR
#endif

TEXT ·signal_disable(SB),NOSPLIT,$0
    JMP runtime·signal_disable(SB)

TEXT ·signal_enable(SB),NOSPLIT,$0
    JMP runtime·signal_enable(SB)

TEXT ·signal_ignore(SB),NOSPLIT,$0
    JMP runtime·signal_ignore(SB)

TEXT ·signal_recv(SB),NOSPLIT,$0
    JMP runtime·signal_recv(SB)

在signal_unix.go文件中绑定

//  build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows

package signal

import (
    "os"
    "syscall"
)

// In assembly.
func signal_disable(uint32)
func signal_enable(uint32)
func signal_ignore(uint32)
func signal_recv() uint32

go:linkname

这种方法需要一个空的汇编.s文件

eg: os/signal/sig.s

// The runtime package uses //go:linkname to push a few functions into this
// package but we still need a .s file so the Go tool does not pass -complete
// to the go tool compile so the latter does not complain about Go functions
// with no bodies.

使用方式是 //go:linkname localname linkname

这种方法可以导出函数供链接,也可以导入,绑定现有的函数

go:linkname 导出函数
runtime/proc.go中实现的一个函数
...

//go:linkname sync_runtime_doSpin sync.runtime_doSpin
//go:nosplit
func sync_runtime_doSpin() {
    procyield(active_spin_cnt)
}

告诉编译器与sync.runtime_doSpin中函数链接

sync/runtime.go中使用

package sync

import "unsafe"

...

// runtime_doSpin does active spinning.
func runtime_doSpin()
go:linkname 导入

eg: net/parse.go

package net

import (
    ...
    _ "unsafe" // For go:linkname
)

...

// byteIndex is strings.IndexByte. It returns the index of the
// first instance of c in s, or -1 if c is not present in s.
// strings.IndexByte is implemented in  runtime/asm_$GOARCH.s
//go:linkname byteIndex strings.IndexByte
func byteIndex(s string, c byte) int
使用要求
  1. 引入 unsafe
  2. 函数声明 eg: func byteIndex(s string, c byte) int
  3. //go:linkname 正确放在函数声明处.eg //go:linkname byteIndex strings.IndexByte,byteIndex是本地函数名,strings.IndexByte是第三方包函数名
  4. 提供.s文件,允许编译器去除-complete检测

这两种方法实现很简单,这里不列代码了

参考资料

https://sitano.github.io/2016/04/28/golang-private/

发现了英文博客的翻译

中文翻译

到此这篇关于“代码包中如何只有函数声明,引用其他包的函数”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!

您可能感兴趣的文章:
javascript变量声明提升(hoisting)
PHP函数类型声明总结
理解Javascript的闭包
php闭包函数是什么
golang 中的闭包详细解释
C#私有构造函数的简单例子
【PHP学习】新手必备PHP常用函数大集合
快速解决jQuery与其他库冲突的方法介绍
JavaScript内部原理系列-闭包(Closures)
JavaScript内部原理系列-变量对象(Variable object)

[关闭]
~ ~