与生成的protobufs的静态链接会导致中止
Static linking with generated protobufs causes abort
我有一个项目,它将c++生成的protobuf序列化程序编译到一个静态库中。可执行文件链接到此库,.so(.dll(也链接。可执行文件稍后加载.so文件。当这种情况发生时,我得到:
[libprotobuf ERROR /mf-toolchain/src/protobuf-3.0.0-beta-1/src/google/protobuf/descriptor_database.cc:57] File already exists in database: mri.proto
[libprotobuf FATAL /mf-toolchain/src/protobuf-3.0.0-beta-1/src/google/protobuf/descriptor.cc:1128] CHECK failed: generated_database_->Add(encoded_file_descriptor, size):
terminate called after throwing an instance of 'google::protobuf::FatalException'
what(): CHECK failed: generated_database_->Add(encoded_file_descriptor, size):
Aborted (core dumped)
为了清楚起见,我有一个静态库A,它链接到程序p和共享库S。后来,p加载了S,我得到了上面的错误。
我在Stack Overflow和google上看到了类似的错误,但我很确定我只是针对库进行链接,而不是重新编译源代码。据我所知,这应该意味着汇编的数据是相同的。
另请注意:这个问题只发生在Linux上。这在Windows和OS X上运行良好。
问题是静态库包含一个文件mri.pb.cc
,该文件在其全局初始化程序中,将类型描述符注册到libprotobuf维护的全局描述符数据库中。因为静态库被加载到程序中两次,所以这个初始化器运行了两次,但因为进程中只有一个libprotobuf副本,所以两个初始化器都注册到同一个全局数据库中,并且它检测到冲突。
要解决这个问题,您需要将静态库更改为共享库,主程序和动态加载的库都依赖于此。
我不知道你为什么在Windows或OSX上看到不同的行为。我的最佳猜测是,在这些平台上,您实际上将libprotobuf的两个独立副本链接到程序中——一个在主可执行文件中,另一个在动态加载库中。因此,描述符数据库有两个副本,并且没有冲突。然而,你可能会在这里看到更微妙的问题。如果在主程序和动态加载的模块之间传输protobuf对象指针(不进行序列化,然后再次解析(,那么最终可能会有一个由库的一个副本创建但与另一个副本(因此是描述符数据库的不同副本(一起使用的protobuf目标,这将混淆库并导致奇怪的事情发生。
或者,如果从未通过边界传递protobuf对象,则可以通过静态链接libprotobuf在Linux上"修复"问题,以便获得如上所述的两个副本。但这是相当危险的;我不推荐它。
这个错误出现在链接到两个库(LibA
,LibB
(的可执行文件的上下文中,这两个库恰好编译了同一个proto消息,其中LibB
依赖于LibA
并链接到它。
我发现自己遇到了这种情况,因为LibA
以前不依赖于任何protobuffer框架,LibB
为这个内部工具应用程序构建了一整套相关的proto消息,以便与另一个应用程序通信。随着LibA
的新发布,它需要对另外两个编译各种原型消息的库(LibC
、LibD
(进行新的依赖。这个问题从LibC
和LibD
都同样表现出来,我将讨论LibC
,因为解决方案是相同的。
在加载时,应用程序加载了LibC
,最终加载了最上面的模块LibB
,这时common.cc
中的LogMessage::Finish()
将触发中止。我通过在中止上下文的几个级别上设置一个断点来发现是谁在进行这种双重加载。以下是我称之为SomeMessage
…的双加载原型消息的相关来源
void LibC_AddDesc_SomeMessage_2eproto() {
static bool already_here = false; // <--- Breakpoint Set Here
if (already_here) return;
already_here = true;
突破点命中1:加载伦敦银行同业拆借利率
LibC.dll!MyNamespace::LibC_AddDesc_SomeMessage_2eproto() Line 415 C++
LibC.dll!MyNamespace::LibC_AddDesc_ParentMessage_2eproto() Line 388 C++
LibC.dll!MyNamespace::StaticDescriptorInitializer_ParentMessage_2eproto::StaticDescriptorInitializer_ParentMessage_2eproto() Line 494 C++
LibC.dll!MyNamespace::`dynamic initializer for 'static_descriptor_initializer_ParentMessage_2eproto_''() Line 495 + 0x21 bytes C++
msvcr100d.dll!_initterm() + 0x2c bytes
LibC.dll!_CRT_INIT(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 289 C
LibC.dll!__DllMainCRTStartup(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 506 + 0x13 bytes C
LibC.dll!_DllMainCRTStartup(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 477 C
ntdll.dll!LdrpRunInitializeRoutines() + 0x1e8 bytes
ntdll.dll!LdrpInitializeProcess() - 0x14c9 bytes
ntdll.dll!string "Enabling heap debug optionsn"() + 0x29a99 bytes
ntdll.dll!LdrInitializeThunk() + 0xe bytes
在加载LibC的过程中,我可以看到断点被击中两次,静态变量already_here
从false设置为true,并保持为true,将跳过此消息的注册。
突破点命中2:加载LibB
当这个库尝试加载时,变量already_here
将被重新初始化为false,我们将继续尝试第二次注册这个消息,这触发了中止。
LibB.dll!MyNamespace::LibC_AddDesc_SomeMessage_2eproto() Line 415 C++
LibB.dll!MyNamespace::LibC_AddDesc_ParentMessage_2eproto() Line 388 C++
LibB.dll!MyNamespace::LibC_AddDesc_FullMessage_2eproto() Line 219 C++
LibB.dll!MyNamespace::StaticDescriptorInitializer_FullMessage_2eproto::StaticDescriptorInitializer_FullMessage_2eproto() Line 358 C++
LibB.dll!MyNamespace::`dynamic initializer for 'static_descriptor_initializer_FullMessage_2eproto_''() Line 359 + 0x21 bytes C++
msvcr100d.dll!_initterm() + 0x2c bytes
LibB.dll!_CRT_INIT(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 289 C
LibB.dll!__DllMainCRTStartup(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 506 + 0x13 bytes C
LibB.dll!_DllMainCRTStartup(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 477 C
ntdll.dll!LdrpRunInitializeRoutines() + 0x1e8 bytes
ntdll.dll!LdrpInitializeProcess() - 0x14c9 bytes
ntdll.dll!string "Enabling heap debug optionsn"() + 0x29a99 bytes
ntdll.dll!LdrInitializeThunk() + 0xe bytes
我们会在中止线的stubs/common.cc中结束
void LogMessage::Finish() {
bool suppress = false;
if (level_ != LOGLEVEL_FATAL) {
InitLogSilencerCountOnce();
MutexLock lock(log_silencer_count_mutex_);
suppress = internal::log_silencer_count_ > 0;
}
if (!suppress) {
internal::log_handler_(level_, filename_, line_, message_);
}
if (level_ == LOGLEVEL_FATAL) {
abort(); // <----- runtime crash!
}
}
对于std::呃,你会发现以下文本。。。
libprotobuf ERROR descriptor_database.cc:57] File already exists in database: SomeMessage.proto
libprotobuf FATAL descriptor.cc:860] CHECK failed: generated_database_->Add(encoded_file_descriptor, size):
解决方案很简单,我打开了LibC
项目,搜索pb
,并从LibB
中删除了这些原始消息。CCD_ 24也是如此。
我也遇到了这个问题,刚刚解决了它。我忘了链接pthread。链接pthread后,问题消失了。在这里发帖以防其他人错过。
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- #为""定义宏;静态";针对不同的上下文
- cmake如何在fedora工作站中找到boost静态库包
- 静态数据成员的问题-修复链接错误会导致编译器错误
- 将公共但非静态的成员函数与ALGLIB集成
- cmake在我的项目中所需的所有静态库都不成功
- C++从另一个类访问公共静态向量的正确方法是什么
- 基于boost的程序的静态链接——zlib问题
- 在静态库中嵌入类方法
- Qt C++静态thread_local QNetworkAccessManager是线程应用程序的好选择吗
- 如何在C++中获得"静态纯虚拟"功能?
- 私有类型的静态常量成员
- 使用gcc从静态链接的文件中查找可选符号
- 在 .h 文件中的类中声明静态变量和在.cpp文件中声明"global"变量有什么区别
- 如何在C++中使用非静态成员函数作为回调函数
- 将静态库链接到不带-fPIC的共享库中
- 静态结构和一个定义规则
- 为什么虚函数不能是静态的和全局的?
- C++17中函数模板中的静态数组初始化(MSVC 2019)
- 与生成的protobufs的静态链接会导致中止