文章目录

  • ABI 是什么
  • _GLIBCXX_USE_CXX11_ABI 是什么
  • 一段示例
  • 踩坑
  • -- 情形1
  • -- 情形2
  • 结论
  • -- 怎么发现的
  • -- 情形1
  • -- 情形2
  • -- 总结
  • 环境
  • 引用

很久没有写文章了记录点什么了,这次问题还算印象深刻

ABI 是什么

我们看看wiki定义: 应用二进制接口(英语:application binary interface,缩写为ABI)是指两程序模块间的接口,一个ABI定义了机器代码如何访问数据结构与运算程序,此处所定义的界面相当低端并且相依于硬件。而类似概念的API则在源代码定义这些,则较为高端,并不直接相依于硬件,通常会是人类可阅读的代码。一个ABI常见的样貌即是调用约定:资料怎么成为计算程序的输入或者从中得到输出;x86的调用约定即是一个ABI的例子

_GLIBCXX_USE_CXX11_ABI 是什么

一个宏定义.

In the GCC 5.1 release libstdc++ introduced a new library ABI that includes new implementations of std::string and std::list. These changes were necessary to conform to the 2011 C++ standard which forbids Copy-On-Write strings and requires lists to keep track of their size.

完整文章
https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dual_abi.html

大意是c++ 11 的新提案修改了 std::string 和 std::list 的 ABI接口,但是直到 GCC 5.1才发布.

截取 gcc-9.3.0/include/c++/9.3.0/x86_64-pc-linux-gnu/bits/c++config.h 中的一段代码

#if _GLIBCXX_USE_CXX11_ABI
namespace std
{inline namespace __cxx11 __attribute__((__abi_tag__ ("cxx11"))) { }
}
namespace __gnu_cxx
{inline namespace __cxx11 __attribute__((__abi_tag__ ("cxx11"))) { }
}
# define _GLIBCXX_NAMESPACE_CXX11 __cxx11::
# define _GLIBCXX_BEGIN_NAMESPACE_CXX11 namespace __cxx11 {# define _GLIBCXX_END_NAMESPACE_CXX11 }
# define _GLIBCXX_DEFAULT_ABI_TAG _GLIBCXX_ABI_TAG_CXX11
#else
# define _GLIBCXX_NAMESPACE_CXX11
# define _GLIBCXX_BEGIN_NAMESPACE_CXX11
# define _GLIBCXX_END_NAMESPACE_CXX11
# define _GLIBCXX_DEFAULT_ABI_TAG
#endif

string 和 list 简化一下实现大概应该是这个样子

#if _GLIBCXX_USE_CXX11_ABI
namespace std {inline namespace __cxx11 {class string {};class list {};};
};
#else
namespace std {class string {};class list {};
}
#endif

命名空间前加 inline 关键字是c++11 的新特性 “内联命名空间”,有兴趣的自己去了解一下,这个不是今天讨论的重点

使用 GCC 5.1+ 的编译器,默认 _GLIBCXX_USE_CXX11_ABI =1也就是默认使用新版ABI的string 和 list,如果要禁用c++11新版ABI, 只需要在Makefile中定义 -D _GLIBCXX_USE_CXX11_ABI=0就可继续使用旧版本ABI顺利编译通过

一段示例

典型的使用了老版ABI库的错误提示

tLogger.cpp:55: undefined reference to `log4cxx::WriterAppender::getEncoding[abi:cxx11]() const'
tLogger.cpp:58: undefined reference to `log4cxx::Level::toString[abi:cxx11]() const'  objs/bin/tLogger.o: In function `libtse::tLogger::createDailyRollingLogger(libtse::tLoggerAttribute const&)':
tLogger.cpp:117: undefined reference to `log4cxx::Logger::getLogger(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
tLogger.cpp:122: undefined reference to `log4cxx::WriterAppender::setEncoding(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
tLogger.cpp:123: undefined reference to `log4cxx::Level::toLevel(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
tLogger.cpp:138: undefined reference to `log4cxx::DailyRollingFileAppender::setDatePattern(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
tLogger.cpp:141: undefined reference to `log4cxx::PatternLayout::setConversionPattern(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
tLogger.cpp:154: undefined reference to `log4cxx::WriterAppender::setEncoding(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
tLogger.cpp:155: undefined reference to `log4cxx::Level::toLevel(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)' objs/bin/tLogger.o: In function `libtse::tLogger::setlevel(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)':
tLogger.cpp:180: undefined reference to `log4cxx::Level::toLevel(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)' objs/bin/tLogger.o: In function `libtse::tLogger::trace(char const*, ...)':
tLogger.cpp:192: undefined reference to `log4cxx::Logger::trace(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const' objs/bin/tLogger.o: In function `libtse::tLogger::debug(char const*, ...)':
tLogger.cpp:204: undefined reference to `log4cxx::Logger::debug(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const' objs/bin/tLogger.o: In function `libtse::tLogger::info(char const*, ...)':
tLogger.cpp:216: undefined reference to `log4cxx::Logger::info(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const' objs/bin/tLogger.o: In function `libtse::tLogger::warn(char const*, ...)':
tLogger.cpp:228: undefined reference to `log4cxx::Logger::warn(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const' objs/bin/tLogger.o: In function `libtse::tLogger::error(char const*, ...)':
tLogger.cpp:240: undefined reference to `log4cxx::Logger::error(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const' objs/bin/tLogger.o: In function `libtse::tLogger::fatal(char const*, ...)':
tLogger.cpp:252: undefined reference to `log4cxx::Logger::fatal(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const'
collect2: error: ld returned 1 exit status

踩坑

– 情形1

情形描述

  • 库LibA 指定采用旧版ABI编译( _GLIBCXX_USE_CXX11_ABI = 0)
  • 库LibB 采用默认编译选项 (_GLIBCXX_USE_CXX11_ABI = 1)
  • 库LibB 引用了 LibA
  • 程序C依赖LibA和LibB,采用默认编译选项 (_GLIBCXX_USE_CXX11_ABI = 1)

Boom! 程序会莫名奇妙的宕机

疑问:为什么LibA使用旧版本ABI,而程序使用新版ABI却可以编译通过?
解答:程序C中没有引用到LibA内有关string 和 list的接口

– 情形2

情形描述

  • 库LibA 采用默认编译选项 _GLIBCXX_USE_CXX11_ABI = 1
  • 库LibB 指定采用旧版ABI编译 (_GLIBCXX_USE_CXX11_ABI = 0)
  • 库LibB 引用了 LibA
  • 程序C依赖LibA和LibB,由于LIbB使用了旧版ABI,需要指定 _GLIBCXX_USE_CXX11_ABI = 0才行

Boom! 程序任然会莫名奇妙的宕机。

宕机的位置

AddressSanitizer:DEADLYSIGNAL
=================================================================
==3730==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000010 (pc 0x7f5edb0c60d5 bp 0x7fffc90c5240 sp 0x7fffc90c5228 T0)
==3730==The signal is caused by a READ memory access.
==3730==Hint: address points to the zero page.#0 0x7f5edb0c60d4 in __pthread_cond_signal (/lib64/libpthread.so.0+0xc0d4)AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (/lib64/libpthread.so.0+0xc0d4) in __pthread_cond_signal

结论

– 怎么发现的

  1. 最近升级了gcc9高版本,修改了Makefile
  2. 这问题断断续续我查了好几天时间,主要是花在啃代码上调试上,觉得代码是不是有什么隐藏bug,完全没有想到是ABI定义不一致问题造成的
  3. 由于我还有一台云主机,上面也有工程代码,在这上面编译运行却一切正常

接下来验证一下自己的猜想

– 情形1

  • 库LibA 指定采用旧版ABI编译 (_GLIBCXX_USE_CXX11_ABI = 0)
  • 库LIbB 指定采用旧版ABI编译 (_GLIBCXX_USE_CXX11_ABI = 0)
  • 程序C 指定采用旧版ABI编译 (_GLIBCXX_USE_CXX11_ABI = 0)

运行正常

– 情形2

  • 库LibA 指定采用新版ABI编译 (_GLIBCXX_USE_CXX11_ABI = 1)
  • 库LIbB 指定采用新版ABI编译 (_GLIBCXX_USE_CXX11_ABI = 1)
  • 程序C 指定采用新版ABI编译 (_GLIBCXX_USE_CXX11_ABI = 1)

运行正常

– 总结

  • 首先一个观点,在实际项目中使用老版ABI的库是再正常不过的事情,如果升级了gcc 5.1+ 并且引用了库中string或者list相关接口,势必需要指定 (_GLIBCXX_USE_CXX11_ABI = 0)
  • 将引用到的老版本ABI库使用新gcc重新编译,如果没有源码或者编译三方库非常麻烦,确保引用的库中间不要混合使用新版ABI和旧版ABI

环境

系统环境

CentOS Linux release 7.7.1908 (Core)

编译器环境

COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/local/gcc-9.3.0/libexec/gcc/x86_64-pc-linux-gnu/9.3.0/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: ../gcc-9.3.0/configure --prefix=/usr/local/gcc-9.3.0/ --enable-checking=release --enable-languages=c,c++ --disable-multilib
Thread model: posix
gcc version 9.3.0 (GCC)

引用

  1. https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dual_abi.html

_GLIBCXX_USE_CXX11_ABI 定义不一致带来的宕机问题相关推荐

  1. 服务器宕机是什么原因

    随着如今互联网,计算行业的快速发展,数据和信息安全的重要性也越发重要,选择一款稳定的服务器固然重要. 但再好的服务器也难免在使用过程中出现这样或那样的问题,其中服务器宕机就是最为常见的.那么,通常造成 ...

  2. AOF日志:宕机了,Redis如何避免数据丢失?

    你会把 Redis 用在什么业务场景下? 我想你大概率会说: "我会把它当作缓存使用,因为它把后端数据库中的数据存储在内存中,然后直接从内存中读取数据,响应速度会非常快. "没错, ...

  3. Redis的KEYS命令引起宕机事件

    摘要: 使用 Redis 的开发者必看,吸取教训啊! 原文:Redis 的 KEYS 命令引起 RDS 数据库雪崩,RDS 发生两次宕机,造成几百万的资金损失 作者:陈浩翔 Fundebug经授权转载 ...

  4. 2018年十大云宕机事故盘点:主流无一幸免!

    根据IDC今年7月份发布的<中国公有云服务市场半年度跟踪报告>显示,阿里云的市场占有率已过45%,腾讯云达到10%.在全球市场,根据Gartner最新数据显示,亚马逊AWS占全球份额的51 ...

  5. 如何降低数据中心宕机事件的影响

    大多数人在生活或工作领域中都不希望出现连接中断的情况,尤其是在以数字生活方式为主的今天,所以数据中心基础设施变得越来越重要.对于许多消费者来说,他们希望自己的数字产品和服务能保持正常工作,所以当发生宕 ...

  6. 由中行IBM大型机宕机谈银行系统运维

    12月15日中行IBM大型机宕机,系统没有第一时间切换到热备或者异地容灾上,直接影响中行的信用卡支付相关业务,直到4小时之后才恢复服务.由于银行业务的特殊性,对于系统的可用性要求极高,就此事件,我们采 ...

  7. 第七十六期:3000台服务器不宕机,微博广告系统全景运维大法

    微博现在日活达到了 2 亿,微博广告是微博最重要且稳定的收入来源,没有之一,所以微博广告系统的稳定性是我们广告运维所有工作中的重中之重. 作者:孙燕来源 微博现在日活达到了 2 亿,微博广告是微博最重 ...

  8. 谷歌、Facebook 大规模宕机!“裸奔时代”程序员该怎么办?

    运行不稳定,宕机两行泪. 作者 | 阿木 责编 | 郭芮 3月13日,大量用户反馈Google云服务器出现异常,此次异常波及大量知名产品,如Gmail.YouTube.Google Drive等,普通 ...

  9. Redis的KEYS命令引起RDS数据库雪崩,RDS发生两次宕机,造成几百万的资金损失

    文章目录 第一次宕机 事故影响 原因分析 改进方案 第二次宕机 原因分析 改进方案 总结 Redis开发建议 1.冷热数据分离,不要将所有数据全部都放到Redis中 2.不同的业务数据要分开存储 3. ...

  10. Kubernetes实战指南:零宕机无缝迁移Spring Cloud至k8s

    1. 项目迁移背景 1.1 为什么要在"太岁"上动土? 目前公司的测试环境.UAT环境.生产环境均已经使用k8s进行维护管理,大部分项目均已完成容器化,并且已经在线上平稳运行许久. ...

最新文章

  1. 太棒啦!PyCharm与Jupyter完美融合,Jupytext来啦!
  2. MFC动态创建控件并响应事件代码实现过程
  3. mysql为什么要编译安装_Mysql编译安装
  4. node.js 函数定义和调用
  5. WinSock重叠I/O模型
  6. IDEA启动Tomcat AJP连接器配置secretRequired=“true“,但是属性secret确实空或者空字符串,这样的组合是无效的解决办法
  7. 真正的mybatis_redis二级缓存
  8. 三类MySQL_Mysql中的三类锁,你知道吗?
  9. Codeforces Beta Round #71 C【KMP+DP】
  10. 我的MYSQL学习心得(十三) 权限管理
  11. JavaScript 02
  12. 华为星环大数据_华为和星环大数据平台关键能力对比(附报告)
  13. java线程的生命周期(图解)
  14. 一步精准获取京东商品评论数据
  15. 第5章第16节:案例:制作一份漂亮的翻页动画 [PowerPoint精美幻灯片实战教程]
  16. 移动端 touch 手机拖动 css停止问题
  17. Vue.js+ECharts:切换图表类型(图表工具栏)
  18. 硬件行业知识体系概要
  19. 【Linux脚本-sed命令在文本首行和尾行插入空行】
  20. java中JAO,Java

热门文章

  1. 干货 | 云智慧透视宝Java代码性能监控实现原理
  2. 快捷方式查看系统的配置信息 使用dxdiag
  3. 按头安利 好看又实用的SolidEdge 3d模型素材看这里
  4. 买卖二手3C成了“拆盲盒”,究竟是谁之责?
  5. centos漏洞系列(三):Google Android libnl权限提升漏洞
  6. 计算机研究生个人简历,美国计算机研究生申请个人简历这样填比模板更出众!...
  7. 数学三次危机(二)毕达哥拉斯学派的数学思想
  8. 热议:CSS为什么这么难学?一定是你的方法不对
  9. Fastjson小于1.2.67 UnSerializable RCE分析研究
  10. 全球-专线香港-大陆快速包税清关