使用标准 C,您只能导出或不导出函数,没有“仅导出到这些文件”选项。所以基本上你必须移动bar()
to foo.c
并将其声明为static
。如果您希望将文件分开,一个丑陋的黑客将是#include
它来自foo.c
(并且不编译bar.o
)…
使用标准 C 范围之外的工具,您可以在链接时或链接后从库中删除公共导出。下面显示了一些链接器解决方案,使用 GCC 和 clang(在可以修改代码的情况下),您可以通过在函数前面添加非标准属性来隐藏函数:__attribute__ ((visibility ("hidden")))
– 与此等效的编译单元范围将是选项-fvisibility=hidden
编译时,例如bar.c
.
C 解决方法
如果您可以自由编辑 C 代码,标准 C 中的解决方法是bar()
static
within bar.c
并传递一个函数指针给它以供使用foo()
通过某种方式,例如导出一个指向struct
包含函数指针(以及任何其他“私有”数据),并且不暴露函数的详细信息struct
在仅供您的库使用的私有标头之外。
例如:
In bar.h
(私人的,不要与图书馆的用户共享):
struct bar_private_struct { int (*bar)(int); };
extern struct bar_private_struct *bar_functions;
In 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;
In foo.c
:
#include "bar.h"
int foo (int x) { x = bar_functions->bar(x); /* … */ }
在此解决方案中,将有一个名为的导出指针bar_functions
,但没有通过此导出揭示有关所指向的数据/函数的详细信息。无法访问bar.h
库的用户必须对内容进行逆向工程才能正确调用“私有”函数。如果有多个“私有”函数,此方法还可以将它们压缩为单个导出指针,从而消除导出列表中的混乱。
链接器解决方案
通过探索特定的链接器,我找到了一种从动态库中排除特定符号的方法:
与 GNUld
,创建版本脚本,如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