windows 如何告诉MinGW链接器不要导出所有符号?

ippsafx7  于 2023-02-13  发布在  Windows
关注(0)|答案(7)|浏览(142)

我正在使用MinGW工具链构建一个Windows动态库。
为了构建这个库,我静态链接到其他2个提供API的库,并且我有一个.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文件,当你没有显式地使用这个选项时,它是禁用的,除非它没有;无论如何,第三方库中的符号将被导出。
编辑:添加选项--exclude-libs LIBS–exclude-symbols SYMBOLS不会阻止导出库中的符号。

jjhzyzn0

jjhzyzn01#

不知道为什么没有真实的的答案,但这里是什么对我有效:
1.编译目标文件:

g++ -O0 -gdwarf-4 dll\dllmain.cpp -c -o dllmain.o

1.链接(-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

(x一米一米一x = x一e0一米一x)

wsewodh2

wsewodh22#

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

7ajki6be

7ajki6be3#

这是一个反复出现的问题。以下是《战略目标》中两个相关的问题:

和外部SO:

即,Windows平台上的全局/本地导出不在链接器级别处理,而是提供.def文件以补充.dll
由于缺乏一个好的答案,我做了一个python脚本,负责从dll导出表中删除内容,您可以找到它here

tktrz96b

tktrz96b4#

如果您的binutils发行版(本机或交叉编译)提供了dllwrap,则可以使用它。
它可以使用DEF文件中的接口生成DLL(在底层它调用gcc、ld和dlltool来完成此操作),使用此接口与直接将DEF文件传递给GCC之间的区别在于文件中的定义被区别对待。
例如,如果导出文件中有一个重命名符号:

_SomeFuntion = _SomeFunction@12

GCC会创建两个导出,一个名为_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调用它。它可以生成一个导入库,以防你也需要它。

o2gm4chl

o2gm4chl5#

您是否在提供了链接的页面上阅读了以下内容,关于未显式使用--export-all-symbols时的行为-在以下情况下禁用自动导出:
任何对象文件中的任何符号都标记有__declspec(dllexport)属性。
你有没有试过只导出你感兴趣的函数?在DEF文件中很容易出现名字错误,这是因为mangling,所以这个方法应该更可靠。

hpxqektj

hpxqektj6#

免责声明:我只在Linux上做过这个,但AFAIK它应该也能在Windows上工作
您可以使用-fvisibility=hidden选项;有关详细信息,请参见http://gcc.gnu.org/wiki/Visibility

wtlkbnrh

wtlkbnrh7#

您可以在GNU LD中使用version script来创建导出符号的允许列表。
假设在library.cpp中有一个library_somefunc函数,而您只想导出该符号,您可以准备一个名为version-script.txt的纯文本文件:

{
    global:
        library_somefunc;

    local:
        *;
};

通过将library_somefunc指定为global,您可以指示该符号应该在动态链接库中导出。我们将*指定为local,以便不导出其他符号。
然后,使用以下命令生成dll:

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

可以使用Dependencies检查DLL文件是否正确导出符号。

相关问题