【问题标题】:Symbol visibility not working as expected符号可见性未按预期工作
【发布时间】:2016-10-22 09:49:47
【问题描述】:

我有一个这样的示例程序:

#include <stdio.h>

#if 1
#define FOR_EXPORT __attribute__ ((visibility("hidden")))
#else
#define FOR_EXPORT
#endif

FOR_EXPORT void mylocalfunction1(void)
{
    printf("function1\n");
}

void mylocalfunction2(void)
{
    printf("function2\n");
}

void mylocalfunction3(void)
{
    printf("function3\n");
}

void printMessage(void)
{
    printf("Running the function exported from the shared library\n");
}

并使用编译它

gcc -shared -fPIC -fvisibility=hidden -o libdefaultvisibility.so defaultvisibility.c

现在编译后我会这样做:

$ nm libdefaultvisibility.so
nm libdefaultvisibility.so 
0000000000000eb0 t _mylocalfunction1
0000000000000ed0 t _mylocalfunction2
0000000000000ef0 t _mylocalfunction3
0000000000000f10 t _printMessage
                 U _printf
                 U dyld_stub_binder

据我所知,这意味着尽管-fvisibility=hidden 所有符号都会被导出。我正在关注的这本书声称只有标有FOR_EXPORT 的函数应该被导出。

我查看了其他几个资源,但对于我正在做的简单测试-fvisibility=hidden 应该足够了。

我的 clang 版本:

$ clang -v
clang -v
Apple LLVM version 7.3.0 (clang-703.0.31)
Target: x86_64-apple-darwin15.0.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

【问题讨论】:

标签: c linker clang


【解决方案1】:

您误解了nm 的输出。滚动浏览man nm,你会的 读到t 标志意味着该符号是本地(静态)符号 text 部分。链接器看不到它。如果它是全局的(外部) 标志将是T。所以你的所有四个函数都是本地的。

对比度:

$ clang -shared -fPIC -fvisibility=hidden -o libdefaultvisibility.so defaultvisibility.c
$ nm libdefaultvisibility.so | grep ' t '
0000000000000570 t deregister_tm_clones
0000000000000600 t __do_global_dtors_aux
0000000000200e08 t __do_global_dtors_aux_fini_array_entry
0000000000000640 t frame_dummy
0000000000200e00 t __frame_dummy_init_array_entry
0000000000000670 t mylocalfunction1
0000000000000690 t mylocalfunction2
00000000000006b0 t mylocalfunction3
00000000000006d0 t printMessage
00000000000005b0 t register_tm_clones   

删除-fvisibility=hidden:

$ clang -shared -fPIC -o libdefaultvisibility.so defaultvisibility.c
$ nm libdefaultvisibility.so | grep ' t '
0000000000000600 t deregister_tm_clones
0000000000000690 t __do_global_dtors_aux
0000000000200e08 t __do_global_dtors_aux_fini_array_entry
00000000000006d0 t frame_dummy
0000000000200e00 t __frame_dummy_init_array_entry
0000000000000700 t mylocalfunction1
0000000000000640 t register_tm_clones
$ nm libdefaultvisibility.so | grep ' T '
0000000000000780 T _fini
00000000000005b0 T _init
0000000000000720 T mylocalfunction2
0000000000000740 T mylocalfunction3
0000000000000760 T printMessage

那么只有显式隐藏的mylocalfunction1 仍然是本地的,并且 其他三个现在是全球性的。

您不应期望标有__attribute__ ((visibility("hidden"))) 的符号 在任何情况下都将由共享库导出。该属性的确切含义 它将不是,无论它是否显式应用于符号,如在本例中, 或在存在链接器选项-fvisibility=hidden 的情况下默认获取。

如果您想通过visibility 属性仅导出示例中的那个函数 你会:

#define FOR_EXPORT __attribute__ ((visibility("default")))

然后:

$ clang -shared -fPIC -fvisibility=hidden -o libdefaultvisibility.so defaultvisibility.c
$ nm libdefaultvisibility.so | grep ' T '
0000000000000720 T _fini
0000000000000550 T _init
00000000000006a0 T mylocalfunction1

它是全局的,因为显式属性会覆盖命令行选项, 并且您的所有其他功能都是本地的。可能令人困惑的是,default 可见性 始终公开

您可以在不诉诸visibility 属性的情况下完成此操作 - 它们是 不可移植 - 只需将您不想导出的所有函数声明为static。然后编译器 首先不会将它们暴露给链接器:

foo.c

#include <stdio.h>

void mylocalfunction1(void)
{
    printf("function1\n");
}

static void mylocalfunction2(void)
{
    printf("function2\n");
}

static void mylocalfunction3(void)
{
    printf("function3\n");
}

static void printMessage(void)
{
    printf("Running the function exported from the shared library\n");
}

你又得到了:-

$ clang -shared -fPIC -o libfoo.so foo.c
$ nm libfoo.so | grep ' T '
00000000000006c0 T _fini
0000000000000550 T _init
00000000000006a0 T mylocalfunction1

虽然在您的示例中并没有体现出这种区别,但您 应该明白,虽然链接器看不到本地/静态符号并且(因此)对于 动态 链接不可用,但全局/外部符号 可能会或可能不会可用于动态链接。 visibility 仅控制 动态 链接的全局符号的可用性。

【讨论】:

  • 谢谢,我确实误解了nm 的输出,尤其是关于Tt 的输出。它实际上按预期工作。我正在阅读的这本书非常简短地介绍了如何解释nm 的输出,因此我不确定我在看什么。这个答案让我明白了。
【解决方案2】:

根据GCC Wiki 的可见性,您应该:

在输出的 DSO [Dynamic Shared Object] 上使用nm -C -D 比较前后看看 它带来的不同。

nm手册所述:

-D 将显示动态符号而不是普通符号

如果我完全按照你的方式编译你的代码,我会得到以下对象:

$ nm -C -D libdefaultvisibility.so
nm -C -D libdefaultvisibility.so
0000000000200a68 B __bss_start
                 w __cxa_finalize
0000000000200a68 D _edata
0000000000200a70 B _end
00000000000006c8 T _fini
                 w __gmon_start__
0000000000000518 T _init
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
                 w _Jv_RegisterClasses
                 U puts

如果我在没有 -fvisibility=hidden 选项的情况下编译它,我会得到对象:

$ nm -C -D libdefaultvisibility.so
nm -C -D libdefaultvisibility.so
0000000000200ae8 B __bss_start
                 w __cxa_finalize
0000000000200ae8 D _edata
0000000000200af0 B _end
0000000000000748 T _fini
                 w __gmon_start__
00000000000005a0 T _init
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
                 w _Jv_RegisterClasses
0000000000000712 T mylocalfunction2
0000000000000724 T mylocalfunction3
0000000000000736 T printMessage
                 U puts

【讨论】:

    猜你喜欢
    • 2011-11-27
    • 2014-06-10
    • 2021-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多