考虑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
函数或变量。[编辑] 显然,希望我们可以这样做:
/* 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
最佳答案
通过创建静态库libww.a
,无法实现所需的功能。如果你
读static-libraries你
会明白为什么。静态库可用于提供N个目标文件
链接器,它将从中提取所需的k(可能= 0)并将其链接。那么你
通过链接无法通过以下方式实现的静态库无法实现任何目标
直接链接那k个目标文件。出于链接目的,静态库实际上并没有
存在。
但是共享库确实确实存在用于链接的目的,并且共享库公开了全局符号
获得一个额外的属性,即动态能见度,该属性恰好适合您
目的。动态可见符号是全局符号的子集:
动态链接(即链接共享库)可见的全局符号
与其他东西(一个程序或另一个共享库)。
动态可见性不是源语言标准可以说明任何问题的属性
关于,因为他们没有说动态链接。所以控制
符号的动态可见性必须通过一种单独的工具链来完成
支持动态链接。 GCC使用特定于编译器的声明来完成此操作
限定词1:
__attribute__((visibility("default|hidden|protected|internal")
和/或编译器switch2:
-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文件(目标文件,共享库,程序)都具有全局符号表,但是
共享库还具有动态符号表。这是
*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
那里没有变化。现在是动态符号表中的
*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("...")))
相同编译器具有的源代码中的声明的限定符
通过将链接信息注入到它生成的目标文件中来兑现。如果
您希望看到可以重复进行最后编译的证据
并要求保存程序集文件:
$ gcc -Wall -Wextra -fvisibility=hidden -c widgets.c wombats.c utility.c -save-temps
然后比较说:
widgets.s
.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:
...
...
[1]参见GCC manual:
[2]参见GCC Manual, 3.16 Options for Code Generation Conventions。
关于c - 我可以使某些符号仅对其他图书馆成员可见吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52798143/