使用 CMake 从静态库链接 Windows DLL 文件,无需手工制作未解析的符号名称
情况
我正在使用 Visual Studio 2008 SP1(专业版,32 位和 64 位)的 构建)。我正在寻找一种我认为非常有效的解决方法 Visual Studio 中无用的“限制”。
我觉得很 令人惊讶的是 Visual Studio 链接器和编译器没有在 DLL 文件中正确执行此操作 创建时,自动扫描所有指定的静态库 对于所有导出的符号,按照 构建 导入库和导出文件以及StackOverflow 评论。我确认仅仅申请是不够的 __declspec(dllexport)
和 __declspec(dllimport)
属性 组成文件的类、函数和数据声明 静态库。
链接器不会扫描所有静态库 对于导出的符号,因此不会将它们拉入 DLL 文件(符号 必须由 DLL 链接命令行上的 .obj
文件引用,或者 通过其他方式我在下面显示)。没有明确引用每个 导出的符号,仍然可以创建 DLL 文件,但其关联的 未创建导入库文件。
据我所知,微软建议使用 LIB.EXE 创建 DEF 文件,但不幸的是, LIB.EXE 页面 应用约束:
请注意,如果您在预备步骤中创建导入库, 在创建
.dll
之前,您必须传递同一组目标文件 构建.dll
时,如构建导入时传递的那样 图书馆。
这是一个不幸的限制,因为我也在使用 CMake 我的新构建环境。 CMake 隐藏了实际的细节 传递给链接器(我认为这对于 99% 的情况来说是一件好事 时间),但在这种情况下,我需要访问其中的一些信息 CMake 执行时间,而不是事后使用手工编写的脚本或 其他脆弱的诈骗。
问题:
我该如何强制 DLL 链接器 解析组成的所有静态库中的所有导出符号 DLL 文件,不会导致脆弱性和额外的构建 逻辑维护工作?这里考虑全自动,并且 请记住,我需要多次执行此操作 不同的 DLL。
我的问题是:
如何获取使用的目标文件和静态库集 在最终的 DLL 链接命令行上单独使用 CMake 语法?
有没有更好的方法来解决这个问题,不需要 在调用之前调用单独的实用程序,例如 LIB.EXE 最后的链接?我担心超出的额外构建开销 LIB.EXE 链接自身以重新扫描所有静态库 再次即使它只是在单独的执行中写出它们。
非答案:
以下是我现在无法考虑的解决方案:
在除 在原始的
.h
或.cpp
文件中,这样做会 每次开发人员忘记更新列出的文件时都会中断 (很可能是名称损坏的)符号名称。它会打破 存在有关未解析符号的非用户友好链接器错误 这对于开发人员来说调试成本很高。这个不回答 包括以下方法:显式添加
.obj
文件到 DLL 链接命令行, (这种变体包括添加“假”.obj
文件,其中包含 对其他未引用但已导出的虚拟引用 符号(请注意,这是我旧的构建环境 今天做了,而且很臭)),并且,手工制作 DEF 文件包括未引用的但是 导出的符号,以及,
专门引用的链接器命令行选项 未引用但导出的符号符号。
完全停止使用静态库。我不能这样做 短期而言,因为与旧的结构相比,结构性变化太大 我正在远离的构建环境。我很可能会 将来走这条路,一旦所有旧建筑的残余 环境都在“垃圾箱”中,但这不是我的重点。
参考资料:
- 使用 DEF 文件从 DLL 导出
- 模块定义 (.def) 文件
- < a href="http://support.microsoft.com/kb/141459" rel="nofollow noreferrer">PRB:无法使用 _declspec(dllexport) 从静态库导出
- dumpbin.exe /symbols 选项
- lib.exe 选项
- 构建导入库和导出文件
- CMake LINK_FLAGS 目标属性
The Situation
I'm using Visual Studio 2008 SP1 (Professional Edition, both for 32-bit and 64-bit
builds). I'm seeking a workaround to what I believe is a very
unhelpful "limitation" in Visual Studio.
I find it quite
surprising that the Visual Studio linker and compiler does not do this right at DLL file
creation time, to automatically scan all specified static libraries
for all exported symbols in the same manner given in Building an
Import Library and Export File and in a StackOverflow
comment. I confirmed that it is not sufficient to simply apply__declspec(dllexport)
and __declspec(dllimport)
attributes to
class, function, and data declarations in the files that comprise the
static libraries.
The linker does not scan all of the static libraries
for exported symbols and so won't pull them into the DLL file (the symbols
have to be referenced by .obj
files on the DLL link command-line or
by other means I show below). Without explicit references to each
exported symbol, the DLL file may still be created, but its associated
import library file is not created.
From what I can gather, Microsoft is recommending using LIB.EXE
to create the DEF file, but unfortunately, the LIB.EXE page
applies a constraint:
Note that if you create your import library in a preliminary step,
before creating your.dll
, you must pass the same set of object files
when building the.dll
, as you passed when building the import
library.
That is an unfortunate constraint given that I am also using CMake in
my new build environment. CMake hides the details of what is actually
passed to the linker (and I deem that to be a good thing in 99% of the
time), but in this case I need to get access to some of that information at
CMake execution time, not afterwards using hand-crafted scripts or
other fragile skulduggery.
The Questions:
How do I go about forcing the DLL linker to
resolve all exported symbols in all static libraries that comprise the
DLL file, that isn't going to result in fragility and additional build
logic maintenance chores? Think in terms of full-automation here, and
keep in mind that I need to do this multiple times for multiple
different DLL's.
My questions are:
How do I obtain the set of object files and static libraries used
on the final DLL link command line using CMake syntax alone?Does the order of files listed on LIB.EXE line (the one used
to generate the DEF file) have to match exactly that order used on
the DLL link line?Are there other difficulties that I might encounter from the use
of the LIB.EXE approach to generate the DEF file?Is there a better way to go about this that does not require
calling a separate utility, such LIB.EXE, before calling the
final link? I'm concerned about the added build overhead beyond the
link itself for LIB.EXE to rescan all of the static libraries
again even though it just wrote them out in separate executions.
The Non-Answers:
The following are solutions that I cannot consider right now:
Manually specifying the unreferenced symbols anywhere other than
in the original.h
or.cpp
files, as doing that is going to
break each time a developer forgets to update the file that lists
the (quite possibly name-mangled) symbol name. And it will break
with non-user-friendly linker errors about unresolved symbols
which will be costly for developers to debug. This non-answer
includes the following approaches:Explicitly added
.obj
files to the DLL link command-line,
(variants of this include adding "fake".obj
files that have
dummy references to the otherwise unreferenced but exported
symbols (and note that this is what my old build environment
does today, and it stinks)), and,Handcrafting DEF files to include the unreferenced but
exported symbols, and,Linker command-line options that specifically reference the
unreferenced but exported symbols symbols.
Stop using static libraries altogether. I cannot do that in the
short-term, as it is too much of a structural change from the old
build environment I am moving away from. It is quite likely I will
take this route in the future, once all remnants of the old build
environment are in the "trash bin", but that is not my focus here.
Reference material:
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我的回答只是为了表明您不使用静态库的感觉是正确的。
DLRdave 已经在评论中说过,您的构建系统正在被 LIB 文件滥用。 LIB 文件很像一个真正的图书馆,你只带出你想要的东西,而不是图书馆中的所有东西。
如果说 Visual Studio 2008 工具集中存在缺陷,那就是它不支持部分链接。部分链接的输入是一组 OBJ 文件,输出是单个 OBJ 文件,其中包含输入 OBJ 文件中的所有代码和数据。
在这个问题的答案中描述了 g++ 的存档/库和部分链接之间的区别: g++ 部分链接而不是存档?,其中 GNU 链接器 (ld ) 确实支持部分链接。
至于可能的短期缓解措施 - 就我个人而言,我会尝试使用脚本在构建时动态构建 DEF 文件,方法是使用
LIB /List
或DUMPBIN /ARCHIVEMEMBERS
来获取obj 文件和LIB /DEF
从该列表生成 DEF 文件。或者,正如我假设正在使用_declspec(dllexport
) 一样,您也可以使用DUMPBIN /DIRECTIVES
,查找/EXPORT
并构建DEF 自己归档。I'm only answering to make it clear your feeling to not use static libraries is correct.
DLRdave said it already in the comments, your build system is being abused by LIB files. A LIB file is much like a real library, you only walk out with the things you ask for, not everything in the library.
If there's a gap in the Visual Studio 2008 tool set, it's that it doesn't support partial linking. The input into a partial link is a set of OBJ files and the output is a single OBJ file that contains all the code and data from the input OBJ files.
The difference between an archive/library and a partial link is described for g++ in the answer to this question: g++ partial linking instead of archives?, where the GNU linker (ld) does support partial linking.
As for possible short term mitigation - personally, I would have tried to use scripting to dynamically build the DEF file at build time by using
LIB /List
orDUMPBIN /ARCHIVEMEMBERS
to get the obj files andLIB /DEF
to generate the DEF file from that list. Or, as I assume_declspec(dllexport
) is being used, you could have also usedDUMPBIN /DIRECTIVES
, looked for/EXPORT
and built the DEF file yourself.