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

如何使函数具有库内部链接?

  •  5
  • Matt  · 技术社区  · 10 年前

    例如,如果我有两个文件 foo.c bar.o 食品c 包含一个函数 foo() 引用函数的 bar() 在里面 巴.o :

    int foo(int x) { x = bar(x); /* ... */ }
    

    如何编译静态或动态库 foo() ,但不显示 巴() ? 换句话说,我想要 巴() 仅在库内链接。

    2 回复  |  直到 10 年前
        1
  •  9
  •   Arkku    10 年前

    使用标准C,您只能导出函数或不导出函数,没有仅导出到这些文件的选项。所以基本上你必须移动 bar() foo.c 并声明为 static 。如果你想把文件分开,一个丑陋的黑客会 #include 它来自 食品c (不编译 bar.o )

    使用标准C范围之外的工具,您可以在链接时或链接后从库中删除公共导出。下面显示了一些链接器解决方案,使用GCC和clang(在可以修改代码的情况下),可以通过在函数前面加上非标准属性来隐藏函数: __attribute__ ((visibility ("hidden"))) 编译单元范围内与此等效的选项 -fvisibility=hidden 当编译(例如。, bar.c .

    C解决方法

    如果您可以自由编辑C代码,那么标准C中的解决方法是 巴() 静止的 在内部 钢筋混凝土 并将函数指针传递给它,以便在 foo() 通过某些方式,例如,将指针导出到 struct 包含函数指针(和任何其他私有数据),并且不公开 结构 在仅由库使用的私有标头之外。

    例如:

    在里面 bar.h (私有,不与库的用户共享):

    struct bar_private_struct { int (*bar)(int); };
    extern struct bar_private_struct *bar_functions;
    

    在里面 钢筋混凝土 :

    #include "bar.h"
    static int bar (int x) { /* … */ return x; }
    static struct bar_private_struct functions = { bar };
    struct bar_private_struct *bar_functions = &functions;
    

    在里面 食品c :

    #include "bar.h"
    int foo (int x) { x = bar_functions->bar(x); /* … */ }
    

    在此解决方案中,将有一个名为 bar_functions ,但未通过此导出显示有关指向的数据/函数的详细信息。无法访问 巴.h 库的用户必须对内容进行反向工程,才能正确调用私有函数。在多个私有函数的情况下,这种方法还可以将它们压缩为单个导出指针,从而从导出列表中消除混乱。

    Linker解决方案

    通过探索特定的链接器,我找到了一种从动态库中排除特定符号的方法:

    使用GNU ld ,创建版本脚本,例如 libfoobar.version :

    FOOBAR {
        global: *;
        local: bar;
    };
    

    通过调用 gcc :

    gcc -shared -o libfoobar.so foo.o bar.o -Wl,-version-script=libfoobar.version
    

    使用 clang 激光二极管 (在OS X上)创建未显示符号的列表,例如 unexported (每行一个符号):

    _bar
    

    通过调用 叮当声 :

    clang -shared -o libfoobar.dylib foo.o bar.o -Wl,-unexported_symbols_list,unexported
    

    在这两种情况下,函数 bar 是隐藏的,外部不可调用,但 foo 保持运行(和通话 酒吧 内部),即使两者在各自的源(和对象)文件中具有相同的外部可见性。

    测试代码, 食品c :

    int bar(int x);
    int foo (int x) { return bar(x) * 3; }
    

    钢筋混凝土 :

    int bar (int x) { return x * 2; }
    

    main.c (在删除导出之前链接到库 酒吧 ):

    #include <stdio.h>
    int foo(int x);
    int bar(int x);
    int main () {
        (void) printf("foo(2) = %d\n", foo(2));
        (void) printf("bar(2) = %d\n", bar(2));
        return 0;
    }
    

    测试用例:

    # before unexporting bar:
    $ nm -gU libfoobar.dylib
    0000000000000f70 T _bar
    0000000000000f50 T _foo
    $ ./main
    foo(2) = 12
    bar(2) = 4
    
    # after unexporting bar:
    $ nm -gU libfoobar.dylib
    0000000000000f50 T _foo
    $ ./main
    foo(2) = 12
    dyld: lazy symbol binding failed: Symbol not found: _bar
    
        2
  •  0
  •   ouah    10 年前

    函数只能具有内部或外部链接。

    如果你想使用不同的模块,你的函数需要有外部链接,这样它们就可以从一个翻译单元调用到另一个。

    您可以使用指向静态函数的外部函数指针,但当然,这仍然允许其他模块通过函数指针调用函数。