将静态本机库链接到托管 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++ 代码使用的功能微不足道,不应引入任何额外的依赖项。

问题:

    链接失败,LNK2001LNK2019 提到代码绝对不依赖的符号 即使我添加了所需的依赖项,切换工具集(例如从 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++ 项目会在的主要内容,如果未能解决你的问题,请参考以下文章

托管 c++ 库,它是如何工作的?

用于 C++ 的 C# 包装器,但仅编译为静态库

带有静态库的 Android Studio 中的本机 C++ 代码

如何将 C++ 本机对象编组到托管 C++ CLI

如何减少用本机 Visual C++ 编写的大型项目的链接时间?

(C++/CLI) 如何在 C++ CLI 中获取从本机代码到托管代码的回调?