Получение исключения при вызове функции Syscall - PullRequest
1 голос
/ 25 июня 2019

Я использую пакет syscall для Go, чтобы вызвать DLL, написанную на C ++.

Подпись метода 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 0x7fffe30bdb33
PC=0x7fffe30bdb33

syscall.Syscall9(0x7fffe32db600, 0x7, 0x97db50, 0xc00007ff10, 
0xc00007ff70, 0xc000054180, 0xc0000541a0, 0xc0000541c0, 0xc0000541e0, 
0x0, ...)

c:/go/src/runtime/syscall_windows.go:210 +0xf3
main.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 Ответ

0 голосов
/ 25 июня 2019

Параметр

Итак, в основном, у вас есть отображение

  1. int* bufferbuffer uintptr

  2. int argcunsafe.Pointer(&argsCount), где &argsCount - указатель на int

  3. char* argv[]unsafe.Pointer(&args), где args равно []string

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

    const char* key, const char* prefix, const char* version - то же, что и выше.

Проблемы

Что с этим не так.

  1. Передача uintptr значений, содержащих адреса живых объектов Go между функциями, запрещена.(Подробнее об этом позже.)

  2. Адрес аргумента функции argsCount передается argc.Адрес на типичной товарной платформе / ОС, такой как amd64 / Windows, является безумно огромным значением - если интерпретировать его как число.

    Моя ставка заключается в том, что функция вылетает при попытке прочитать столько элементов из argv - заставляет его читать память, которую ваш процесс не отображал.

  3. Здесь две проблемы:

    1. Передача адреса значения слайса в видеаргумент, ожидающий адрес первого элемента этого среза, неверен.

      Это потому, что значение среза (в настоящее время в реализации «ссылки» Go, которую вы предположительно используете) представляет собой структуру из 3 аргументов:адрес базового массива данных, количество элементов в этом массиве, которое можно использовать, и общее количество таких элементов в этом массиве, которые могут использоваться без перераспределения функцией append при вызове значения слайса.

      Когда у вас есть args []string и вы делаете &args, вы получите адрес этой структуры, а не адрес первого элемента этого среза.

      Чтобы сделать последнее, используйте &args[0].

    2. В Go строка (обычно, и давайте предположим, что это так) содержит символы, закодированные как UTF-8.Как правило, это не то, с чем может столкнуться нативный код C ++ для Windows, когда он говорит, что хочет 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_t или WCHAR.

    Если да, то, что вы делаете, должно быть в порядке;в противном случае это не так.Вы должны проверить это.

Примечание о передаче uintptr s между выражениями

Go содержит сборку мусора, и поэтому во время выполнения он должен знать все указатели на всев данный момент живые объекты.Пока существует переменная, содержащая указатель на блок памяти, выделенный во время выполнения программы, этот блок не будет собираться мусором.

Значение unsafe.Pointer считается правильной ссылкой на памятьблок , но значение uintptr отсутствует. Это означает, что когда код

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

работает, GC может освободить объект, выделенный p как только p был назначен его адрес - просто потому, что p является единственной ссылкой на этот объект, а u - нет.

Теперь рассмотрим, что GC в эталонной реализации Go выполняетсяодновременно с кодом вашей программы.Это означает, что когда foo выполняется, то же самое может быть и GC, и он может смести ваш экземпляр someType прямо из-под ног foo.

В качестве исключения из этого общего правила, 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 как uintptr s , если только они не содержат указателей, полученных со стороны "C".

...