如何使函数具有库内部链接?
How can I make a function have library-internal linkage?
例如,如果我有两个文件 foo.c
和 bar.o
,并且 foo.c
包含一个函数 foo()
,它引用 bar()
中的一个函数 bar.o
:
int foo(int x) { x = bar(x); /* ... */ }
如何编译公开 foo()
但不公开 bar()
的静态或动态库?换句话说,我希望 bar()
仅在库内链接。
使用标准 C,您只能导出或不导出函数,没有“仅导出到这些文件”选项。所以基本上你必须将 bar()
移动到 foo.c
并将其声明为 static
。如果您希望将文件分开,一个丑陋的 hack 是从 foo.c
到 #include
(而不是编译 bar.o
)…
使用标准 C 范围之外的工具,您可以在 linking 时或之后从库中删除 public 导出。下面显示了一些 linker 解决方案,使用 GCC 和 clang(在您可以修改代码的情况下),您可以通过在函数前面加上 non-standard 属性来隐藏函数:__attribute__ ((visibility ("hidden")))
– compilation-unit-wide 等同于编译时的选项 -fvisibility=hidden
,例如 bar.c
.
C 解决方法
如果您可以自由编辑 C 代码,标准 C 中的解决方法是在 bar.c
中创建 bar()
static
并提供指向它的函数指针以供 foo()
通过某种方式,例如,导出指向包含函数指针(和任何其他“私有”数据)的 struct
的指针,并且不要在 struct
外部公开细节私有 header 仅供您的图书馆使用。
例如:
在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); /* … */ }
在此解决方案中,将有一个名为 bar_functions
的导出指针,但不会通过此导出显示所指向的 data/functions 的详细信息。如果无法访问 bar.h
,库的用户将不得不对内容进行逆向工程以正确调用“私有”函数。在多个“私有”函数的情况下,这种方法还可以将它们压缩为单个导出指针,从而消除导出列表中的混乱。
链接器解决方案
探索特定的 link 用户,我找到了一种从动态库中排除特定符号的方法:
使用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
),即使两者在各自的外部可见性相同源(和 object)文件。
测试代码,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
(link 在删除 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
函数只能有内部或外部链接。
如果你想使用不同的模块,你的函数需要有外部链接,这样它们就可以从一个翻译单元调用到另一个。
您可以使用指向静态函数的外部函数指针,当然这仍然允许其他模块通过函数指针调用该函数。
例如,如果我有两个文件 foo.c
和 bar.o
,并且 foo.c
包含一个函数 foo()
,它引用 bar()
中的一个函数 bar.o
:
int foo(int x) { x = bar(x); /* ... */ }
如何编译公开 foo()
但不公开 bar()
的静态或动态库?换句话说,我希望 bar()
仅在库内链接。
使用标准 C,您只能导出或不导出函数,没有“仅导出到这些文件”选项。所以基本上你必须将 bar()
移动到 foo.c
并将其声明为 static
。如果您希望将文件分开,一个丑陋的 hack 是从 foo.c
到 #include
(而不是编译 bar.o
)…
使用标准 C 范围之外的工具,您可以在 linking 时或之后从库中删除 public 导出。下面显示了一些 linker 解决方案,使用 GCC 和 clang(在您可以修改代码的情况下),您可以通过在函数前面加上 non-standard 属性来隐藏函数:__attribute__ ((visibility ("hidden")))
– compilation-unit-wide 等同于编译时的选项 -fvisibility=hidden
,例如 bar.c
.
C 解决方法
如果您可以自由编辑 C 代码,标准 C 中的解决方法是在 bar.c
中创建 bar()
static
并提供指向它的函数指针以供 foo()
通过某种方式,例如,导出指向包含函数指针(和任何其他“私有”数据)的 struct
的指针,并且不要在 struct
外部公开细节私有 header 仅供您的图书馆使用。
例如:
在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); /* … */ }
在此解决方案中,将有一个名为 bar_functions
的导出指针,但不会通过此导出显示所指向的 data/functions 的详细信息。如果无法访问 bar.h
,库的用户将不得不对内容进行逆向工程以正确调用“私有”函数。在多个“私有”函数的情况下,这种方法还可以将它们压缩为单个导出指针,从而消除导出列表中的混乱。
链接器解决方案
探索特定的 link 用户,我找到了一种从动态库中排除特定符号的方法:
使用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
),即使两者在各自的外部可见性相同源(和 object)文件。
测试代码,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
(link 在删除 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
函数只能有内部或外部链接。
如果你想使用不同的模块,你的函数需要有外部链接,这样它们就可以从一个翻译单元调用到另一个。
您可以使用指向静态函数的外部函数指针,当然这仍然允许其他模块通过函数指针调用该函数。