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
动态中的引用(即在运行时),因为S
在Y
模块中隐藏了链接}}
我可以确认问题来自非共享对象[…] [但]我没有在我的非共享对象中明确隐藏这些符号。
您可能没有明确标记隐藏在非共享对象中的任何符号。根据它的构建方式,符号 默认情况下可能会隐藏,除非明确标记。
假设非共享对象为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