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

考虑3个C源文件:

/* 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
。这是