go syscall: 在Windows上,通过值传递结构体是有问题的,

mrzz3bfm  于 4个月前  发布在  Go
关注(0)|答案(5)|浏览(43)

在Windows上,有时GUID是通过值传递的。这在不同的架构上有不同的意思。

  • 在amd64上,无论如何都是通过引用传递的。
  • 386上,它是按字(word)逐个传递在栈上的。
  • 在arm和arm64上,它是按字(word)逐个传递在寄存器中的,但只有在有足够的寄存器剩余的情况下才会这样做;否则,它是通过引用传递的。

所以,这里有一个不容易与mkwinsyscall一起工作的函数:

//sys  setInterfaceDnsSettings(guid windows.GUID, settings *dnsInterfaceSettings) (ret error) = iphlpapi.SetInterfaceDnsSettings?

目前,mkwinsyscall试图用类型转换将guid转换为uintptr,但失败了,因此代码无法编译。相反,我正在做一些相当粗暴的事情,如下所示:

//sys   setInterfaceDnsSettingsByPtr(guid *windows.GUID, settings *dnsInterfaceSettings) (ret error) = iphlpapi.SetInterfaceDnsSettings?
//sys   setInterfaceDnsSettingsByQwords(guid1 uintptr, guid2 uintptr, settings *dnsInterfaceSettings) (ret error) = iphlpapi.SetInterfaceDnsSettings?
//sys   setInterfaceDnsSettingsByDwords(guid1 uintptr, guid2 uintptr, guid3 uintptr, guid4 uintptr, settings *dnsInterfaceSettings) (ret error) = iphlpapi.SetInterfaceDnsSettings?

func setInterfaceDnsSettings(guid windows.GUID, settings *dnsInterfaceSettings) error {
        words := (*[4]uintptr)(unsafe.Pointer(&guid))
        switch runtime.GOARCH {
        case "amd64":
                return setInterfaceDnsSettingsByPtr(&guid, settings)
        case "arm64":
                return setInterfaceDnsSettingsByQwords(words[0], words[1], settings)
        case "arm", "386":
                return setInterfaceDnsSettingsByDwords(words[0], words[1], words[2], words[3], settings)
        default:
                panic("unknown calling convention")
        }
}

这样应该可以工作。但是它非常手动。对于大于GUID的结构体,这甚至更令人讨厌。所以我在考虑尝试以某种方式将其通用化。
你认为这段代码应该放在哪里?在syscall.Syscall中?还是在mkwinsyscall中?它应该看起来像什么样子?
CC @alexbrainman@bradfitz@ianlancetaylor@rozmansi

1aaf6o9v

1aaf6o9v1#

在syscall或golang.org/x/sys/windows包中定义的函数,我们可以简单地让生成的代码执行正确的操作,但我没有看到这样的函数。有吗?
目前在x/sys/windows中没有。不过在win32 API中有一些分散的函数。例如,OLE相关的函数是通过值传递GUID,而不是引用。几个月前我不得不做出这个改变:lxn/win@c5100a6#diff-bc58b257cf27988dcfdc566b7e06d2b860735a55a826eccd037b89f5bb5bcda5
可能还有其他接受结构体(除了GUID)的函数,虽然我现在想不起来具体有哪些了。
所以一个方法是让mkwinsyscall根据ABI的拆分规则将通用结构体拆分,然后为每个架构生成辅助函数并使用runtime.GOARCH选择。这可以工作,但也可能有点困难。另一个方法是仅针对windows.GUIDsyscall.GUID类型的匹配进行此操作,每次都以相同的方式拆分。这相对容易一些,但也不太健壮。而且也不是完全容易——我们需要在其中添加逻辑来检查拆分参数之前有多少个参数,以确定它实际上是否应该通过引用传递,因为没有足够的寄存器(按arm ABI)。
我在想是否有一种更聪明的方法可以在syscall.Syscall中实现,允许任意类型传递,而不仅仅是uintptr。但是通过interface{}进行转换听起来有点糟糕。这需要特殊的编译器支持吗?还是在汇编中做更灵活的跳板来处理这个问题?或者是Go2泛型?
这样的函数有多少个?
我不确定,而且这肯定不是任何紧迫需求,至少现在不是。
但我想至少应该开放讨论,作为随着事情发展要牢记的事情——winmd、泛型、regabi,也许还有其他令人兴奋的事情——如果有人对此有任何新的想法。

uoifb46i

uoifb46i2#

一个明显的例子存在于syscall/中,这是由@rsc最近的CL引起的我的注意:https://go-review.googlesource.com/c/go/+/288815

tuwxkamq

tuwxkamq3#

@gopherbot add OS-Windows

wh6knrhe

wh6knrhe4#

也许我没有理解问题。但是你应该和@aclements谈谈这个问题。
我最近审查了他的CL,解决了在回调过程中从C传递Go结构体/不同数据类型的问题。奥斯汀需要正确的C到Go数据转换,以便将来为传递寄存器中的参数做工作。甚至有runtime.TestStdcallAndCDeclCallbacks,它使用gcc构建相应的C dll来测试这种交互。你可以添加自己的数据类型来看看它们是否有效(适用于任何GOARCH)。Windows回调代码是运行时包的一部分。但我相信Go团队可以在编译器中采用类似的代码。
希望这对你有所帮助。
Alex

epggiuax

epggiuax5#

对于在syscall或golang.org/x/sys/windows包中定义的函数,我们可以简单地让生成的代码执行正确的操作,但我没有看到这样的函数。有这样的函数吗?
所以我想问的问题是:我们应该如何处理使用DLL.FindProc或类似函数加载的参数值为GUID的函数。对吗?
处理这种情况似乎很困难。我看不到任何方法来实现它。甚至不知道如何将其概括,因为GUID值对于不同的GOARCH值具有不同的参数数量。
这样的函数有多少个?

相关问题