考虑3 C源文件:
/* widgets.c */
void widgetTwiddle ( struct widget * w ) {
utilityTwiddle(&w->bits, 1);
}
和
/* wombats.c */
void wombatTwiddle ( struct wombat * w ) {
utilityTwiddle(&w->bits, 1);
}
和
/* utility.c */
void utilityTwiddle ( int * bitsPtr, int bits ) {
*bitsPtr ^= bits;
}
将其编译并放入库中(例如,libww.a或libww.so)。
有没有一种方法可以使utilityTwiddle()
对其他两个库成员可见并可用,但是对链接到该库的人不可见?也就是说,鉴于此:
/* appl.c */
extern void utilityTwiddle ( int * bitsPtr, int bits );
int main ( void ) {
int bits;
utilityTwiddle(&bits, 1);
return 0;
}
和
cc -o appl appl.c -lww
由于utilityTwiddle()
对appl.c
不可见,因此链接失败。因此,appl.c
可以自由定义自己的utilityTwiddle
函数或变量。
[EDIT] 显然,我们希望此方法能够起作用:
/* workingappl.c */
extern void wombatTwiddle ( struct wombat * wPtr );
int main ( void ) {
struct wombat w = { .bits = 0 };
wombatTwiddle(&w);
return 0;
}
这个Limiting visibility of symbols when linking shared libraries似乎相关,但似乎没有解决被抑制的符号是否可用于其他库成员的情况。
[EDIT2] 我已经找到一种无需修改C源代码即可完成此操作的方法。添加地图文件:
/* utility.map */
{ local: *; };
然后执行:
$ gcc -shared -o utility.so utility.c -fPIC -Wl,--version-script=utility.map
为我们提供了不带utilityTwiddle
的动态符号表:
$ nm -D utility.so
w _Jv_RegisterClasses
w __cxa_finalize
w __gmon_start__
但是我不清楚如何有效地从这开始构建包含所有三个源文件的共享库。如果我将所有三个源文件放在命令行上,则这三个文件中的符号都将被隐藏。如果有一种增量构建共享库的方法,我可以有两个简单的映射文件(一个不导出任何内容,一个不导出所有内容)。这是可行的还是唯一的选择是这样的:
/* libww.map */
{ global: list; of; all; symbols; to; export; local: *; };
和
$ gcc -shared -o libww.so *.c -fPIC -Wl,--version-script=libww.map
[EDIT3] 伙计,似乎在不使用共享库的情况下也应该可以实现。如果我这样做:
ld -r -o wboth.o widgets.o wombats.o utility.o
我可以看到链接器已解析到utilityTwiddle()
和widgetTwiddle()
称为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
但是utilityTwiddle
仍然是符号:
$ nm wboth.o
U _GLOBAL_OFFSET_TABLE_
0000000000000020 T utilityTwiddle
0000000000000000 T widgetTwiddle
0000000000000010 T wombatTwiddle
,因此,如果您找到删除该符号的方法,则仍然可以成功地与wboth.o
链接(我已经通过wboth.o的二进制编辑进行了测试),并且它仍然可以正常运行:
$ nm wboth.o
U _GLOBAL_OFFSET_TABLE_
0000000000000000 T widgetTwiddle
0000000000000010 T wombatTwiddle
0000000000000020 T xtilityTwiddle
答案 0 :(得分:1)
通过创建静态库libww.a
无法实现所需的目标。如果你
阅读static-libraries您
会明白为什么。静态库可用于提供一堆 N 个目标文件
链接器,它将从中提取所需的 k (可能= 0)并链接它们。那么你
与静态库链接无法实现任何目标
直接链接那些 k 对象文件。出于链接目的,静态库实际上并没有
存在。
但是 shared 库确实确实存在用于链接目的,并且共享库公开了全局符号 获得另外一个属性,动态可见性,该属性正是为您 目的。动态可见的符号是全局符号的子集:它们是 对于 dynamic 链接(即链接共享库)可见的全局符号 以及其他内容(程序或其他共享库)。
动态可见性不是源语言标准可以说明任何问题的属性 关于,因为他们没有说动态链接。所以控制 符号的动态可见性必须通过工具链以单独的方式完成 支持动态链接。 GCC使用特定于编译器的声明来完成此操作 限定词 1 :
__attribute__((visibility("default|hidden|protected|internal")
和/或编译器开关 2 :
-fvisibility=default|hidden|protected|internal
这里是一个演示如何构建libww.so
的示例,以便utilityTwiddle
被隐藏
wombatTwiddle
和widgetTwiddle
可见的库客户端。
您的源代码需要以一种或另一种方式充实才能进行编译。 这是第一个剪辑:
ww.h(1)
#ifndef WW_H
#define WW_H
struct widget {
int bits;
};
struct wombat {
int bits;
};
extern void widgetTwiddle ( struct widget * w );
extern void wombatTwiddle ( struct wombat * w );
#endif
utility.h(1)
#ifndef UTILITY_H
#define UTILITY_H
extern void utilityTwiddle ( int * bitsPtr, int bits );
#endif
utility.c
#include "utility.h"
void utilityTwiddle ( int * bitsPtr, int bits ) {
*bitsPtr ^= bits;
}
wombats.c
#include "utility.h"
#include "ww.h"
void wombatTwiddle ( struct wombat * w ) {
utilityTwiddle(&w->bits, 1);
}
widgets.c
#include "utility.h"
#include "ww.h"
void widgetTwiddle ( struct widget * w ) {
utilityTwiddle(&w->bits, 1);
}
以默认方式将所有*.c
文件编译为*.o
文件:
$ gcc -Wall -Wextra -c widgets.c wombats.c utility.c
并以默认方式将其链接到libww.so
:
$ gcc -shared -o libww.so widgets.o wombats.o utility.o
*Twiddle
的全局符号表中有libww.so
个符号
$ nm libww.so | egrep '*Twiddle'
000000000000063a T utilityTwiddle
00000000000005fa T widgetTwiddle
000000000000061a T wombatTwiddle
这只是进入链接的全局(extern
)*Twiddle
符号的总和
目标文件中libww.so
的值。它们都是定义的(T
),因为它们必须是
如果要链接库本身而没有外部*Twiddle
依赖项。
任何ELF文件(目标文件,共享库,程序)都有全局符号表,但是
共享库还具有 dynamic 符号表。这是*Twiddle
的动态符号表中的libww.so
符号:
$ nm -D libww.so | egrep '*Twiddle'
000000000000063a T utilityTwiddle
00000000000005fa T widgetTwiddle
000000000000061a T wombatTwiddle
它们是完全一样的。这就是我们要更改的内容,因此utilityTwiddle
消失。
这是第二次切。我们必须稍微更改源代码。
utility.h(2)
#ifndef UTILITY_H
#define UTILITY_H
extern void utilityTwiddle ( int * bitsPtr, int bits ) __attribute__((visibility("hidden")));
#endif
然后像以前一样重新编译并重新链接:
$ gcc -Wall -Wextra -c widgets.c wombats.c utility.c
$ gcc -shared -o libww.so widgets.o wombats.o utility.o
这是全局符号表中的*Twiddle
符号:
$ nm libww.so | egrep '*Twiddle'
000000000000063a T utilityTwiddle
00000000000005fa T widgetTwiddle
000000000000061a T wombatTwiddle
没有变化。这是 dynamic 符号表中的*Twiddle
符号:
$ nm -D libww.so | egrep '*Twiddle'
00000000000005aa T widgetTwiddle
00000000000005ca T wombatTwiddle
utilityTwiddle
不见了。
这是第三次切割,以不同的方式获得相同的结果。更long
但说明了-fvisibility
编译器选项的播放方式。这次,
utility.h
仍然是(1),但是ww.h
是:
ww.h(2)
#ifndef WW_H
#define WW_H
struct widget {
int bits;
};
struct wombat {
int bits;
};
extern void widgetTwiddle ( struct widget * w ) __attribute__((visibility("default")));
extern void wombatTwiddle ( struct wombat * w ) __attribute__((visibility("default")));
#endif
现在,我们像这样重新编译:
$ gcc -Wall -Wextra -fvisibility=hidden -c widgets.c wombats.c utility.c
我们告诉编译器注释其生成的每个全局符号
__attribute__((visibility("hidden")))
,除非有反补贴
__attribute__((visibility("...")))
在源代码中明确显示。
然后像以前一样重新链接共享库。我们再次在全局符号表中看到:
$ 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
的一种确实确实将其隐藏在与
libww.so
。这是一个要调用所有*Twiddle
的程序:
程序
#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;
}
我们可以像这样构建它
$ gcc -Wall -Wextra -I. -c prog.c
$ gcc -o prog prog.o utility.o widgets.o wombats.o
但是没有人可以像这样构建它:
$ gcc -Wall -Wextra -I. -c prog.c
$ gcc -o prog prog.o -L. -lww
prog.o: In function `main':
prog.c:(.text+0x4a): undefined reference to `utilityTwiddle'
collect2: error: ld returned 1 exit status
请注意,-fvisibility
是编译选项,而不是链接选项。
您将其传递给编译命令而不是链接命令,
因为它的效果与撒__attribute__((visibility("...")))
一样
您的源代码中声明的限定符, compiler 具有
通过将链接信息注入到它生成的对象文件中来兑现。如果
您希望看到可以重复进行最后编译的证据
并要求保存程序集文件:
$ gcc -Wall -Wextra -fvisibility=hidden -c widgets.c wombats.c utility.c -save-temps
然后比较说:
小工具
.file "widgets.c"
.text
.globl widgetTwiddle
.type widgetTwiddle, @function
widgetTwiddle:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movq %rdi, -8(%rbp)
movq -8(%rbp), %rax
movl $1, %esi
movq %rax, %rdi
call utilityTwiddle@PLT
nop
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size widgetTwiddle, .-widgetTwiddle
.ident "GCC: (Ubuntu 7.3.0-16ubuntu3) 7.3.0"
.section .note.GNU-stack,"",@progbits
具有:
实用程序。
.file "utility.c"
.text
.globl utilityTwiddle
.hidden utilityTwiddle
^^^^^^^^^^^^^^^^^^^^^^
.type utilityTwiddle, @function
utilityTwiddle:
...
...
[2]参见GCC Manual, 3.16 Options for Code Generation Conventions。