← Архив: Common Lisp

SBCL, CFFI, Windows, ошибка загрузки библиотеки

Author: · 09.11.2012 15:35
· original author: Norgat
Здравствуйте, не получается загрузить, через CFFI, собственную dll.
Сам CFFI работает:
Загрузка системной dll.
CL-USER> (cffi:load-foreign-library "user32.dll")
STYLE-WARNING: Undefined alien: "xor_int"
#<CFFI:FOREIGN-LIBRARY USER32.DLL-935 "user32.dll">
Тестовый вызов сишной функции:
CL-USER> (cffi:foreign-funcall "strlen" :string "hello" :int)
5
Но при попытке загрузить собственную библиотеку (:search-path крутить пробовал, безуспешно, подсовывание dll в system32 тоже не работает):
(cffi:load-foreign-library "D:/AutLib.dll")
Получаю:
Unable to load foreign library (AUTLIB.DLL-944).
 Error opening shared object "D:\\AutLib.dll":
 193.
  [Condition of type CFFI:LOAD-FOREIGN-LIBRARY-ERROR]
...
Backtrace:
 0: (CFFI::FL-ERROR "Unable to load foreign library (~A).~%  ~A" #:AUTLIB.DLL-944 "Error opening shared object \"D:\\\\AutLib.dll\":\n  193.")
 1: (CFFI::REPORT-SIMPLE-ERROR #:AUTLIB.DLL-944 #<SIMPLE-ERROR "Error opening shared object ~S:~%  ~A." {10030C4273}>)
 2: (CFFI::LOAD-FOREIGN-LIBRARY-PATH #:AUTLIB.DLL-944 "D:/AutLib.dll" NIL)
 3: ((FLET CFFI::%DO-LOAD :IN CFFI::%DO-LOAD-FOREIGN-LIBRARY) #<CFFI:FOREIGN-LIBRARY AUTLIB.DLL-944> #:AUTLIB.DLL-944 "D:/AutLib.dll")
 4: (CFFI::%DO-LOAD-FOREIGN-LIBRARY "D:/AutLib.dll" NIL)
 5: (CFFI:LOAD-FOREIGN-LIBRARY "D:/AutLib.dll" :SEARCH-PATH NIL)
 6: (SB-INT:SIMPLE-EVAL-IN-LEXENV (CFFI:LOAD-FOREIGN-LIBRARY "D:/AutLib.dll") #<NULL-LEXENV>)
 7: (EVAL (CFFI:LOAD-FOREIGN-LIBRARY "D:/AutLib.dll"))
Сама библиотека создана правильно вроде (все функции в extern "C" обёрнуты + _declspec(dllexport)) и в LispWorks, через FLI, отлично дёргается.
· original author: michael.filonenko
зависимости, компиляторы?
· original author: Norgat
Зависимостей нет левых. В либе используются только стандартные сишные функции. Собираю с помощью VS2012, в принципе можно пересобрать с помощью minGW, который идёт вместе с QtCreator.
· original author: Norgat
Собственно собрал с помощью gcc из поставки QtCreator:
C:\Windows\system32>g++ --version g++ (GCC) 4.4.0 Copyright (C) 2009 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Эффект тот же.
· original author: Norgat
Сейчас собрал этот же код под Linux:norgat@ubuntu:~$ g++ --version g++ (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2
Всё отлично заработало:
CL-USER> (cffi:load-foreign-library "~/libALib.so")
#<CFFI:FOREIGN-LIBRARY LIBALIB.SO-960 "libALib.so">
CL-USER> (cffi:foreign-funcall "xor_int" :int 1 :int 2 :int)
1
Интересно, где косяк с CFFI под Windows...
· original author: Love5an
QtCreator? А в либе C++ ?
· original author: Love5an
с CFFI косяк очень вряд ли, на самом деле
а SBCL какой, sbcl-win32-threads от Антона Коваленко?
· original author: Norgat
Либа написана на C++, но реально там нету крестов, т.е. всё, что там внутри и торчит наружу можно переписать на чистом C(раньше там был заюзан STL, но потом был выкинут к чертям). Как уже говорилось, все внешние функции завёрнуты в extern "C".
sbcl-win32-threads от Антона Коваленко?Да, он.
Самое смешное, что под Linux тот же код (один в один, за исключением __declspec(dllexport)), собранный тем же QtCreator, отлично грузится через CFFI и работает, как и в винде, но из LispWorks с тамошним FFI. Может я что-то с путями поиска не то делаю и CFFI банально не видит мою либу?
· original author: den73
> CFFI банально не видит мою либу?
Попытка открытия файлов диагностируется
программой filemon из sysinternals.
· original author: Love5an
В DllMain что-нибудь есть?Линковка с libstd++ какая? статическая/динамическая?
Если собрать с MSVC++ - работает?
Просто библиотеки на обычном Си грузит? Системные, как я понимаю - грузит.
Если написать тупо какой-нибудь helloworld.dll, скомпилировать тем же компилятором, и экспортировать оттуда одну функцию?
· original author: Love5an
Короче, сдается проблема в линковке с libgcc и libstdc++
· original author: Norgat
Системные грузит, мои - нет. Тест с простейшей dll делал.
header.h: extern "C" { _declspec(dllexport) int echo (int a); } source: #include "header.h" int echo (int a) { return a + 1; }
CFFI один фиг выдаёт 193 ошибку загрузки shared object'а. Компилировал MSVC ест. (про QtCreator вообще забудь, я им компилировать попробовал ради теста).
А вот в LispWorks всё работало опять же.
P.S. DllMain пустой у меня. Это кусок числодробилки, там нечего инициализировать.
P.S.S. Вообще говоря, мне это надоело и я переполз под Linux. Там CFFI работает отлично. Хотя понять почему вылезает эта проблема было бы не плохо.
· original author: Norgat
Спасибо за наводку. Правда FileMon устарел, теперь предлагается использовать  Process Monitor, но не суть важно.
SBCL к либе лезет.
Делает:
Имя операции -> Результат QueryOpen -> SUCCESS CreateFile -> SUCCESS CreateFileMapping -> FILE_LOCKED_WITH_ONLY_READERS CreateFileMapping -> SUCCESS CloseFile -> SUCCESS
Т.е. с путями проблем нет как я понимаю.
· original author: Love5an
Имхо проблема где-то в районе линковки со стандартной библиотекой. (то есть DLL то он находит, я вижу)
Если собирать с /TC (используем Си, а не C++) - она имхо отпадет. Если собрать с /MT (статическая линковка со стандартной библиотекой) - имхо тоже.
Кстати, какая винда?
То есть если бы проблемы была с SBCL - системные либы то тоже бы не грузились, и вообще, т.к. sbcl-win32-threads активно использует WinAPI, он бы просто не работал.
У меня вот проект на C++ тоже. 
https://github.com/Lovesan/D3DWorkshop
Собираю MSVC++  - всё ништяк, и SBCL в том числе отлично всё грузит, естественно.
Может SBCL криво собран? Вот попробуй мою сборку:
https://dl.dropbox.com/u/5521262/sbcl-1.1.0.36.mswinmt.1201-284e340.msi
· original author: Love5an
Про D3DW:
(in-package #:cl-user)
(defpackage #:d3dw-app
  (:use #:cl #:cffi)
  (:export #:main)
)

(in-package #:d3dw-app)
(define-foreign-library ole32
  (t (:default "ole32"))
)

(define-foreign-library d3dw
  (t (:default "D:/Dev/D3DW/D3DWorkshop/Bin/D3DW"))
)
;; 0.1.1.1
(use-foreign-library ole32)
(use-foreign-library d3dw)
(defconstant cw-use-default #x80000000)
(defcfun ("OleInitialize" ole-initialize
          :convention :stdcall
          :library ole32
)

    :int32
  (reserved :pointer)
)

(defcfun ("OleUninitialize" ole-uninitialize
          :convention :stdcall
          :library ole32
)

    :void
)

(defcfun ("D3DWCreateWindow" d3dw-create-window
          :convention :stdcall
          :library d3dw
)

    :int32
  (width :uint)
  (height :uint)
  (x :uint)
  (y :uint)
  (parent :pointer)
  (pp-out :pointer)
)

(defun %iunknown-release (p)
  (foreign-funcall-pointer
      (mem-aref (mem-ref p :pointer) :pointer 2) ;; IUnknown::Release
     (:convention :stdcall)
    :pointer p
    :uint32
)
)

(defun %id3dwwindow-show-dialog (p)
  (let ((hr (foreign-funcall-pointer
                (mem-aref (mem-ref p :pointer) :pointer 15) ;; ID3DWWindow::ShowDialog
               (:convention :stdcall)
              :pointer p
              :int32
)
)
)

    (if (minusp hr)
      (error "Failed to show dialog. HRESULT: ~8,'0X"
             (logand #xFFFFFFFF hr)
)

      T
)
)
)

(defun %id3dwwindow-set-title (p title)
  (let ((hr (foreign-funcall-pointer
                (mem-aref (mem-ref p :pointer) :pointer 19) ;; ID3DWWindow::SetTitle
               (:convention :stdcall)
              :pointer p
              (:string :encoding :utf-16/le) title
              :int32
)
)
)

    (if (minusp hr)
      (error "Failed to set title. HRESULT: ~8,'0X"
             (logand #xFFFFFFFF hr)
)

      T
)
)
)

(defun main ()
  (when (minusp (ole-initialize (null-pointer)))
    (error "Unable to initialize COM runtime")
)

  (unwind-protect
      (with-foreign-object (pp-out :pointer)
        (when (minusp (d3dw-create-window
                        cw-use-default
                        cw-use-default
                        cw-use-default
                        cw-use-default
                        (null-pointer)
                        pp-out
)
)

          (error "Unable to create ID3DWWindow")
)

        (let ((window (mem-ref pp-out :pointer)))
          (unwind-protect
              (progn
                (%id3dwwindow-set-title window "Hello from SBCL!")
                (%id3dwwindow-show-dialog window)
)

            (%iunknown-release window)
)
)
)

    (ole-uninitialize)
)
)
· original author: Love5an
Кстати, есть еще один вариант.Какая винда, я имею ввиду, 64 бит или нет? И какие, соответственно, версии битности SBCL, и библиотеки?
· original author: Norgat
/TC /MT есть. Сейчас попробуй твой проект собрать и загрузить, посмотрим что получится.
Винда - W7 x64. SBCL x86_64, либа x86.
· original author: Norgat
Ах, смена SBCL на твою помогла, теперь моя либа грузится.
· original author: Norgat
Спасибо за помощь)
· original author: Norgat
Собственно сборка, с которой Не работало: sbcl-1.1.0.36.mswinmt.1201-284e340-x86-64
Брал тут: https://github.com/akovalenko/sbcl-win32-threads/wiki
· original author: Love5an
О, ну я так и думал.
Я имею ввиду, x86-64 SBCL и x86-библиотека. Битность разная. Кстати, моя либа, если говорить о файлах проекта MSVC++ - тоже x86. Но ее, разумеется, можно и под x64 скомпилить, достаточно 64-битного компилятора MSVC++, и создания конфигурации под x64 в студии.
Если x86-64 версию своей либы собрать? Ну или взять x86 SBCL чтобы использовать x86 библиотеку.
А LW почему работает - так он в бесплатной версии(я так понимаю, именно она юзается) - только 32-битный.
Соответственно, либу, которая тоже 32-бит - отлично грузит.
К 64-битной программе 32-битную либу прилинковать нельзя. 
· original author: Love5an
Ну я так и думал, как я уже сказал - просто битность разная.
Моя сборка SBCL - x86, то есть 32 бит. А у тебя была x64.
· original author: Love5an
Соответственно, ответ на то, почему под линуксом работает.Под линуксом GCC по дефолту компилирует под ту платформу, на которой работает.
То есть, и SBCL там 64 бит, и твоя либа получается 64 бит, и все нормально.
А если бы разрядности не совпадали, тоже не грузилось бы.
· original author: Love5an
И кстати насчет системных либ. Системные библиотеки на винде существуют в двух вариантах - для 32 бит, и для 64 бит. Чтобы можно было запускать программы и для x86 и для x86-64.Но, это два параллельных мира(32 и 64), и они никак между собой не взаимодействуют, кроме как через IPC только если.
На линуксе то же самое.