Получить причину, по которой LoadLibrary не может загрузить DLL - PullRequest
2 голосов
/ 27 мая 2020

На Linux и Ma c, при использовании dlopen() для загрузки общей библиотеки, которая связана с другой библиотекой, если связывание не удается из-за отсутствующего символа, вы можете получить имя отсутствующего символа с помощью dlerror(). Он говорит что-то вроде

dlopen failed: cannot locate symbol "foo"

На Windows, при использовании LoadLibrary() для загрузки DLL с отсутствующим символом вы можете получить код ошибки только из GetLastError(), который для этого типа проблемы будет всегда быть 127. Как я могу определить, какой символ отсутствует, или более подробное сообщение об ошибке из LoadLibrary(), объясняющее, почему функция завершилась неудачно?

Ответы [ 2 ]

0 голосов
/ 27 мая 2020

Я придумал способ, используя терминал MSYS2 . Другие методы могут работать с программным обеспечением GUI. Главное предостережение заключается в том, что это невозможно сделать на чистом C / C ++ и выпустить для конечных пользователей. Это только для разработчиков, но это лучше, чем ничего.

Установите Debugging Tools для Windows, загрузив Windows SDK и сняв все флажки, кроме Debugging Tools. Я могу ошибаться, но похоже, что установка этого программного обеспечения устанавливает ловушку в ядро ​​Windows, позволяющую LoadLibrary() записывать подробную информацию в stderr.

Откройте терминал MSYS2 Mingw64 от имени администратора и запустите

'/c/Program Files (x86)/Windows Kits/10/Debuggers/x64/gflags.exe' -i main.exe +sls

Это выводит на терминал следующее, чтобы подтвердить, что реестр был изменен.

Current Registry Settings for main.exe executable are: 00000002
    sls - Show Loader Snaps

Используйте -sls вместо +sls, если вам нужно отменить, поскольку я считают, что изменение происходит для всех программ, называемых main.exe в Windows, глобально, а не только для вашего файла.

Затем запуск main.exe должен распечатать отладочную информацию в stderr, но поскольку я отлаживаю приложение -mwindows, оно у меня не работает.

Но по какой-то причине запуск двоичного файла с помощью gdb MSYS2 позволяет распечатать эту отладочную информацию в stderr. Установите mingw-w64-x86_64-gdb с MSYS2, запустите gdb ./main.exe и введите run или r. Найдите раздел, похожий на следующий.

warning: 1ec8:43a0 @ 764081125 - LdrpNameToOrdinal - WARNING: Procedure "foo" could not be located in DLL at base 0x000000006FC40000.
warning: 1ec8:43a0 @ 764081125 - LdrpReportError - ERROR: Locating export "foo" for DLL "C:\whatever\plugin.dll" failed with status: 0xc0000139.
warning: 1ec8:43a0 @ 764081125 - LdrpGenericExceptionFilter - ERROR: Function LdrpSnapModule raised exception 0xc0000139
    Exception record: .exr 00000000050BE5F0
    Context record: .cxr 00000000050BE100
warning: 1ec8:43a0 @ 764081125 - LdrpProcessWork - ERROR: Unable to load DLL: "C:\whatever\plugin.dll", Parent Module: "(null)", Status: 0xc0000139
warning: 1ec8:43a0 @ 764081171 - LdrpLoadDllInternal - RETURN: Status: 0xc0000139
warning: 1ec8:43a0 @ 764081171 - LdrLoadDll - RETURN: Status: 0xc0000139

Отлично! Здесь написано Procedure "foo" could not be located in DLL, значит, у нас есть отсутствующий символ, как в POSIX / UNIX s dlopen().

0 голосов
/ 27 мая 2020

Хотя ответ Реми Лебо технически верен, определение отсутствующего символа с помощью GetLastError () все еще возможно на платформе Windows. Чтобы понять, чего именно не хватает, важно понимать терминологию.

Символ:

Когда DLL компилируется, на ее функции ссылаются символы. Эти символы напрямую относятся к имени функции (символы представлены видимыми и читаемыми строками), ее типу возврата и ее параметрам. На самом деле символы можно прочитать непосредственно через текстовый редактор, хотя их трудно найти в больших библиотеках DLL. Символы DLL - C ++ Forum

Отсутствие символа означает, что функция внутри не может быть найденным. Если эта ошибка возникает до использования GetProcAddress (), то возможно, что любое количество функций не может быть загружено из-за отсутствия предварительных условий. Это означает, что возможно, что библиотека, которую вы пытаетесь загрузить, также требует библиотеки, которую первая не может загрузить. Эти уровни зависимости могут go для неизвестного количества слоев, но единственный ответ, который может определить GetLastError (), - это то, что был пропущенный символ. Одним из таких методов является использование Dependency Walker для определения недостающей библиотеки, необходимой для первой библиотеки. Как только все необходимые библиотеки доступны и могут быть найдены этой библиотекой (которая может быть отдельной банкой червей), эта библиотека может быть загружена с помощью LoadLibrary ().

...