为了账号安全,请及时绑定邮箱和手机立即绑定

调用 Syscall 函数时出现异常

调用 Syscall 函数时出现异常

Go
炎炎设计 2023-06-26 17:18:32
我正在使用 Go 的系统调用包来调用用 C++ 编写的 DLL。C++ 方法签名如下所示。init(int* buffer, int argc, char* argv[], const char* fileName, const char* key, const char* prefix, const char* version)这是我用来在 Go 中调用上述方法的函数。func init(  buffer uintptr,   argsCount int,   args []string,   fileName string,   key string,   prefix string,   version string) uintptr {    // libHandle is handle to the loaded DLL    methodAddress := getProcAddress(libHandle, "init")    status, _, err := syscall.Syscall9(      methodAddress,      7,      buffer,      uintptr(unsafe.Pointer(&argsCount)),      uintptr(unsafe.Pointer(&args)),      uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(fileName))),      uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(key))),      uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(prefix))),      uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(version))),      0,      0)      fmt.Println(err.Error())      return status }当我调用此方法并且对此没有任何想法时,我收到此错误。Exception 0xc0000005 0x0 0x0 0x7fffe30bdb33PC=0x7fffe30bdb33syscall.Syscall9(0x7fffe32db600, 0x7, 0x97db50, 0xc00007ff10, 0xc00007ff70, 0xc000054180, 0xc0000541a0, 0xc0000541c0, 0xc0000541e0, 0x0, ...)c:/go/src/runtime/syscall_windows.go:210 +0xf3main.main() E:/Path/test.go:157 +0x2be rax     0x81fbf0 rbx     0x1 rcx     0x7ff804ad1310 rdi     0x7ff10 rsi     0xc00007ff70 rbp     0x0 rsp     0x81fbc0 r8      0x0 r9      0x7ff804ad0000 r10     0xc00007ff00 r11     0x81fbf0 r12     0x7ff10 r13     0xc00007ff70 r14     0xc000054180 r15     0x97db50 rip     0x7fffe30bdb33 rflags  0x10257 cs      0x33 fs      0x53 gs      0x2b
查看完整描述

1 回答

?
小唯快跑啊

TA贡献1863条经验 获得超2个赞

那个设定

所以,基本上,你得到的映射是

  1. int* buffer → buffer uintptr

  2. int argc→ unsafe.Pointer(&argsCount),其中&argsCount是一个指向int

  3. char* argv[]→ unsafe.Pointer(&args),哪里args[]string

  4. const char* fileNameunsafe.Pointer(syscall.StringToUTF16Ptr(fileName))

    const char* keyconst char* prefixconst char* version— 同上。

存在的问题

现在这有什么问题呢。

  1. uintptr禁止在函数之间传递包含活动 Go 对象地址的值。(稍后会详细介绍。)

  2. argsCount您将函数参数的地址传递给argc。如果将其解释为计数,则典型的商品平台/操作系统(例如 amd64/Windows)上的地址是一个极其巨大的值。

    我敢打赌,当函数尝试从中读取这么多元素时,它会崩溃,导致argv它读取进程未映射的内存。

  3. 这里有两个问题:

    1. 将切片值的地址作为参数传递,期望该切片的第一个元素的地址是错误的。

      这是因为切片值(目前,在您应该使用的“参考”Go 实现中)是具有struct3 个字段的:底层数据数组的地址、该数组中允许使用的元素数以及总数该数组中此类元素的计数append,在对切片值调用时,函数可以使用这些元素,而无需重新分配。

      当您拥有args []string并执行时&args,您将获得该结构的地址,而不是该切片的第一个元素的地址。

      要执行后者,请使用&args[0].

    2. 在 Go 中,字符串(通常是这样,我们假设是这样)包含编码为 UTF-8 的字符。这通常不是 Windows 本机 C++ 代码在表示需要char *.

      我猜,你需要首先为 构建一个合适的东西argv,比如

      argv := make([]unsafe.Pointer, 0, len(args))for i, s := range args {
          argv[i] = unsafe.Pointer(syscall.StringToUTF16Ptr(s))
      }

      然后传递&argv[0]给被调用者。

      但请参阅下文syscall.StringToUTF16Ptr()

  4. 您正在做的将字符串数据传递给具有该类型的其余参数的准备工作const char*似乎是正确的,但前提是被调用者确实意味着它char是一个 16 位整数。

    换句话说,用于编译该库的源代码和工具链必须确保它char确实是wchar_tWCHAR

    如果是,那么你的做法应该没问题;否则就不是。你应该验证这一点。

uintptr关于在表达式之间传递 s 的注意事项

Go 具有垃圾收集功能,因此它的运行时必须知道指向所有当前活动对象的所有指针。只要存在一个变量,其中包含指向在程序运行时分配的内存块的指针,该块就不会被垃圾收集。

unsafe.Pointer算作对内存块的正确引用,uintptr则不然。这意味着当代码

p := new(someType)
u := uintptr(unsafe.Pointer(&p))
foo(u)return

p正在运行时,一旦分配了地址,GC 就可以自由地回收由 分配的对象p,因为p是对该对象的唯一引用,但u事实并非如此。

现在考虑参考 Go 实现中的 GC 与程序代码同时运行。这意味着当foo运行时,GC 也可能会执行,并且它可能会将您的实例someTypefoo.

作为此一般规则的一个例外,Go 编译器保证uintptr(unsafe.Pointer)同一语言表达式中发生的所有类型转换都不受 GC 的影响。所以以我们之前的例子为例,这样做就可以了

foo(uintptr(unsafe.Pointer(new(someType)))return

p := new(someType)
v := unsafe.Pointer(&p)
foo(uintptr(v))return

因为类型转换发生uintptr在单个表达式中——这是一个函数调用。

因此,您不能将指向 Go 对象的指针作为uintptrs传递,除非它们包含从“C 端”获取的指针。


查看完整回答
反对 回复 2023-06-26
  • 1 回答
  • 0 关注
  • 116 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信