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

标签 c gcc compilation linker ld

例如,如果我有两个文件 foo.cbar.o,并且 foo.c 包含一个函数 foo() 引用 bar.o 中的函数 bar():

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

如何编译公开foo()但不公开bar()的静态或动态库?换句话说,我希望 bar() 仅在库内链接。

最佳答案

使用标准 C,您只能导出或不导出函数,没有“仅导出到这些文件”选项。所以基本上你必须将 bar() 移动到 foo.c 并将其声明为 static。如果您希望将文件分开,一个丑陋的技巧是 #include 来自 foo.c (而不是编译 bar.o )…

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

C 解决方法

如果您可以自由编辑 C 代码,标准 C 中的解决方法是在 bar.c 中制作 bar() static并通过某种方式向它传递一个函数指针,以便在 foo() 中使用,例如,导出一个指向包含函数指针(以及任何其他“私有(private)”)的 struct 的指针数据),并且不要在仅供您的库使用的私有(private) header 之外公开 struct 的详细信息。

例如:

bar.h 中(私有(private),不要与库的用户共享):

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

bar.c中:

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

foo.c中:

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

在此解决方案中,将有一个名为 bar_functions 的导出指针,但不会通过此导出显示有关指向的数据/函数的详细信息。如果无法访问 bar.h,库的用户将不得不对内容进行逆向工程以正确调用“私有(private)”函数。在多个“私有(private)”函数的情况下,这种方法还可以将它们压缩为单个导出指针,从而消除导出列表中的困惑。

链接器解决方案

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

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

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

通过gcc调用:

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

使用 clang ld(在 OS X 上)创建未导出符号列表,例如 unexported(每行一个符号):

_bar

通过clang调用:

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

在这两种情况下,函数 bar 都是隐藏的并且无法从外部调用,但是 foo 仍然可以运行(并在内部调用 bar),即使两者都已在各自的源(和目标)文件中具有相同的外部可见性。

测试代码,foo.c:

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

bar.c:

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

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

#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

关于c - 如何使函数具有库内部链接?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28588661/

相关文章:

java - 从命令行编译类时出错

java - 使用终端编译java项目

c - Unix getaddrinfo C函数启动服务器的用法

c - 通过信号 SIGQUIT 唤醒 sleep 守护进程

c - 错误 : expected a ")" on CUDA kernel code

c - 从 C 调用 Ada 时 Ada 查找表不起作用

c - 为什么 unsigned typeof(var) 不起作用? const typeof(x) 工作得很好,为什么不呢?

gcc - __USE_XOPEN2K8的用途以及如何设置?

regex - 从 gcc/g++ 编译结果中删除文件名

c++ - 在 Windows 上编译命令行 Linux 程序