gcc中_GLIBCXX_VISIBILITY的含义和DSO

本文详细解释了GCC中的_GLIBCXX_VISIBILITY宏和DSO(动态共享对象)的概念。_GLIBCXX_VISIBILITY宏用于控制库中符号的可见性,允许开发人员隐藏或导出函数。隐藏符号有助于减少动态库的符号输出,提高程序模块性和启动效率。DSO是动态库的一种形式,其内部符号的可见性通过__attribute__((__visibility__(default)或__attribute__((__visibility__(hidden))来设定。文章还讨论了隐藏符号的引用问题及其在链接过程中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

gcc中_GLIBCXX_VISIBILITY的含义和DSO

背景

在查看gcc中包含的一些标准标头的来源(在/usr/include/c++/中),并在每个标头的顶部找到以下内容

namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION

	//...

_GLIBCXX_END_NAMESPACE_VERSION
} // namespace

究竟是什么_GLIBCXX_VISIBILITY(default)

源文件:gcc-9.1.0/libstdc+±v3/include/bits/c++config

// Macros for visibility attributes.
//   _GLIBCXX_HAVE_ATTRIBUTE_VISIBILITY
//   _GLIBCXX_VISIBILITY
#define _GLIBCXX_HAVE_ATTRIBUTE_VISIBILITY

#if _GLIBCXX_HAVE_ATTRIBUTE_VISIBILITY
# define _GLIBCXX_VISIBILITY(V) __attribute__ ((__visibility__ (#V)))
#else
// If this is not supplied by the OS-specific or CPU-specific
// headers included below, it will be defined to an empty default.
# define _GLIBCXX_VISIBILITY(V) _GLIBCXX_PSEUDO_VISIBILITY(V)
#endif

// Inline namespace for symbol versioning.
#if _GLIBCXX_INLINE_VERSION
# define _GLIBCXX_BEGIN_NAMESPACE_VERSION namespace __8 {
# define _GLIBCXX_END_NAMESPACE_VERSION }

因此,如果_GLIBCXX_HAVE_ATTRIBUTE_VISIBILITY为真,那么,它将扩展为:

__attribute__ (( __visibility__ ("default")))

如果_GLIBCXX_HAVE_ATTRIBUTE_VISIBILITY为假,则无效。

__visibility__属性用于定义DSO文件中符号的可见性。可以用来隐藏DSO之外的符号。

例如:

__attribute__ ((__visibility__("default"))) void foo();
__attribute__ ((__visibility__("hidden"))) void bar();

函数foo()可以在DSO外部使用,而bar()基本上是私有的,只能在DSO中使用。

您可以在此处详细了解__visibility__属性:https://gcc.gnu.org/wiki/Visibility

限制符号可见性的原因:从动态库中尽可能少地输出符号是一个好的实践经验。输出一个受限制的符号会提高程序的模块性,并隐藏实现的细节。动态库装载和识别的符号越少,程序启动和运行的速度就越快。导出所有符号会减慢程序速度,并耗用大量内存。

“default”:用它定义的符号将被导出,动态库中的函数默认是可见的。

”hidden”:用它定义的符号将不被导出,并且不能从其它对象进行使用,动态库中的函数是被隐藏的。default意味着该方法对其它模块是可见的。而hidden表示该方法符号不会被放到动态符号表里,所以其它模块(可执行文件或者动态库)不可以通过符号表访问该方法。

要定义GNU属性,需要包含__attribute__和用括号括住的内容。可以将符号的可见性指定为visibility(“hidden”),这将不允许它们在库中被导出,但是可以在源文件之间共享。实际上,隐藏的符号将不会出现在动态符号表中,但是还被留在符号表中用于静态链接。

导出列表由编译器在创建共享库的时候自动生成,也可以由开发人员手工编写。导出列表的原理是显式地告诉编译器可以通过外部文件从对象文件导出的符号是哪些。GNU用户将此类外部文件称作为”导出映射”

DSO 是什么

DSO动态共享对象,或者更不正式的shared library

什么是隐藏符号

隐藏符号是一个符号(即函数或数据对象的名称) 已使用隐藏链接编译,例如根据(GCC具体) 声明:

int x __attribute__ ((visibility ("hidden")));

如果它被隐藏了怎么能被引用

它不可能,这是你被警告的内容。例如。链接时间警告:

隐藏符号stat;在/usr/lib/libc_nonshared.a(stat.oS中由DSO引用
“hidden symbol `stat’ in /usr/lib/libc_nonshared.a(stat.oS) is referenced
by DSO”

告诉您链接中的DSO引用了符号stat,和 链接器可以在stat中找到/usr/lib/libc_nonshared.a的定义, 但(显然)该定义不在引用它的DSO中 并且不能从该DSO引用,因为它是隐藏的。

如果问题DSO未正确构建以供使用,则会出现此问题 作为DSO。见this example 并按照解决方案的后续行动。

继续进行OP的跟进

如果某些DSO已经引用了隐藏符号,那么为什么问题出在DSO上?

链接器说:

DSO X包含对符号S的引用。我可以找到符号S的定义是另一个链接模块Y, 但是该定义无法满足X 动态中的引用(即在运行时),因为SY模块中隐藏了链接}}

我可以确认问题来自非共享对象[…] [但]我没有在我的非共享对象中明确隐藏这些符号。

您可能没有明确标记隐藏在非共享对象中的任何符号。根据它的构建方式,符号 默认情况下可能会隐藏,除非明确标记。

假设非共享对象为Y,并且涉嫌隐藏的符号为libnonshared.a。运行:

foo

获取有关objdump -t libnonshared.a中符号的信息。在输出中,查找libnonshared.a的条目。它是否包含标记foo? - 例如

.hidden

此条目表示0000000000000000 g F .text 000000000000000b .hidden foo是一个全局符号(标记为foo - 这就是链接器能够看到它的原因)它隐藏动态链接。

如果情况确实如此,那么您需要修复g的版本,以便它不会隐藏libnonshared.a。 否则该符号无被引用

reference

[1]. https://blog.csdn.net/fengbingchun/article/details/78898623

[2]. https://www.thinbug.com/q/29270208

[3]. https://www.thinbug.com/q/23696585

<think>好的,我需要解决用户关于CXXFLAGS中-D__GLIBCXX__宏定义的问题,以及它与System VABI的关系。首先,用户提到了引用中的内容,特别是关于_GLIBCXX_USE_CXX11_ABI参数的选择,这可能与ABI兼容性有关。用户还提到了其他引用,比如链接错误交叉编译的问题,这些都可能与宏定义ABI相关。首先,我需要明确__GLIBCXX__宏的作用。根据GCC文档,这个宏是GCC自动定义的,用于指示libstdc++的版本。用户手动定义这个宏可能是不必要的,甚至可能导致问题,比如版本不一致。接下来,用户提到的-D_GLIBCXX_USE_CXX11_ABI=0在引用24中出现,这涉及到GCCC++11 ABI切换。这个宏控制是否使用新的C++11ABI,比如std::stringstd::list的实现。如果用户错误地定义了__GLIBCXX__,可能会影响这个ABI设置,导致链接错误,如引用2所述。然后,考虑System VABI的关系。System VABI规定了函数调用、寄存器使用等底层约定,而C++的ABI(如由GCC的libstdc++实现)则涉及名字修饰、类型布局等。当使用不同的_GLIBCXX_USE_CXX11_ABI值时,符号的命名类型可能变化,导致与旧库不兼容,这属于C++ ABI问题,但整体上遵循System VABI的其他部分。 用户可能在编译时手动定义__GLIBCXX__,试图覆盖默认版本,但这可能破坏libstdc++的版本检测,导致ABI不兼容。正确的做法是使用_GLIBCXX_USE_CXX11_ABI来控制ABI选择,而不是手动设置__GLIBCXX__。例如,引用4中使用了-D_GLIBCXX_USE_CXX11_ABI=0来兼容旧库。 此外,用户的问题可能涉及如何正确设置编译选项以避免链接错误,如引用2中的情况。需要解释这两个宏的区别,以及错误使用的影响。同时,交叉编译中的问题,如引用3中的编译器设置,可能需要确保ABI一致性,避免混合不同ABI编译的对象文件。 最后,生成相关问题需要考虑用户可能的后续疑问,比如如何检测ABI设置、解决链接错误、处理兼容性等。</think>### 一、`-D__GLIBCXX__`宏的作用与本质 1. **宏定义的本质** `__GLIBCXX__`是GCC编译器自动定义的宏,用于表示当前使用的`libstdc++`库版本号,其值为日期格式的整数(例如`20230118`表示2023年1月18日发布的版本)。**手动定义此宏通常没有必要且可能引发问题**,因为: - 该宏由编译器自动设置,反映实际使用的库版本[^1] - 强行覆盖可能导致版本检测失效,造成ABI不兼容 2. **与System V ABI的间接关系** System V ABI规范中并未直接涉及`__GLIBCXX__`,但该宏间接影响C++ ABI的实现细节: $$ \text{C++ ABI} \subseteq \text{System V ABI扩展} $$ - 不同`libstdc++`版本可能扩展System V ABI中的类型布局规则 - 符号修饰(Name Mangling)规则与`libstdc++`版本绑定[^2] ### 二、关键宏对比分析 | 宏名称 | 作用域 | 典型值 | 与System V ABI的关联 | |-------------------------|---------------|------------|-------------------------------| | `__GLIBCXX__` | 编译器自动定义 | 20230118 | 间接影响类型布局符号修饰规则 | | `_GLIBCXX_USE_CXX11_ABI`| 用户可定义 | 0/1 | 直接控制C++11 ABI的启用状态 | ### 三、典型问题场景与解决方案 #### 场景1:符号未定义错误 ```bash undefined reference to `std::__cxx11::basic_string<char...' ``` **原因分析**: 混合了使用不同`_GLIBCXX_USE_CXX11_ABI`值编译的对象文件[^2] **解决步骤**: 1. 统一编译单元的ABI模式: ```bash # 强制使用旧ABI CXXFLAGS += -D_GLIBCXX_USE_CXX11_ABI=0 ``` 2. 检查第三方库的ABI兼容性: ```bash nm libthird_party.a | grep cxx11 # 若存在cxx11符号则需重新编译 ``` #### 场景2:交叉编译兼容性问题 引用[^3]中出现的`compiler.libcxx=libstdc++11`配置需与`_GLIBCXX_USE_CXX11_ABI`值对应: $$ \begin{cases} \text{libstdc++11} \Rightarrow \_GLIBCXX\_USE\_CXX11\_ABI=1 \\ \text{libstdc++} \Rightarrow \_GLIBCXX\_USE\_CXX11\_ABI=0 \end{cases} $$ ### 四、验证ABI一致性的方法 1. **符号版本检查** ```bash objdump -t libfoo.so | grep cxx11 ``` 2. **运行时检测** 使用用户提供的检测代码[^1]: ```cpp #include <iostream> int main() { #ifdef _GLIBCXX_USE_CXX11_ABI std::cout << _GLIBCXX_USE_CXX11_ABI << std::endl; #else std::cout << "not defined" << std::endl; #endif } ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Erice_s

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值