我正在调用一个带有回调函数的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")
}
我正在Windows 64位计算机上运行,该DLL也是64位。
$ go version
go version go1.11.4 windows/amd64
上面的代码将打印指针的地址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
如您所见,Go在eax
中而不是在rax
中写入返回值,这将丢弃较高的地址,因此DLL将仅获得0x80018
并引起错误。 / p>
尽管我知道它有什么问题,但我不知道如何解决它。
更新:
我对Go的回调实现做了一些深入的研究,并在src/runtime/sys_windows_amd64.s
找到了汇编代码。我注意到在runtime·callbackasm1
中,返回值是使用MOVL
而不是MOVQ
设置的。所以我将其更改为MOVQ
,一切正常。
我不确定这是错误还是实际意图,所以我会open an issue on GitHub。