so符号可见性_控制共享库的符号可见性:第2部分-IBMXLCC++V13编译器的...

so符号可见性_控制共享库的符号可见性:第2部分-IBMXLCC++V13编译器的...

2023年7月29日发(作者:)

so符号可见性_控制共享库的符号可见性:第2部分-IBMXLCC++V13编译器的⾼级知识...IBM XL C / C ++ V13编译器:可见性属性的⽀持在 ,我们了解了处理符号可见性的不同⽅法。 最重要的包括:GNU可见性属性扩展,IBMAIX®导出⽂件和GNU版本脚本。 GNU可见性可以使程序员从源代码级别控制符号的可见性,⽽⽤于链接的脚本对于从构建和发布步骤的⾓度来控制符号可见性可能更为有⽤。但是,随着⽤于AIX和Linux®V13编译器的XL C / C ++的发布,XL C / C ++编译器接受了GNU可见性扩展。 此外,诸如new pragma指令和new global编译器选项之类的实体也可以⽤于此⽬的。 在这⼀部分中,我们将重点更多放在可见性属性设置及其最终解决⽅案上。此外,我们可能会详细介绍AIX和Linux可见性设计之间的差异以及所实现的AIX可见性的兼容性。可见性属性说明符与GNU可见性属性扩展类似,对于XL C / C ++编译器,程序员可以通过属性说明符使⽤GNU属性语法来设置符号可见性。 清单1显⽰了函数和变量声明的⽰例。清单1.变量和函数的可见性说明符int __attribute__((visibility("hidden"))) m; // m is set as "hidden"void __attribute__((visibility("protected"))) fun(); // fun() is set as "protected"可见性属性的类型可以是default , hidden , protected或internal 。 这与我们在上⼀篇⽂章中介绍的GNU语法⼀致。 但是,在缺省值中,AIX和Linux实现之间存在细微的差异。 本⽂稍后将对此进⾏讨论。乍⼀看,可见性属性看起来很简单。 但是,在编程实践中,程序员可能会有更多问题。 例如,⼀个普遍提出的问题是关于具有不同可见性的同⼀符号的多重定义。 这对于处理不同的标头定义可能并不罕见,并且可能给程序员带来很多⿇烦。 从编译器的⾓度来看,此问题是由不完善的现实引起的,解决⽅案将更多地依赖于程序员⽅⾯。 但是,编译器确实提供了规则和便利来提供帮助。 XL C / C ++编译器将采⽤⾸次遇到的可见性属性。 并且,在处理其余部分(具有不同的可见性)时,会向程序员发出警告。 最后,编译器将忽略后者定义的可见性。 清单2提供了⼀个⽰例。清单2.为同⼀符号指定的不同可见性属性extern int __attribute__((visibility("hidden"))) m; // "m" is "hidden".int __attribute__((visibility("protected"))) m; // Compiler warning - "protected" is ignored.在此⽰例中, m⾸先定义为hidden 。 这意味着在动态链接期间,变量的符号将不可见。 但是,后⾯的代码将再次对其进⾏设置,但是在protected , 全局可见 。 编译器将发出警告,然后在处理代码时忽略protected属性。有关可见性的其他⼀些问题将涉及类型 。 在C ++编码中,程序员可能能够⾃⼰创建类型 。 程序员甚⾄可能想知道是否可以为此类类型指定可见性属性。 这是⼀个有趣的问题,因为在C编程中此类问题永远不会发⽣。 所有内置类型都不会⼲扰可见性属性。 但是,在C ++编程中,这不再是正确的。 在下⾯的清单中对此进⾏了演⽰。清单3.为类指定的可见性属性class __attribute__((visibility("hidden"))) T // class T has "hidden" visibility{public: static int gVal; // T::gVal is "hidden" void Dosth(); // T::Dosth() is "hidden"};

T t1; // t1 is "hidden".T __attribute__((visibility("internal"))) t2; // t2 has "internal" visibility.在清单3中,我们定义了具有hidden可见性的类T 在C ++代码中,程序员可以为类以及函数和对象定义可见性属性。 语法相同,但效果会有所不同。 在这⾥,我们可以假设类型T是hidden 。 后来,代码定义了两个T类型的对象: t1和t2 。 您可能会发现定义的t1没有任何可见性属性说明符。 因此, t1的可见性设置是从其类型直观地获得的。 并且,对于t2 ,代码将其定义为internal可见性。 这样的定义将在其类型T的可见性属性之前。 因此, t2的最终可见性是internal 。 这是基本规则。 但是,与对象相关联的实体(如成员函数和静态成员)的可见性由于不同的对象可见性定义⽽不会更改。 这意味着,在清单3中, T::gVal和T::Dosth()将具有T类定义的hidden可见性。 此外,这还意味着相同类型的对象( t1和t2 )仍然共享相同的功能表,⽆论为它们指定了什么可见性属性。 并且,如果程序员打算改变此类成员的可见性,例如以成员函数为例,则程序员需要直接为成员函数指定可见性属性。 清单4显⽰了如何将T::Dosth()的可见性属性更改为protected 。清单4.更改类成员的可见性属性class __attribute__((visibility("hidden"))) T // class T has "hidden" visibility{public: static int gVal; // T::gVal is "hidden" void __attribute__((visibility("protected"))) Dosth(); // T::Dosth() is now "protected"};

T t1; // t1 is "hidden".T __attribute__((visibility("internal"))) t2; // t2 has "internal" visibility.可见性属性也可以应⽤于联合,枚举,名称空间和模板。 名称空间上的可见性将类似于作⽤域或上下⽂中的类。 为命名空间指定了可见性属性时,该属性仅应⽤于作⽤域中包含的任何符号。 但是,与此同时,功能仅限于封闭的范围。 清单5显⽰了⼀个⽰例。清单5.命名空间的Visibility属性namespace std __attribute__((visibility("hidden"))) { void foo1() {}; // foo1() has "hidden" visibility.}namespace std { void foo2() {} // The visibility of foo2() is "default"/"unspecified".}在此⽰例中, foo1()被hidden 。 但是, foo2()不会是hidden符号。 这意味着,如果您在⽂件中定义了名称空间std的可见性,那么编写具有相同名称空间std另⼀个⽂件的程序员将不会受到影响。 结果,这样的设计可以潜在地避免跨代码块(具有相同名称空间)的可见性设置的污染。模板定义也同样有趣。 程序员可能总是对规则感到好奇,并且想知道当模板和type参数都具有可见性属性设置时如何设置可见性属性。 简⽽⾔之,如果主模板定义具有可见性,则该可见性将隐式传播到模板实例化。 否则,默认情况下,模板实例化的可见性与其模板参数相同。 有关⽰例,请参见清单6。清单6.命名空间的Visibility属性class __attribute__((visibility("internal"))) A {};template void foo1() {};template void __attribute__((visibility("hidden"))) foo2() {};foo1(); // The visibility of "foo1()" is "internal" which is propagated from its argument "A".foo2(); // The visibility of "foo2()" is "hidden" which is propagated from 2(); // The visibility of "foo2()" is "hidden".此代码将类型A定义为internal ,⼀个通⽤模板函数foo1和具有hidden可见性的模板函数foo2 。 对于第⼀个实例foo1,由于A具有可见性属性,因此该函数将从该模板参数传播可见性属性。 对于foo2 ,它从模板获取可见性属性。 ⽽且, foo2 。 实例化忽略类类型A的可见性属性。 因此,模板的可见性属性具有优先权。语⽤除了可见性属性外,还有另⼀种⽅法可以从源代码级别设置符号的可见性属性。 XL C / C ++编译器通过引⼊可见性杂注来提供此类功能。 实⽤程序⽐可见性属性具有⼀些优势。 它允许程序员为多个声明设置可见性,⽽⽆需逐个符号定义可见性。 可见性编译指⽰语法如清单7所⽰。清单7.可见性⽤法的语法#pragma GCC visibility push(hidden)#pragma GCC visibility pop可见性编译指⽰仅包括两个堆栈操作。 堆栈顶部将显⽰当前的可见性属性。 当前可见性可以应⽤于在代码点之后定义的所有变量。 例如,在Linux上,当前上下⽂的默认可见性是default 。 如果程序员使⽤pragma 推送 hidden可见性,则在push pragma之后定义的所有声明的可见性都是hidden 。 除⾮遇到其他可见性实⽤程序,否则它将不会更改。 清单8显⽰了⼀个⽰例。清单8.命名空间的Visibility属性#pragma GCC visibility push(hidden)int m; // "hidden" visibility#pragma GCC visibility push(protected)void foo() // "protected" visibility#pragma GCC visibility popclass A{}; // "hidden" visibility ("protected" visibility is popped)#pragma GCC visibility popint n; // "default" visibility#pragma GCC visibility pop // An error is emitted as there is no visibility pragma to pop.请注意,如果可见性堆栈已为空,则弹出编译指⽰可能会在编译过程中引起错误,具体情况如下所⽰。编译器选项有时,程序员可能还想从编译器选项中设置可见性属性。 使⽤这些选项,它们可以更改嵌⼊在编译⽂件中的符号的可见性。 对于XL C / C++编译器,您可以指定以下选项。xlc -qvisibility=default | protected | hidden | internal | unspecified注意, unspecified选项仅适⽤于AIX平台。 在Linux上, -qvisibility等效于–qvisibility=default 。 在AIX上, -qvisibility等效于–qvisibility=unspecified 。可见性确定规则由于可以通过可见性属性说明符,可见性编译指⽰或可见性选项设置声明可见性,因此了解有关编译器如何解决它们之间的冲突的规则⾮常重要。实际上,编译器最终将通过检查以下顺序来解决声明的可见性:可见性属性说明符(如果有)如果是模板实例,则其主要模板定义的可见性包含它的上下⽂(结构,类,联合,枚举,名称空间或编译指⽰)的可见性否则,其类型的可见性规则很简单。 ⾸先,可见性属性说明符如果是声明的⼀部分,则始终优先。 如果不正确,并且代码是模板实例化,则考虑主模板定义的可见性。 清单6向我们展⽰了这样的⼀个实例。否则,声明的可见性由其封闭上下⽂确定。 声明的封闭上下⽂可以是,例如,全局变量的名称空间范围或其成员的类范围,依此类推。 有关⽰例,请参见清单9。清单9.封闭上下⽂的Visibility属性#pragma GCC visibility push(hidden)class A // "hidden" from pragma{ static int m; // "hidden" from class definition void __attribute__((visibility("protected"))) foo() // foo is "protected"};在清单9中, class A被hidden因为该杂注定义了具有可见性hidden的封闭上下⽂。 它的成员也将hidden作为其可见性,因为其封闭上下⽂是A的定义。但是,由于存在属性说明符,因此foo设置为protected可见性。作为最后⼀条规则,声明的可见性由其类型决定。 清单4已经显⽰了有关类类型的⽰例。⽽对于没有任何可见性说明符的函数声明,其可见性由以下四个元素确定。功能参数函数返回类型函数模板参数(类型或⾮类型)从编译器传递的可见性选项通过⽐较这四个元素,不难发现可见性最⼩的元素。 在这⾥,我们认为default是最可见的, protected是次要的, hidden位置是第三,⽽internal是最不可见的。 该功能将从提供的四个元素中获得的可见性最⼩。 清单10显⽰了⼀个⽰例。清单10.函数的可见性确定class __attribute__((visibility("internal"))) A {};A *foo (int a) {}; // Even with option -qvisibility=hidden, foo is "internal".在此⽰例中,⽤户可能会观察到foo函数正在将指针类型返回给class A , class A是使⽤internal可见性定义的。 在全局上下⽂中,有⼀个选项定义可见性为hidden 。 foo的最终解析结果是internal因为它的可见性最⼩。如果上⾯列出的规则不能应⽤于变量声明或函数,则会为它们设置默认的可见性。 默认可见性在以下部分中描述。AIX上的可见性属性和向后兼容性从上⼀篇⽂章中我们知道,在AIX上,默认情况下不可见符号,除⾮我们在链接阶段⼿动或借助CreateExportList导出它们。 但是,在Linux上,符号默认情况下具有导出(即default )可见性。 这在AIX可见性属性和GNU可见性属性之间造成了差距。 为了向后兼容,在AIX上,XL C / C ++不会像Linux⼀样设置所有要导出的符号。 它可能认为没有任何可见性设置的符号是⾮特定的可见性,与旧的AIX实现保持⼀致。 对于此类符号,AIX编译器,链接器和相关⼯具将像以前⼀样处理它。 但是,不明确的可见性并不意味着该符号是内部的或根本不可见的。 这只是为了保持兼容性⽽专门设计的可见性。因此,正如我们在本⽂开头提到的那样,如果程序员未明确指定符号的可见性属性,则在Linux上,符号的可见性可能是默认值 。 但是在AIX上,可见性是不确定的 。链接时的可见性冲突在AIX上链接时的可见性冲突从上⼀篇⽂章中,我们知道,在AIX系统上,可以通过使⽤导出⽂件来确定是否导出了库/可执⾏⽂件中的符号。 但是,由于IBM XL C / C++ V13编译器引⼊了符号可见性属性,因此必须知道该功能如何与导出⽂件交互。考虑⽂件中的⼀个典型定义,如清单11所⽰。清单11.⽂件 sym __attribute__((visibility("default")));变量sym是使⽤源代码中的default (导出)属性定义的。 我们可以编译⽂件以⽣成⼀个动态共享库。 我们希望sym是库中的导出符号。 但是,如果在链接步骤中给出了exportlist(导出⽂件)(如清单12所⽰),我们将-bE:exportlist选项传递给链接器以⽣成库(如清单13所⽰)。 ,通常会提出⼀个问题: sym最终是共享库中的全局可见(导出)符号。清单12. exportlist⽂件sym hidden清单13.编译命令xlC -G -o a.c -qpic -bE:exportlist这样的问题给链接器/编译器设计带来了挑战。 但是作为设计师,我们注意到:可见性属性将由AIX的XL C / C ++ V13引⼊。 它的⽬标是更多新代码和移植代码(即接受GNU可见性属性的代码)。在链接阶段,exportlist⽬标中的可见性,通常在编译阶段之后调⽤。因此,链接器最好使导出⽂件中的可见性关键字优先。 实际上,链接器可能必须处理来⾃导出⽂件(定义符号的可见性关键字)和诸如对象⽂件(提供符号的可见性属性)之类的模块的不同可见性关键字/属性组合。 这种处理也称为可视性冲突的解决 。 表1描述了链接器的⼯作⽅式。表1.通过AIX链接程序解决的可见性冲突导出⽂件中的关键字⽬标⽂件中的符号可见性属性进⼝的出⼝受保护的为 hidden我 nternalN 个(⽆关键字)经验值保护错误错误没有已出⼝经验值保护没有没有经验值受保护的经验值保护没有没有保护隐经验值保护没有没有没有内部错误错误错误没有没有没有经验值保护没有没有没有EXP :符号已导出PROT :导出具有受保护可见性的符号否 :不导出符号错误 :未导出符号。 打印错误消息,链接失败在表1中,⾏标题显⽰了源模块中定义的符号可见性。 列标题是在导出⽂件中定义的符号关键字。 注意,在某些版本的AIX中,关键字可以导出⽽不是export 。 表1显⽰了链接器可见的符号的最终分辨率结果,包括:符号在⽬标模块中是否可见(此类模块可以是最终构建的库)导出的符号是否可以被抢占如表1所⽰,如果导出⽂件声称该符号已导出或受保护 ,则该符号最终将被链接器解析为全局可见(不能抢占后者)。 同样,如果导出⽂件将符号描述为hidden或internal ,则该符号最终将不可见。 此外,如果未指定导出⽂件-bexpall/-bxpfull指定链接选项-bexpall/-bxpfull ,则对象⽂件中的符号可见性优先。 例如,对于某些⾮理性组合,该符号在⽬标⽂件中具有内部属性,⽽导出⽂件希望将其导出 ,则链接器将引发错误。 原因是导出⽂件可能会改变程序员的意图。结果,对于前⾯提到的⽂件, sym变量的符号最终将在全局范围内不可见。有时,程序员可能仍然选择使⽤链接器选项-bexpall/-bexpfull来导出所有符号。 但是,作为这种可见性分辨率的新设计,在这种情况下,除⾮未为符号定义可见性(⾮特定性),否则⽬标⽂件中的符号可见性将优先。现在,我们了解到导出⽂件通常可以在最后的链接步骤中覆盖可见性。 但是,我们不建议在新代码中同时使⽤导出⽂件和可见性属性。 混合使⽤可能会给代码开发带来混乱。 但是,有时此功能对于确保共享库的发⾏版本之间的⼆进制兼容性仍然⾮常有⽤。在Linux上链接时的可见性冲突链接时的可见性冲突也发⽣在Linux平台上。 但是,由于平台的不同,与AIX链接器相⽐,GNU链接器的⾏为略有不同。清单14显⽰了Linux平台上的⼀个⽰例,假设我们在cc中有⼀个变量:清单14.⽂件ccint bar __attribute__((visibility("protected")));并且我们在exmap导出地图⽂件中将 bar声明为全局可见。 GNU链接器还将其命名为版本脚本 。 请注意,程序员可以在导出映射中将符号声明为全局可见符号或局部符号。清单15. exmap⽂件{ global: bar; local: *;};使⽤清单16中所⽰的命令来构建动态共享库(DSO)。清单16.编译命令xlC -qmkshrobj c.c -o -Wl,--version-script=exmap最后,是时候检查的动态符号表(DYNSYM)部分(通常称为.dynsym)了。清单17.符号栏的转储5 0: 0001100c 4 OBJECT GLOBAL PROTECTED 21 bar同样,在Linux平台上,如果导出了符号,则将其拾取到DSO的DYNSYM部分。 表2显⽰了导出地图和可见性属性如何确定是否应导出符号(选择为DYNSYM)。表2. Linux链接程序解决的可见性冲突导出⽂件中的关键字⽬标⽂件中的符号可见性属性默认全球本地是没有受保护的是没有隐没有没有内部没有没有与表1相似,表2的第⼀⾏显⽰了源模块中的符号可见性。 ⼀般来说,这与AIX相似,因为关键字优先。 但是Linux版本脚本⽆法导出隐藏的符号。 这是由于这两个平台的性质不同⽽引起的。AIX和Linux之间的另⼀个区别是DYNSYM的符号和SYMTAB的符号都维护可见性属性信息。 但是,AIX在符号表和装⼊程序符号表中的符号之间没有保留相同的属性。 可见性属性信息仅在符号表中有效。 加载程序符号表仅显⽰最终的可见性分辨率结果。 如果此处列出了符号,则将其导出。 否则,该符号不可见。 这也是AIX和Linux平台之间的区别。CreateExportList⼯具在上⼀篇⽂章中,我们介绍了CreateExportList⼯具,该⼯具有助于⾃动⽣成导出⽂件。 程序员可能仍会关⼼它如何与可见性属性进⾏交互。 CreateExportList⼯具的⼀般规则是将所有全局可见符号与匹配的可见性属性作为关键字⼀起添加到导出⽂件。 表3显⽰了是否可以将符号var提取到导出⽂件中,以及如何为该符号⽣成关键字。表3. CreateExportList的新⼯作规则.o中的可见性属性(未指定)出⼝的受保护的隐内部导出⽂件中的表⽰形式变种var已导出var保护----评论为了保持旧代码的兼容性,此规则保持不变关键字已导出关键字受保护此可见性属性的符号未提取到exportlist中此可见性属性的符号未提取到exportlist中参考表1,读者通常会发现它⼯作良好,因为链接器可以正确处理冲突。 并且,当我们从混合对象(例如,具有符号可见性的旧对象和新对象)构建模块(库或可执⾏⽂件)时,此⼯具⾮常有⽤。 此外,它可以确保在编译器版本更新时,旧的构建脚本不会中断。汇编语法编写汇编代码的程序员也许也可以分配具有不同可见性的符号。 清单14提供了.globl , .comm , .extern和.weak伪操作的⽰例语法。清单14.在程序集中指定可见性属性的语法.extern Name [ , Visibility ] .globl Name [ , Visibility ] .weak Name [ , Visibility ] .comm Name, Expression [, Number [, Visibility ] ]For .comm , if "Visibility" is specified, "Number" (alignment) can be omitted: .comm Name, Expression , , Visibility此处列出的Visibility关键字的定义与清单14相似,并且可以具有四个值: internal , hidden , protected和exported 。 如果未定义可见性,则符号条⽬可能没有任何可见性属性(未指定)。 这类似于C / C ++源代码中的规则。但是,除⾮汇编程序的版本是最新的,否则汇编程序将不接受此类语法。 AIX链接器需要类似的预需求。 这意味着AIX 6.1 TL8 / AIX 7.1TL2或更⾼版本将满⾜要求。 并且⽤于AIX,V13.1或更⾼版本的XL C / C ++编译器⾜以⽀持C / C ++代码中的可见性功能。

发布者:admin,转转请注明出处:http://www.yc00.com/xiaochengxu/1690561087a369184.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信