将静态本机库链接到托管 C++ 项目会在
Posted
技术标签:
【中文标题】将静态本机库链接到托管 C++ 项目会在【英文标题】:Linking static native library to managed C++ project pulls unused (and unexpected) dependencies in 【发布时间】:2020-08-06 17:57:58 【问题描述】:简介:
托管 (/clr
) C++ 项目 (.dll
) 静态链接本机 C++ 库(使用 /MD
编译)。静态库很大,并且引用了许多其他库,但托管 C++ 代码使用的功能微不足道,不应引入任何额外的依赖项。
问题:
-
链接失败,
LNK2001
和 LNK2019
提到代码绝对不依赖的符号
即使我添加了所需的依赖项,切换工具集(例如从 VS2017
迁移到 VS2019
)也会导致错误再次出现(这次提到其他依赖项)
会发生什么:
显然,/clr
开关导致编译器以不同的方式处理内联函数——它们不再嵌入到.obj
文件中(作为“弱符号”),而是在导入表中被引用。这意味着链接器必须找到以下内容:
"public: virtual char const * __cdecl std::exception::what(void)const " (?what@exception@std@@UEBAPEBDXZ)
... 它所做的并且(因为 CRT 库是 DEFAULTLIB
并因此最后使用)通常它会在其他库中找到它(即在上述静态本机库中)。因此,它在包含std::exception::what()
的静态库中找到第一个.obj
文件并将其拉入——这意味着我们现在依赖于.obj
所依赖的所有内容。这解释了问题 #1(虚假链接器错误)。
现在,如果您使用另一个工具集编译您的静态库 -- obj
文件可能会以不同的顺序存储,从而导致问题 #2。
要重现该问题,您可以使用此代码(确保托管项目链接静态库):
//--------------------
// statlib.cpp
//
#include <exception>
void this_is_a_trap() throw std::exception();
extern int bar();
int foo() return bar();
//--------------------
// clrdll.cpp (managed code)
//
#include <exception>
__declspec(dllexport) void oops()
throw std::exception();
如果你链接 /VERBOSE
标志,你会看到类似:
1> Searching C:\Program Files (x86)\Windows Kits\10\lib\10.0.17763.0\um\x64\oleaut32.lib:
1> Searching C:\Program Files (x86)\Windows Kits\10\lib\10.0.17763.0\um\x64\uuid.lib:
1> Searching C:\Program Files (x86)\Windows Kits\10\lib\10.0.17763.0\um\x64\odbc32.lib:
1> Searching C:\Program Files (x86)\Windows Kits\10\lib\10.0.17763.0\um\x64\odbccp32.lib:
1> Searching C:\tst\x64\Release\statlib.lib:
1> Found "public: virtual char const * __cdecl std::exception::what(void)const " (?what@exception@std@@UEBAPEBDXZ)
1> Referenced in clrdll.obj
1> Loaded statlib.lib(statlib.obj) <-- Now we depend on `bar()`
问题
解决这个问题的最佳方法是什么?
注意事项:
将msvcrt.lib
添加到链接器输入(在其他静态库之前)会有所帮助,但并非总是如此——某些符号(例如std::bad_weak_ptr::what()
在msvcrt.lib
中不存在)
这个问题是this SO post的根本原因
【问题讨论】:
【参考方案1】:在混合(/clr
和本机)代码中,std::exception::what()
(和其他类似符号)确实内联,但这些定义是托管(非本机)。这通常不是问题,但本机代码通过 std::exception
的 vtable 引用 native 定义。通常,此类引用(一旦解析失败)会被重定向到托管定义(如上所述,生成的定义),但在这种情况下——在“重定向”之前,在另一个对象(来自本机静态库的随机对象)中找到了本机定义" 启动,导致该对象被引用。
查看详情here。 MS 正在想办法解决这个问题。
【讨论】:
以上是关于将静态本机库链接到托管 C++ 项目会在的主要内容,如果未能解决你的问题,请参考以下文章
带有静态库的 Android Studio 中的本机 C++ 代码