Golang中linkname的用法与注释解析
Golang中linkname的用法与注释解析
你好,我试图弄清楚Go是如何执行诸如打开文件、写入文件、打开套接字等对环境产生副作用的操作的。我在代码中写下了os.Open并开始查看其实现。最终我看到了类似这样的代码:
//go:linkname runtime_entersyscall runtime.entersyscall
func runtime_entersyscall()
func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
- 这是有效的Go语法吗?据我所知,函数应该有实现体,我们不能只写一个函数头。
//go:linkname与这段代码有关吗?也许它链接到了某个.so或.dll文件?如果是这样,这些.so和.dll文件是从哪里加载的?- 如果我对第(2)点的猜测是正确的,我能否使用
//go:linkname创建自己的函数,并将其与我用C/C++/Rust/任何语言编写并编译成.so或.dll的自定义共享库链接? - 第二个函数定义(
RawSyscall6)是怎么回事?它甚至没有//go:linkname注释。这背后的解释是什么?
我之所以问这些问题,是因为我试图理解Go编译器的工作原理,并且我很好奇Go是如何执行副作用的,因为据我所知,仅凭“语言”本身应该无法做到这一点。不像C语言,我们可以编写内联汇编,这意味着我们可以通过中断指令执行任何系统调用。
更多关于Golang中linkname的用法与注释解析的实战教程也可以访问 https://www.itying.com/category-94-b0.html
据我所知,//go:linkname 用于引用其他未导出的外部函数定义,而不是用于导入或引用外部系统或动态库。
我的意思在此页面中有详细说明。
当然,由于这是一种绕过 Go 标准包规则的方法,你需要导入 unsafe 包才能使用这种技巧。
更多关于Golang中linkname的用法与注释解析的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Go语言中,//go:linkname是一个编译器指令,用于链接到其他包中的未导出函数。以下是针对您问题的详细解析:
1. 函数声明语法
//go:linkname runtime_entersyscall runtime.entersyscall
func runtime_entersyscall()
这是有效的Go语法。func runtime_entersyscall()是一个函数声明(declaration),而不是定义(definition)。它告诉编译器这个函数存在,但实现在其他地方。这种声明通常用于链接到runtime包中的内部函数。
2. //go:linkname的作用
//go:linkname指令的格式是:
//go:linkname localname importpath.name
它告诉编译器将当前包中的localname链接到importpath.name指定的函数。例如:
//go:linkname entersyscall runtime.entersyscall
func entersyscall()
这会将当前包的entersyscall函数链接到runtime包的entersyscall函数实现。
3. 系统调用实现示例
查看syscall包的实现:
// syscall/syscall_unix.go
//go:linkname syscall_syscall syscall.syscall
func syscall_syscall(fn, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) {
return syscall_syscall(uintptr(unsafe.Pointer(&libc_syscall)), trap, a1, a2, a3)
}
在runtime包中:
// runtime/syscall.go
//go:linkname syscall_syscall syscall.syscall
func syscall_syscall(fn, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) {
// 实际的系统调用实现
entersyscall()
// ... 系统调用逻辑
exitsyscall()
return
}
4. RawSyscall6的实现
RawSyscall6通常通过汇编实现。例如在syscall/asm_linux_amd64.s中:
// syscall/asm_linux_amd64.s
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
MOVQ trap+0(FP), DI
MOVQ a1+8(FP), SI
MOVQ a2+16(FP), DX
MOVQ a3+24(FP), R10
MOVQ a4+32(FP), R8
MOVQ a5+40(FP), R9
MOVQ a6+48(FP), R11
MOVQ R11, 8(SP)
SYSCALL
MOVQ AX, r1+56(FP)
MOVQ DX, r2+64(FP)
RET
5. 自定义链接示例
可以使用//go:linkname链接到其他包的非导出函数:
// mypkg/mylink.go
package mypkg
import _ "unsafe"
//go:linkname timeNow time.now
func timeNow() (sec int64, nsec int32, mono int64)
func GetInternalTime() (int64, int32, int64) {
return timeNow()
}
// runtime/time.go
package runtime
// 非导出函数
func now() (sec int64, nsec int32, mono int64) {
// 实现时间获取
}
6. 系统调用流程
Go执行系统调用的典型流程:
func Open(name string) (*File, error) {
return openFile(name, O_RDONLY, 0)
}
func openFile(name string, flag int, perm FileMode) (*File, error) {
// ...
r, e = syscall.Open(name, flag|syscall.O_CLOEXEC, syscallMode(perm))
// ...
}
// syscall.Open -> syscall.Syscall -> runtime.entersyscall -> 实际系统调用
//go:linkname是Go编译器的一个内部特性,主要用于标准库中链接runtime和其他包的内部函数。它允许包之间共享非导出的函数实现,是Go实现系统调用和底层操作的关键机制之一。

