标题可能并不完全清楚。我正在使用两个源文件(一个用C语言,一个用Haskell语言)来编译一个共享库(.so)。我希望一个文件中的一些函数可以从另一个文件中的函数调用。但是,我不希望它们可以从加载库的程序调用。如果我将它们声明为静态的,其他文件的代码就看不到它们。有没有办法做到这一点?它是否需要配置链接器或在事后修改.so文件?
qyswt5oh1#
在创建.so时,您可以/应该使用链接器 * 脚本 * 执行此操作但是,首先,看看info gcc和-fvisibility=部分[包括在下面]。您也可以在每个函式上使用__attribute__((visibility("...")))。例如,如果大多数函数是内部的(只有少数是公共API的一部分),则可以使用默认的隐藏可见性,并向公共函数添加显式属性。
.so
info gcc
-fvisibility=
__attribute__((visibility("...")))
来自info gcc:
-fvisibility=[default|internal|hidden|protected]将默认的ELF图像符号可见性设置为指定的选项--除非在代码中被覆盖,否则所有符号都用此选项标记。使用此功能可以极大地改善共享对象库的链接和加载时间,生成更优化的代码,提供近乎完美的API导出并防止符号冲突。* 强烈 * 建议您在分发的任何共享对象中使用此选项。尽管有命名法,default总是意味着公共; protected和internal在实际应用中几乎没有什么用处,因此唯一常用的选项是hidden。如果没有指定-fvisibility,则默认值为default,也就是说,将每个符号都设置为公共。Ulrich Drepper的“How To Write Shared Libraries”很好地解释了确保ELF符号具有正确可见性所带来的好处(其可以在https://www.akkadia.org/drepper/中找到)--然而,当缺省值是公共的时,通过该选项使标记隐藏的内容成为可能的一种上级的解决方案是使缺省值为隐藏并将内容标记为公共。这是Windows上DLL的标准,使用-fvisibility=hidden和__attribute__ ((visibility("default")))而不是__declspec(dllexport),您可以获得几乎相同的语义和相同的语法。这对那些跨平台项目的工作人员来说是一个巨大的布恩。对于那些在现有代码中添加可见性支持的人,你可能会发现#pragma GCC visibility的用处。这是通过用(例如)#pragma GCC visibility push(hidden)和#pragma GCC visibility pop来封装你希望设置可见性的声明来实现的。请记住,符号可见性应该被视为 * API接口协定的一部分 *,因此所有新代码都应该在它不是默认值时指定可见性;也就是说,只在本地DSO中使用的声明应该 * 总是 * 显式标记为隐藏,以避免PLT间接开销--使这一点非常清楚也有助于代码的可读性和自文档化。注意,由于ISO C规范要求,operator new和operator delete必须总是默认可见。请注意,来自项目外部的头文件,特别是系统头文件和您使用的任何其他库的头文件,可能不希望以默认可见性以外的其他可见性进行编译。在包括任何此类头文件之前,您可能需要显式地说#pragma GCC visibility push(default)。extern声明不受-fvisibility的影响,因此许多代码可以用-fvisibility=hidden重新编译而无需修改。然而,这意味着对extern函数的调用没有显式可见性,而是使用PLT。因此,使用__attribute ((visibility))和/或#pragma GCC visibility来告诉编译器哪些extern声明应该被视为隐藏声明会更有效。请注意,-fvisibility确实会影响C模糊链接实体。这意味着,例如,在DSO之间抛出的异常类必须用默认可见性显式标记,以便type_info节点在DSO之间统一。这些技术的概述、它们的优点以及如何使用它们,请访问https://gcc.gnu.org/wiki/Visibility
-fvisibility=[default|internal|hidden|protected]
default
protected
internal
hidden
-fvisibility
-fvisibility=hidden
__attribute__ ((visibility("default")))
__declspec(dllexport)
#pragma GCC visibility
#pragma GCC visibility push(hidden)
#pragma GCC visibility pop
operator new
operator delete
#pragma GCC visibility push(default)
extern
__attribute ((visibility))
type_info
1条答案
按热度按时间qyswt5oh1#
在创建
.so
时,您可以/应该使用链接器 * 脚本 * 执行此操作但是,首先,看看
info gcc
和-fvisibility=
部分[包括在下面]。您也可以在每个函式上使用
__attribute__((visibility("...")))
。例如,如果大多数函数是内部的(只有少数是公共API的一部分),则可以使用默认的隐藏可见性,并向公共函数添加显式属性。
来自
info gcc
:-fvisibility=[default|internal|hidden|protected]
将默认的ELF图像符号可见性设置为指定的选项--除非在代码中被覆盖,否则所有符号都用此选项标记。使用此功能可以极大地改善共享对象库的链接和加载时间,生成更优化的代码,提供近乎完美的API导出并防止符号冲突。* 强烈 * 建议您在分发的任何共享对象中使用此选项。
尽管有命名法,
default
总是意味着公共;protected
和internal
在实际应用中几乎没有什么用处,因此唯一常用的选项是hidden
。如果没有指定-fvisibility
,则默认值为default
,也就是说,将每个符号都设置为公共。Ulrich Drepper的“How To Write Shared Libraries”很好地解释了确保ELF符号具有正确可见性所带来的好处(其可以在https://www.akkadia.org/drepper/中找到)--然而,当缺省值是公共的时,通过该选项使标记隐藏的内容成为可能的一种上级的解决方案是使缺省值为隐藏并将内容标记为公共。这是Windows上DLL的标准,使用
-fvisibility=hidden
和__attribute__ ((visibility("default")))
而不是__declspec(dllexport)
,您可以获得几乎相同的语义和相同的语法。这对那些跨平台项目的工作人员来说是一个巨大的布恩。对于那些在现有代码中添加可见性支持的人,你可能会发现
#pragma GCC visibility
的用处。这是通过用(例如)#pragma GCC visibility push(hidden)
和#pragma GCC visibility pop
来封装你希望设置可见性的声明来实现的。请记住,符号可见性应该被视为 * API接口协定的一部分 *,因此所有新代码都应该在它不是默认值时指定可见性;也就是说,只在本地DSO中使用的声明应该 * 总是 * 显式标记为隐藏,以避免PLT间接开销--使这一点非常清楚也有助于代码的可读性和自文档化。注意,由于ISO C规范要求,operator new
和operator delete
必须总是默认可见。请注意,来自项目外部的头文件,特别是系统头文件和您使用的任何其他库的头文件,可能不希望以默认可见性以外的其他可见性进行编译。在包括任何此类头文件之前,您可能需要显式地说
#pragma GCC visibility push(default)
。extern
声明不受-fvisibility
的影响,因此许多代码可以用-fvisibility=hidden
重新编译而无需修改。然而,这意味着对extern
函数的调用没有显式可见性,而是使用PLT。因此,使用__attribute ((visibility))
和/或#pragma GCC visibility
来告诉编译器哪些extern
声明应该被视为隐藏声明会更有效。请注意,
-fvisibility
确实会影响C模糊链接实体。这意味着,例如,在DSO之间抛出的异常类必须用默认可见性显式标记,以便type_info
节点在DSO之间统一。这些技术的概述、它们的优点以及如何使用它们,请访问https://gcc.gnu.org/wiki/Visibility