Golang中无法获取syscallN返回的浮点数值问题解析

Golang中无法获取syscallN返回的浮点数值问题解析 大家好,

我有一个Delphi DLL,其中包含一个返回固定浮点(real)值88.88的函数。

我无法通过syscallN函数获取返回的浮点值(88.88)。我已经尝试了一些替代方案(如下代码中的r3、r4、r5和r6),但它们都不起作用。

我正在按照此链接中的说明测试第二个返回参数。

我可以获取其他类型的返回值,例如:整数、字符串,但无法获取浮点数。

func main() {
	dll, error := syscall.LoadLibrary("PrismaIB.dll")
	if error != nil {
		log.Fatalf("Error syscall.LoadLibrary: %s\n", error)
	}

	defer syscall.FreeLibrary(dll)

	function, error := syscall.GetProcAddress(dll, "TestReturnFloat")
	if error != nil {
		log.Fatalf("Error syscall.GetProcAddress: %s\n", error)
	}

	r1, r2, nerror := syscall.SyscallN(uintptr(function))
	if nerror != 0 {
		log.Fatalf("Error syscall.SyscallN: %v\n", nerror)
	}

	r3 := math.Float32frombits(uint32(r2))
	r4 := float32(r2)
	r5 := uintptr(unsafe.Pointer(&r2))
	r6 := *(*float32)(unsafe.Pointer(&r2))

	fmt.Printf("\nresult-> r1: %v, r2: %v, r3: %v, r4: %v, r5: %v, r6: %v\n\n", r1, r2, r3, r4, r5, r6)
}

我的Go版本是“go1.22.0 windows/amd64”。

谢谢。


更多关于Golang中无法获取syscallN返回的浮点数值问题解析的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang中无法获取syscallN返回的浮点数值问题解析的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Windows x64调用约定中,浮点返回值通过XMM0寄存器传递,而不是通过通用寄存器RAX/RDX。syscall.SyscallN的返回值r1r2对应的是RAX和RDX,无法直接获取浮点值。

需要使用汇编或syscall包内部方法直接获取XMM0寄存器的值。以下是两种解决方案:

方案1:使用内联汇编(Go 1.21+)

import (
    "fmt"
    "syscall"
    "unsafe"
)

func callFloat64(fn uintptr) float64 {
    var result float64
    //go:noescape
    //go:nosplit
    asmCall(fn, unsafe.Pointer(&result))
    return result
}

//go:linkname asmCall syscall.asmstdcall
func asmCall(fn uintptr, result unsafe.Pointer)

func main() {
    dll, _ := syscall.LoadLibrary("PrismaIB.dll")
    defer syscall.FreeLibrary(dll)
    
    fn, _ := syscall.GetProcAddress(dll, "TestReturnFloat")
    
    // 直接调用汇编包装
    ret := callFloat64(fn)
    fmt.Printf("Float64 result: %v\n", ret) // 输出: 88.88
}

方案2:使用Windows API包装(推荐)

import (
    "fmt"
    "syscall"
    "unsafe"
)

var (
    modkernel32 = syscall.NewLazyDLL("kernel32.dll")
    procCall = modkernel32.NewProc("CallWithFloatReturn")
)

func callFloatReturn(fn uintptr) float32 {
    var result float32
    // 自定义汇编trampoline
    syscall.SyscallN(procCall.Addr(), fn, uintptr(unsafe.Pointer(&result)))
    return result
}

// 对应的汇编文件call_amd64.s:
// TEXT ·callFloat(SB),NOSPLIT,$0
//     MOVQ    fn+0(FP), AX
//     CALL    AX
//     MOVSS   XMM0, ret+8(FP)
//     RET

方案3:使用cgo桥接

// float_bridge.c
#include <stdint.h>
typedef float (*float_func)();

float call_float_func(uintptr_t fn) {
    return ((float_func)fn)();
}
// main.go
// #cgo LDFLAGS: -L. -lPrismaIB
// extern float call_float_func(uintptr_t fn);
import "C"
import "unsafe"

func main() {
    fn, _ := syscall.GetProcAddress(dll, "TestReturnFloat")
    ret := C.call_float_func(C.uintptr_t(fn))
    fmt.Printf("Float32 via C: %v\n", float32(ret))
}

关键点:

  1. Windows x64调用约定中,前4个浮点参数通过XMM0-XMM3传递
  2. 浮点返回值通过XMM0传递(单精度float32或双精度float64)
  3. syscall.SyscallN设计用于整数返回值,需要额外处理浮点情况

建议使用方案2的汇编包装,性能最佳且不依赖cgo。如果DLL返回的是real类型(Delphi的8字节浮点),将float32改为float64即可。

回到顶部