Я вызываю функцию 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
и вызвало ошибки.
Хотя я знаю, что с ним не так, я не могу понять, как это исправить.
Обновление:
Я немного покопался в реализации обратного вызова Go и нашел код сборки в src/runtime/sys_windows_amd64.s
.Я заметил, что в runtime·callbackasm1
возвращаемое значение устанавливается с помощью MOVL
вместо MOVQ
.Поэтому я изменил его на MOVQ
и все заработало.
Я не уверен, является ли это ошибкой или на самом деле предназначено, поэтому я открою проблему на GitHub .