Windows本机回调仅在64位平台上返回32位结果

Rix*_*Rix 8 go

我正在调用一个DLL函数,它接受一个回调函数,其中回调函数返回一个指向某个数据结构的指针,DLL将对数据结构执行一些操作.

package main

import (
    "golang.org/x/sys/windows"
    "unsafe"
)

type A struct {}
var a = &A{}

func Callback() uintptr {
    ptr := uintptr(unsafe.Pointer(a))
    fmt.Printf("Address: 0x%X\n", ptr)
    return ptr
}

func main() {
    dll := windows.MustLoadDLL("some.dll")
    init := dll.MustFindProc("init")
    init.Call(windows.NewCallback(Callback))
    fmt.Printf("This never get prints\n")
}
Run Code Online (Sandbox Code Playgroud)

我正在运行Windows 64位机器,DLL也是64位.

$ go version
go version go1.11.4 windows/amd64
Run Code Online (Sandbox Code Playgroud)

上面的代码将打印指针的地址0xC000080018,但是对DLL的调用将无声地失败.我没有DLL的源代码,所以我无法打印它获得的值.所以我使用IDA pro来动态调试程序,我发现Go回调函数实际上是返回32位值而不是64位值.这是Go回调例程返回DLL之前的最终代码:

main.exe:00000000004537E7     mov     eax, [rcx+rdx-8]
main.exe:00000000004537EB     pop     qword ptr [rcx+rdx-8]
main.exe:00000000004537EF     retn
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,Go正在写入返回值eax而不是rax,它会丢弃更高的地址,因此DLL只会获取0x80018并导致错误.

虽然我知道它有什么问题,但我无法弄清楚如何修复它.

更新:

我已经完成了一些Go的回调实现,并找到了汇编代码src/runtime/sys_windows_amd64.s.我注意到,runtime·callbackasm1使用MOVL而不是使用设置返回值MOVQ.所以我把它改成了MOVQ,一切都运转了.

我不确定这是一个错误还是实际意图,所以我将在GitHub上打开一个问题.

LeG*_*GEC 1

该错误已作为问题发布在 golang 存储库(问题 29331)上,并由 golang 团队修复。