符号可见性和名称空间 我正在Linux和GCC上进行C++符号可见性的实验。似乎首选的方法是使用-fvisibility=hidden,并根据可见性gcc wiki页面()逐个导出使用过的符号。 我的问题是许多库不能很好地处理这个问题,它们忘记显式地导出符号,这是一个严重的问题。在修复了几个bug之后,甚至boost的某些部分仍然可能受到影响。当然,这些错误应该被修复,但在此之前,我想用一种“安全”的方式来隐藏尽可能多的符号

符号可见性和名称空间 我正在Linux和GCC上进行C++符号可见性的实验。似乎首选的方法是使用-fvisibility=hidden,并根据可见性gcc wiki页面()逐个导出使用过的符号。 我的问题是许多库不能很好地处理这个问题,它们忘记显式地导出符号,这是一个严重的问题。在修复了几个bug之后,甚至boost的某些部分仍然可能受到影响。当然,这些错误应该被修复,但在此之前,我想用一种“安全”的方式来隐藏尽可能多的符号,c++,gcc,visibility,symbols,C++,Gcc,Visibility,Symbols,我想出了一个解决方案:我将所有符号放在一个名称空间中,并在该名称空间上使用symbol hide属性,然后导出公共接口,这样只有我的符号会受到影响 问题是,当我针对库为我没有导出的每个类编译一些东西时,我收到了一条警告消息,我在应用程序的“类”字段中使用了这些类 namespace MyDSO __attribute__ ((visibility ("hidden"))) { struct Foo { void bar() __attribute__ ((visibility ("d

我想出了一个解决方案:我将所有符号放在一个名称空间中,并在该名称空间上使用symbol hide属性,然后导出公共接口,这样只有我的符号会受到影响

问题是,当我针对库为我没有导出的每个类编译一些东西时,我收到了一条警告消息,我在应用程序的“类”字段中使用了这些类

namespace MyDSO __attribute__ ((visibility ("hidden"))) {
  struct Foo {
    void bar() __attribute__ ((visibility ("default"))) {}
  };
}

struct Bar {
  MyDSO::Foo foo;
};

int main() {}
在这个小示例中可以复制警告消息,但是名称空间当然应该位于应用程序中的另一个类的库中

$ gcc-4.7.1 namespace.cpp -o namespace
namespace.cpp:7:8: warning: ‘Bar’ declared with greater visibility than the type of its field ‘Bar::foo’ [-Wattributes]
据我所知,隐藏名称空间应该具有与使用-fvisibility=hidden非常相似的效果,但使用后者我从未收到类似的警告。我看到,当我将-fvisibility=hidden传递给应用程序时,应用程序中的类也将被隐藏,因此我不会得到警告。但是当我不传递该选项时,编译器不会认为头中的任何符号是隐藏的,因此我不会再收到警告


这条警告信息的目的是什么?这是一个严重的问题吗?在哪些情况下会导致任何问题?隐藏名称空间与fvisibility=hidden有何不同?

在回答您的具体问题之前,我应该向阅读本文的其他人提到,为每个名称空间应用符号可见性属性是GCC特有的功能。MSVC只支持类、函数和变量上的dllexport,如果您希望代码可移植,则必须在那里匹配MSVC。正如我最初的GCC符号可见性指南(您在GCC网站上链接到的指南)所指出的,MSVC基于宏的dllexport机制可以很容易地重用,以在GCC上实现类似的功能,因此移植到MSVC将使您“免费”处理符号可见性

关于您的具体问题,GCC警告您是正确的。如果外部用户试图使用公共类型的Bar,他们几乎肯定需要使用Bar中的所有内容,包括Bar::foo。出于完全相同的原因,所有私有成员函数(尽管是私有的)都需要可见。很多人对此感到惊讶,他们认为私有成员函数符号在定义上是任何人都无法访问的,但他们忘记了,仅仅因为程序员没有访问权限并不意味着编译器不需要访问权限。换句话说,私有成员函数对您是私有的,而不是编译器。如果它们出现在头文件中,通常意味着编译器需要访问,即使在匿名名称空间中也是如此(对于程序员来说是匿名的,而对于倾向于使用内容哈希作为“真实”名称空间的编译器来说则不是)

隐藏名称空间对-fvisibility=hidden有非常不同的影响。这是因为GCC在特定类型(例如vtables、type_info等)的符号之上和之外会输出许多符号。-fvisibility=hidden隐藏任何编译器指示的方式都无法隐藏的内容,将两个二进制文件加载到具有冲突符号的同一进程中是绝对必要的,例如,使用不同版本的Boost构建的两个共享对象


我很欣赏您试图解决由ELF中的符号符号可见性造成的问题以及对损坏的C++二进制文件和大量丢失的程序员生产力的影响。然而,你不能修复它们——它们是ELF本身的错误,它是针对C而不是C++设计的。如果这是一个安慰,我在几个月前写了一篇内部的黑莓白皮书,因为ELB符号可见性问题对于BB10来说是一个问题,因为对于任何一个具有显著C++代码库的大公司来说,这是个问题。所以,也许你会看到C++ 17的一些解决方案,尤其是如果Doug Gregor的C++模块实现有了很大的进步。

< p>你的可见性属性的使用似乎对我来说是倒退的;我认为使用-fvisibility=hidden并将可见性“default”添加到库声明名称空间会获得更好的结果,因为库的接口可能具有默认可见性,或者您无法从应用程序中使用它。如果您不想修改库标题,可以在#includes周围使用#pragma GCC visibility push/pop


此外,正如Niall所说,将单个成员函数标记为默认值是不起作用的,如果它是库接口的一部分,则整个Foo类型需要具有默认可见性。

感谢您的详细回答。出于好奇,我对编译器需要哪些无法生成的符号感兴趣?据我所知,在普通类中甚至可以生成typeinfo。当使用vis=hidden时,即使隐藏了不应该隐藏的符号,也不会得到警告,只会从链接器中得到未定义的符号错误。使用隐藏名称空间gcc可以检测问题。也许只导出类中的一些符号是合法的,但gcc还是会发出警告。Doug Gregor的C++模块非常有趣,我喜欢他的演示,谢谢分享。总之,任何类型的虚拟信息总是会发出,而没有类型的Type信息只在Type ID(或)使用它时使用。此外,大多数编译器为每个程序指定的构造函数发出两个或多个构造函数实现,析构函数生成也有一些神奇之处。简而言之,-fvisibility=hidden隐藏了很多东西,而您的方法只会隐藏程序员指定的东西,而不会隐藏神奇的内部。M