【问题标题】:How to tell the MinGW linker not to export all symbols?如何告诉 MinGW 链接器不要导出所有符号?
【发布时间】:2011-02-18 02:26:50
【问题描述】:

我正在使用 MinGW 工具链构建一个 Windows 动态库。

为了构建这个库,我静态链接到提供 API 的其他 2 个库,并且我有一个 .def 文件,我在其中编写了我想在我的库中导出的唯一符号。

问题是 GCC 正在导出所有符号,包括我链接到的库中的符号。有没有办法告诉链接器只导出def 文件中的符号?

我知道有--export-all-symbols 选项,但似乎没有相反的选项。

现在构建脚本的最后一行具有这样的结构:

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

编辑:在关于链接器的docs 中,它说--export-all-symbols 是默认行为,如果您提供def 文件,则当您不明确使用该选项时,它会被禁用,除非它没有'吨;无论如何,第 3 方库中的符号正在被导出。

编辑:添加选项 --exclude-libs LIBS–exclude-symbols SYMBOLS 不会阻止导出库中的符号。

【问题讨论】:

  • --exclude-libs ALL 选项会很有趣

标签: c++ windows dll gcc g++


【解决方案1】:

不知道为什么没有真正的答案,但这对我有用:

  1. 目标文件的编译:

    g++ -O0 -gdwarf-4 dll\dllmain.cpp -c -o dllmain.o
    
  2. 链接(-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)

【讨论】:

    【解决方案2】:

    这是一个反复出现的问题。以下是 SO 中的两个相关问题:

    在 SO 之外:

    即Windows 平台上的全局/本地导出不在链接器级别处理,而是提供.def 文件以补充.dll

    由于没有好的答案,我做了一个 python 脚本,负责从 dll 导出表中删除内容,你可以找到它here

    【讨论】:

      【解决方案3】:

      您可以使用选项-Wl,--retain-symbols-file=file,然后在file 中列出您想要保留的符号(每行一个)。这将导致链接器丢弃所有其他符号,只保留您想要的符号。

      【讨论】:

      【解决方案4】:

      如果您的 binutils 发行版(本机或交叉编译)提供了 dllwrap,则可以使用它。

      它可以使用 DEF 文件中的接口生成 DLL(在后台它调用 gcc、ld 和 dlltool 来执行此操作)。使用它和直接将 DEF 文件传递​​给 GCC 的区别在于文件中的定义被区别对待。

      例如,如果您在导出文件中有符号重命名:

      _SomeFuntion = _SomeFunction@12
      

      GCC 将创建 2 个导出,一个名为 _SomeFunction,另一个具有修饰名称,而 dllwrap 只会导出 _SomeFuntion。因此,如果您只将要导出的符号添加到 DEF 文件中,您最终只会在库中使用它们。

      dllwrap 默认使用 C 编译器驱动程序,因为它无法知道其他情况。当您链接 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 年就已经是 deprecated 并且在官方的 binutils 包中没有关于它的文档;要获取一些信息,您可以像往常一样使用--help 调用它。如果您也需要它,它可以生成一个导入库。

      【讨论】:

      • 感谢您的回答!它可以工作,除非在def 文件中指定了符号,否则它们不会被导出,即使是库中的符号也是如此。我将使用 dllwrap,我正在使用 binutils 2.20 并且 dllwrap 仍然存在,所以它可能仍然被维护。如果它已被弃用......难道不应该有办法只使用 gcc 和 cia 来做同样的事情吗?
      • @James 首选的方法是用dllexport/dllimport 标记源中的符号,并像这样处理符号可见性。
      【解决方案5】:

      您是否在提供链接的页面上阅读了此内容,关于未明确使用 --export-all-symbols 时的行为 - 在以下情况下禁用自动导出:

      任何目标文件中的任何符号都是 标有 __declspec(dllexport) 属性。

      您是否尝试过仅显式导出您感兴趣的函数? DEF 文件中很容易弄错名字,因为 mangling,所以这种方法应该更可靠。

      【讨论】:

      • 我已经标记了我想要导出的符号,它们是,但我无法访问我链接到的库的源代码。我不能把那个标记从那里拿走。
      • @James 恐怕一旦导出完成,您就无法取消导出。
      • 啊哈,那就谢谢你了,所以其他库导出的符号一定要保留。
      【解决方案6】:

      免责声明:我只在 Linux 上做过这个,但 AFAIK 它应该也可以在 Windows 上工作

      您可以使用-fvisibility=hidden 选项;欲了解更多信息,请参阅http://gcc.gnu.org/wiki/Visibility

      【讨论】:

      • 恐怕它在 Windows 环境中没有帮助(至少在我的编译器中)。即使在 foo 测试中,我也会收到“此配置不支持的可见性属性;忽略”警告,但无论如何感谢!
      猜你喜欢
      • 2012-11-30
      • 2011-03-17
      • 2014-04-14
      • 2016-01-20
      • 1970-01-01
      • 2014-06-16
      • 1970-01-01
      • 1970-01-01
      • 2015-07-14
      相关资源
      最近更新 更多