C 我是否可以使某些符号仅对其他库成员可见?
考虑3个C源文件:C 我是否可以使某些符号仅对其他库成员可见?,c,linker,shared-libraries,static-libraries,symbols,C,Linker,Shared Libraries,Static Libraries,Symbols,考虑3个C源文件: /* widgets.c */ void widgetTwiddle ( struct widget * w ) { utilityTwiddle(&w->bits, 1); } 及 及 它们被编译并放入库中(比如libww.a或libww.so)。 有没有办法使utilityTwiddle()对其他两个库成员可见并可用,但对链接到库的人不可见?也就是说,鉴于此: /* appl.c */ extern void utilityTwiddle ( in
/* widgets.c */
void widgetTwiddle ( struct widget * w ) {
utilityTwiddle(&w->bits, 1);
}
及
及
它们被编译并放入库中(比如libww.a或libww.so)。
有没有办法使utilityTwiddle()
对其他两个库成员可见并可用,但对链接到库的人不可见?也就是说,鉴于此:
/* appl.c */
extern void utilityTwiddle ( int * bitsPtr, int bits );
int main ( void ) {
int bits;
utilityTwiddle(&bits, 1);
return 0;
}
及
它将无法链接,因为utilityTwiddle()
对appl.c
不可见。因此,appl.c
可以自由定义自己的utilityTwiddle
函数或变量
[编辑]很显然,我们希望这样做能够奏效:
/* workingappl.c */
extern void wombatTwiddle ( struct wombat * wPtr );
int main ( void ) {
struct wombat w = { .bits = 0 };
wombatTwiddle(&w);
return 0;
}
这似乎是相关的,但似乎并没有说明被抑制的符号是否可供其他库成员使用
[EDIT2]我找到了一种不用修改C源代码的方法。添加映射文件:
/* utility.map */
{ local: *; };
然后做:
$ gcc -shared -o utility.so utility.c -fPIC -Wl,--version-script=utility.map
为我们提供了一个动态符号表,其中不包含实用程序twidle
:
$ nm -D utility.so
w _Jv_RegisterClasses
w __cxa_finalize
w __gmon_start__
但我不清楚如何有效地从这一点出发,构建一个包含所有三个源文件的共享库。如果将所有三个源文件都放在命令行上,则所有三个源文件中的符号都将隐藏。如果有一种增量构建共享库的方法,我可以有两个简单的映射文件(一个不导出任何内容,一个导出所有内容)。这是可行的还是唯一的选择
/* libww.map */
{ global: list; of; all; symbols; to; export; local: *; };
及
[EDIT3]
天啊,看来这也应该是可能的,不用使用共享库。如果我这样做:
ld -r -o wboth.o widgets.o wombats.o utility.o
我可以看到链接器已解析为utilityTwiddle()
的位置,其中widgetWiddle()
和wombatTwiddle()
调用它:
$ objdump -d wboth.o
0000000000000000 <widgetTwiddle>:
0: be 01 00 00 00 mov $0x1,%esi
5: e9 00 00 00 00 jmpq a <widgetTwiddle+0xa>
0000000000000010 <wombatTwiddle>:
10: be 01 00 00 00 mov $0x1,%esi
15: e9 00 00 00 00 jmpq 1a <wombatTwiddle+0xa>
0000000000000020 <utilityTwiddle>:
20: 31 37 xor %esi,(%rdi)
22: c3 retq
因此,如果您能够找到删除该符号的方法,您仍然可以成功地链接到wboth.o
(我已经通过二进制编辑wboth.o对此进行了测试),并且它仍然链接并运行良好:
$ nm wboth.o
U _GLOBAL_OFFSET_TABLE_
0000000000000000 T widgetTwiddle
0000000000000010 T wombatTwiddle
0000000000000020 T xtilityTwiddle
通过创建静态库
libww.a
,您无法实现所需的功能。如果你
听到了吗
我会明白原因的。静态库可用于提供大量对象文件
到链接器,它将从中提取所需的k(可能=0)并链接它们。那么你呢
无法通过链接静态库实现您无法实现的任何功能
直接链接那些k对象文件。出于链接的目的,静态库实际上并不
存在
但共享库确实是为了链接和共享库公开的全局符号而存在的
获取一个额外的属性dynamic visibility,该属性正是为您的应用程序而存在的
目的。动态可见符号是全局符号的子集:它们是
动态链接可见的全局符号,即用于链接共享库的全局符号
与其他东西(一个程序或另一个共享库)一起使用
动态可见性不是源语言标准所说的一个属性
关于,因为他们没有说任何关于动态链接的内容。那么控制
符号的动态可见性必须通过工具链以单独的方式完成
不支持动态链接。GCC使用特定于编译器的声明来实现
限定符1:
和/或编译器开关2:
下面是一个演示如何构建libww.so
,从而隐藏utilityTwiddle
当wombatTwiddle
和widgetTwiddle
时,库的客户端可见
您的源代码需要以这样或那样的方式进行充实才能编译。
这里是第一个剪辑:
ww.h(1)
utility.h(1)
utility.c
#include "utility.h"
void utilityTwiddle ( int * bitsPtr, int bits ) {
*bitsPtr ^= bits;
}
#include "utility.h"
#include "ww.h"
void wombatTwiddle ( struct wombat * w ) {
utilityTwiddle(&w->bits, 1);
}
#include "utility.h"
#include "ww.h"
void widgetTwiddle ( struct widget * w ) {
utilityTwiddle(&w->bits, 1);
}
#include <ww.h>
extern void utilityTwiddle ( int * bitsPtr, int bits );
int main()
{
struct widget wi = {1};
struct wombat wo = {2};
widgetTwiddle(&wi);
wombatTwiddle(&wo);
utilityTwiddle(&wi.bits,wi.bits);
return 0;
}
wombats.c
#include "utility.h"
void utilityTwiddle ( int * bitsPtr, int bits ) {
*bitsPtr ^= bits;
}
#include "utility.h"
#include "ww.h"
void wombatTwiddle ( struct wombat * w ) {
utilityTwiddle(&w->bits, 1);
}
#include "utility.h"
#include "ww.h"
void widgetTwiddle ( struct widget * w ) {
utilityTwiddle(&w->bits, 1);
}
#include <ww.h>
extern void utilityTwiddle ( int * bitsPtr, int bits );
int main()
{
struct widget wi = {1};
struct wombat wo = {2};
widgetTwiddle(&wi);
wombatTwiddle(&wo);
utilityTwiddle(&wi.bits,wi.bits);
return 0;
}
widgets.c
#include "utility.h"
void utilityTwiddle ( int * bitsPtr, int bits ) {
*bitsPtr ^= bits;
}
#include "utility.h"
#include "ww.h"
void wombatTwiddle ( struct wombat * w ) {
utilityTwiddle(&w->bits, 1);
}
#include "utility.h"
#include "ww.h"
void widgetTwiddle ( struct widget * w ) {
utilityTwiddle(&w->bits, 1);
}
#include <ww.h>
extern void utilityTwiddle ( int * bitsPtr, int bits );
int main()
{
struct widget wi = {1};
struct wombat wo = {2};
widgetTwiddle(&wi);
wombatTwiddle(&wo);
utilityTwiddle(&wi.bits,wi.bits);
return 0;
}
以默认方式将所有*.c
文件编译为*.o
文件:
$ gcc -Wall -Wextra -c widgets.c wombats.c utility.c
$ gcc -shared -o libww.so widgets.o wombats.o utility.o
并以默认方式将它们链接到libww.so
:
$ gcc -Wall -Wextra -c widgets.c wombats.c utility.c
$ gcc -shared -o libww.so widgets.o wombats.o utility.o
以下是libww的全局符号表中的*twidle
符号。因此
$ nm libww.so | egrep '*Twiddle'
000000000000063a T utilityTwiddle
00000000000005fa T widgetTwiddle
000000000000061a T wombatTwiddle
这只是进入链接的全局(extern
)*twidle
符号的总和
来自对象文件的libww.so
。它们都是被定义的(T
),就像它们必须被定义的那样
如果要链接库本身,而不需要外部*twidle
依赖项
任何ELF文件(对象文件、共享库、程序)都有一个全局符号表,但
共享库还有一个动态符号表。以下是libww的动态符号表中的*twidle
符号。因此
:
$ nm -D libww.so | egrep '*Twiddle'
000000000000063a T utilityTwiddle
00000000000005fa T widgetTwiddle
000000000000061a T wombatTwiddle
他们完全一样。这就是我们想要改变的,因此utilityTwiddle
消失了
这是第二个切口。我们必须稍微修改一下源代码
实用程序.h(2)
然后重新编译并重新链接,与前面一样:
$ gcc -Wall -Wextra -c widgets.c wombats.c utility.c
$ gcc -shared -o libww.so widgets.o wombats.o utility.o
以下是全局符号表中的*twidle
符号:
$ nm libww.so | egrep '*Twiddle'
000000000000063a T utilityTwiddle
00000000000005fa T widgetTwiddle
000000000000061a T wombatTwiddle
$ nm -D libww.so | egrep '*Twiddle'
00000000000005aa T widgetTwiddle
00000000000005ca T wombatTwiddle
$ nm libww.so | egrep '*Twiddle'
00000000000005ea t utilityTwiddle
00000000000005aa T widgetTwiddle
00000000000005ca T wombatTwiddle
$ nm -D libww.so | egrep '*Twiddle'
00000000000005aa T widgetTwiddle
00000000000005ca T wombatTwiddle
没有变化。下面是动态符号表中的*twidle
符号:
$ nm libww.so | egrep '*Twiddle'
000000000000063a T utilityTwiddle
00000000000005fa T widgetTwiddle
000000000000061a T wombatTwiddle
$ nm -D libww.so | egrep '*Twiddle'
00000000000005aa T widgetTwiddle
00000000000005ca T wombatTwiddle
$ nm libww.so | egrep '*Twiddle'
00000000000005ea t utilityTwiddle
00000000000005aa T widgetTwiddle
00000000000005ca T wombatTwiddle
$ nm -D libww.so | egrep '*Twiddle'
00000000000005aa T widgetTwiddle
00000000000005ca T wombatTwiddle
utilityTwiddle
不见了
这是第三个切割,以不同的方式获得相同的结果。更冗长
但说明了-fvisibility
编译器选项的作用。这一次,,
utility.h
再次符合(1),但ww.h
是:
ww.h(2)
现在我们像这样重新编译:
$ gcc -Wall -Wextra -fvisibility=hidden -c widgets.c wombats.c utility.c
我们告诉编译器注释它生成的所有全局符号
\uuuuu属性(可见性(“隐藏”))
,除非存在反补贴
\uuuu属性(可见性(“…”))
在源代码中显式显示
然后像以前一样重新链接共享库。我们在全局符号表中再次看到:
$ nm libww.so | egrep '*Twiddle'
000000000000063a T utilityTwiddle
00000000000005fa T widgetTwiddle
000000000000061a T wombatTwiddle
$ nm -D libww.so | egrep '*Twiddle'
00000000000005aa T widgetTwiddle
00000000000005ca T wombatTwiddle
$ nm libww.so | egrep '*Twiddle'
00000000000005ea t utilityTwiddle
00000000000005aa T widgetTwiddle
00000000000005ca T wombatTwiddle
$ nm -D libww.so | egrep '*Twiddle'
00000000000005aa T widgetTwiddle
00000000000005ca T wombatTwiddle
在动态符号表中:
$ nm libww.so | egrep '*Twiddle'
000000000000063a T utilityTwiddle
00000000000005fa T widgetTwiddle
000000000000061a T wombatTwiddle
$ nm -D libww.so | egrep '*Twiddle'
00000000000005aa T widgetTwiddle
00000000000005ca T wombatTwiddle
$ nm libww.so | egrep '*Twiddle'
00000000000005ea t utilityTwiddle
00000000000005aa T widgetTwiddle
00000000000005ca T wombatTwiddle
$ nm -D libww.so | egrep '*Twiddle'
00000000000005aa T widgetTwiddle
00000000000005ca T wombatTwiddle
最后,显示从动态符号表中删除utilityTwiddle
所以在这些方法中的一种确实对链接到的客户端隐藏了它
libww.so
。这是