Как сообщить линкеру MinGW не экспортировать все символы?

Я создаю динамическую библиотеку Windows с помощью инструментальной комбинации MinGW.

Чтобы создать эту библиотеку, я статически связываюсь с другими 2, предлагающими API, и у меня есть файл .def, где я написал единственный символ, который я хочу экспортировать в своей библиотеке.

Проблема заключается в том, что GCC экспортирует все символы, включая те из библиотек, к которым я привязываюсь. В любом случае, чтобы сообщить компоновщику просто экспортировать символы в файл def?

Я знаю, что есть опция --export-all-symbols, но, похоже, это не так.

В настоящее время последняя строка сборки script имеет следующую структуру:

g++ -shared CXXFLAGS DEFINES INCLUDES -o library.dll library.cpp DEF_FILE \
OBJECT_FILES LIBS -Wl,--enable-stdcall-fixup

EDIT: В docs о компоновщике говорится, что --export-all-symbols является поведением по умолчанию и что он отключен, когда вы не используете эту опцию явно, если вы предоставляете файл def, за исключением случаев, когда это не так; в любом случае экспортируются символы в сторонних библиотеках.

EDIT: добавление опции --exclude-libs LIBS или –exclude-symbols SYMBOLS не позволяет экспортировать символы из библиотек.

Ответы

Ответ 1

Вы можете использовать dllwrap, если это дает вам дистрибутив binutils (как встроенного, так и кросс-компиляции).

Он может создавать библиотеки DLL, используя интерфейс в DEF файле (под капотом он вызывает gcc, ld и dlltool для этого). Разница между использованием этого и передачей DEF файла в GCC напрямую заключается в том, что определения в файле обрабатываются по-разному.

Например, если у вас есть символ переименования в файле экспорта:

_SomeFuntion = [email protected]

GCC создаст 2 экспорта, один по имени _SomeFunction, а другой - с украшенным именем, а dllwrap будет экспортировать только _SomeFuntion. Поэтому, если вы только добавите в файл DEF символы, которые хотите экспортировать, вы попадете только в них в библиотеке.

dllwrap по умолчанию использует драйвер компилятора C, поскольку он не знает иначе. Когда вы связываете код С++, вы должны использовать опцию --driver-name c++ для установки драйвера. Если у вас есть исполняемые файлы MinGW с префиксом, вы должны включить его также в имя драйвера (например, i686-mingw32-c++ вместо c++), и вам может понадобиться также использовать параметр --dlltool-name.

Попробуйте использовать эти две строки вместо той, которую вы разместили:

g++ -c CXXFLAGS DEFINES INCLUDES -o library.o library.cpp
dllwrap -o library.dll --driver-name c++ --def DEF_FILE OBJECT_FILES LIBS -Wl,--enable-stdcall-fixup

Первый генерирует объектный файл из кода library.cpp, а второй собирает динамическую библиотеку. Объект OBJECT_FILES (который, как я полагаю, является другим объектным файлом, созданным ранее), должен иметь library.o.

Тем не менее, я должен сказать, что dllwrap уже был устарел в 2006 году, и в официальном пакете binutils нет документации; для получения информации вы можете назвать ее с помощью --help, как обычно. Он может генерировать библиотеку импорта, если она вам тоже нужна.

Ответ 2

Не знаю, почему на это нет реального ответа, но вот что для меня работало:

  • Компиляция ваших объектных файлов:

    g++ -O0 -gdwarf-4 dll\dllmain.cpp -c -o dllmain.o
    
  • Связывание (-Wl,--exclude-all-symbols важно):

    g++ -Wl,--enable-auto-import -Wl,--out-implib,libsomelib.a -Wl,--exclude-all-symbols -shared dllmain.o -o somelib.dll
    

Затем вы выбираете, какие функции экспортировать непосредственно в исходный код вашей DLL:

#include <windows.h>

__declspec(dllexport) void someExportedFunction() {
    MessageBox(NULL, "msgbox", "msgbox", MB_OK);
}

void nonExportedFunction() {
    MessageBox(NULL, "notexported", "notexported", MB_OK);
}

Проверка:

C:\libtest>pedump -E somelib.dll

=== EXPORTS ===

# module "somelib.dll"
# flags=0x0  ts="2014-02-20 08:37:48"  version=0.0  ord_base=1
# nFuncs=1  nNames=1

  ORD ENTRY_VA  NAME
    1     1570  _Z20someExportedFunctionv

(pedump= http://pedump.me)

Ответ 3

Это повторяющаяся проблема. Вот два связанных вопроса в SO:

и вне SO:

то есть. глобальный/локальный экспорт на платформе Windows не обрабатывается на уровне компоновщика, а предоставляется файл .def для дополнения .dll.

Отсутствие хорошего ответа, я сделал python script, который заботится о удалении файлов из таблицы экспорта dll, вы можете найти его здесь.

Ответ 4

Вы прочитали это на странице, на которую вы предоставили ссылку, относительно поведения, если --export-all-symbols не используется явно - автоматический экспорт отключен, если:

Любой символ в любом объектном файле отмечен __declspec (dllexport) атрибут.

Вы пытались явно экспортировать только те функции, которые вас интересуют? Очень легко получить неправильные имена в DEF файле, из-за mangling, поэтому этот метод должен быть более надежным.

Ответ 5

Вы можете использовать опцию -Wl,--retain-symbols-file=file, а затем перечислить символы, которые вы хотите сохранить (по одной в строке), в file. Это заставит компоновщик сбросить все остальные символы, сохранив только те, которые вы хотите.

Ответ 6

Отказ от ответственности: я сделал это только в Linux, но AFAIK также должен работать на Windows

Вы можете использовать опцию -fvisibility=hidden; для получения дополнительной информации см. http://gcc.gnu.org/wiki/Visibility