代码之家  ›  专栏  ›  技术社区  ›  rafl

链接共享库时限制符号的可见性

  •  43
  • rafl  · 技术社区  · 17 年前

    某些平台要求您向链接器提供共享库的外部符号列表。但是,在大多数unixish系统上,这是不必要的:默认情况下,所有非静态符号都可用。

    我的理解是,GNU工具链可以选择性地将可见性限制为仅显式声明的符号。如何使用GNU ld实现这一点?

    4 回复  |  直到 17 年前
        1
  •  74
  •   Employed Russian    17 年前

    ld

    以下是如何使用链接器版本脚本执行此操作:

    /* foo.c */
    int foo() { return 42; }
    int bar() { return foo() + 1; }
    int baz() { return bar() - 1; }
    
    gcc -fPIC -shared -o libfoo.so foo.c && nm -D libfoo.so | grep ' T '
    

    默认情况下,将导出所有符号:

    0000000000000718 T _fini
    00000000000005b8 T _init
    00000000000006b7 T bar
    00000000000006c9 T baz
    00000000000006ac T foo
    

    假设您只想导出 bar() baz() . 创建“版本脚本” libfoo.version :

    FOO {
      global: bar; baz; # explicitly list symbols to be exported
      local: *;         # hide everything else
    };
    

    gcc -fPIC -shared -o libfoo.so foo.c -Wl,--version-script=libfoo.version
    

    请遵守导出的符号:

    nm -D libfoo.so | grep ' T '
    00000000000005f7 T bar
    0000000000000609 T baz
    
        2
  •  46
  •   a3f    10 年前

    -fvisibility=hidden 使用gcc选项,并在代码中显式公开某些符号(通过 __attribute__((visibility("default"))) )。请参阅文档 here .

    也许有一种方法可以通过ld链接器脚本来实现,但我对它知之甚少。

        3
  •  7
  •   user188731 user188731    16 年前

    生成用于调用任何导出函数或使用任何导出全局函数的代码的效率低于未导出的代码。这涉及到一个额外的间接层次。这适用于任何 出口于 时间gcc仍将为稍后由链接器脚本取消导出的函数生成额外的间接寻址。因此,使用visibility属性将生成比链接器脚本更好的代码。

        4
  •  3
  •   codesniffer    6 年前

    似乎有几种方法可以在GNU/Linux上管理导出的符号。根据我的阅读,以下是3种方法:

    • 源代码注释/装饰:
      • 方法1: -fvisibility=hidden 随着 __attribute__((visibility("default")))
      • 方法2(自GCC 4起): #pragma GCC visibility
    • 版本脚本:
      • -Wl,--version-script=<version script file> )

    我不会在这里讨论这些例子,因为它们大部分都被其他答案所涵盖,但这里有一些注意事项、优点和缺点;我头脑中的不同方法的缺点:

    • 使用带注释的方法允许编译器稍微优化代码(减少一个间接寻址)。
    • 如果使用注释的方法,那么也要考虑使用 strip --strip-all --discard-all .
    • 带注释的方法可以为内部功能级单元测试添加更多工作,因为单元测试可能无法访问符号。这可能需要构建单独的文件:一个用于内部开发&测试,另一个用于生产。(从单元测试纯粹主义者的角度来看,这种方法通常是非最优的。)
    • 使用版本脚本会丢失优化,但会允许符号版本控制,这似乎不适用于带注释的方法。
    • Mac上不支持版本脚本(至少在使用Mac提供的链接器时不支持,即使编译器使用GCC),因此如果需要Mac,请使用带注释的方法。

    我肯定还有其他人。

    以下是一些我认为有用的参考资料(附示例):

        5
  •  1
  •   aaa90210    9 年前

    如果您使用的是libtool,那么还有另一个选项,非常类似于Employed Russian的答案。

    以他的例子来说,可能是这样的:

    cat export.sym
    bar
    baz
    

    libtool -export-symbols export.sym ...
    

    请注意,使用-export符号时,默认情况下不会导出所有符号,而只导出export.sym中的符号(因此libfoo.version中的“local:*”行实际上在这种方法中是隐式的)。