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)
  1. 这是有效的Go语法吗?据我所知,函数应该有实现体,我们不能只写一个函数头。
  2. //go:linkname 与这段代码有关吗?也许它链接到了某个 .so.dll 文件?如果是这样,这些 .so.dll 文件是从哪里加载的?
  3. 如果我对第(2)点的猜测是正确的,我能否使用 //go:linkname 创建自己的函数,并将其与我用C/C++/Rust/任何语言编写并编译成 .so.dll 的自定义共享库链接?
  4. 第二个函数定义(RawSyscall6)是怎么回事?它甚至没有 //go:linkname 注释。这背后的解释是什么?

我之所以问这些问题,是因为我试图理解Go编译器的工作原理,并且我很好奇Go是如何执行副作用的,因为据我所知,仅凭“语言”本身应该无法做到这一点。不像C语言,我们可以编写内联汇编,这意味着我们可以通过中断指令执行任何系统调用。


更多关于Golang中linkname的用法与注释解析的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

据我所知,//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实现系统调用和底层操作的关键机制之一。

回到顶部