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

时间:2015-02-18 16:30:17

标签: c gcc compilation linker ld

例如,如果我有两个文件foo.cbar.ofoo.c包含引用{{1}中的函数foo()的函数bar() }}:

bar.o

如何编译公开int foo(int x) { x = bar(x); /* ... */ } 但不公开foo()的静态或动态库?换句话说,我希望bar()仅在库内链接。

2 个答案:

答案 0 :(得分:7)

使用标准C,您只能导出该功能,没有“仅导出到这些文件”选项。所以基本上你必须将bar()移到foo.c并将其声明为static。如果您希望将文件保持独立,那么来自#include的{​​{1}}(而不是编译foo.c)将会是一个丑陋的黑客攻击...

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

C解决方法

如果您可以自由编辑C代码,标准C中的解决方法是在bar.c中生成bar() static并提供一个函数指针,以便在{{1}中使用通过某种方式,例如,导出指向包含函数指针(和任何其他“私有”数据)的bar.c的指针,并且不要在私有头之外公开foo()的详细信息仅供您的图书馆使用。

例如:

struct中(私有,不要与图书馆用户共享):

struct

bar.h

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); /* … */ } ,则库的用户必须对内容进行反向工程以正确调用“私有”函数。在多个“私有”函数的情况下,这种方法也可以将它们压缩为单个导出的指针,从导出列表中消除混乱。

链接器解决方案

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

使用GNU bar_functions,创建一个版本脚本,例如bar.h

ld

通过libfoobar.version调用:

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

使用gcc gcc -shared -o libfoobar.so foo.o bar.o -Wl,-version-script=libfoobar.version (在OS X上)创建一个未导出符号列表,例如clang(每行一个符号):

ld

通过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

答案 1 :(得分:0)

功能只能有内部或外部联系。

如果您想使用不同的模块,您的功能需要具有外部链接,以便可以从一个翻译单元调用到另一个翻译单元。

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