隐藏来自第 3 方的符号 .a 链接到 .so 文件的文件

Hide symbols from a 3rd party .a file that is linked into a .so file

我正在构建一个共享 (.so) 库,它由几个 .a 文件和一个调用它们的薄 API 层组成。我只希望我的 API 和外部依赖项可见,因此我使用 GCC (-fvisibility=hidden) 提供的 "hidden" 可见性构建我的代码 (-fvisibility=hidden)。

但是,其中一个库是专有的第三方 .a 文件(我们已付费使用),我只能访问其二进制文件。当我 link 它静态地进入我的 .so 文件时,它的符号在我的 .so 的动态符号 table 中是可见的。我猜这是因为库不是用隐藏的可见性选项构建的。我宁愿隐藏这些功能作为管理我们软件的敏感部分,我不希望第三方 link 使用这些符号。

有没有什么办法可以在事后将这些符号标记为 "hidden",这样它们就不会出现在我的 .so 文件的符号列表中?我看过 objdumpobjcopy,但我很难理解这些术语。

我尝试过的其他事情:

这是一个解决问题的示例。

这是您无法重新编译的专有静态库的源代码:

$ cat tpa.c
int tpa(void)
{
    return 2;
}
$ cat tpb.c
int tpb(void)
{
    return 3;
}

图书馆,libtp.a,一定是这样建造的1

$ gcc -fPIC -c -O1 tpa.c tpb.c
$ ar rcs libtp.a tpa.o tpb.o

tpa.otpb.o的符号table是:-

$ readelf -s libtp.a

File: libtp.a(tpa.o)

Symbol table '.symtab' contains 10 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS tpa.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    2 
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 
     5: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 
     6: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    7 
     8: 0000000000000000     0 SECTION LOCAL  DEFAULT    4 
     9: 0000000000000000    10 FUNC    GLOBAL DEFAULT    1 tpa

File: libtp.a(tpb.o)

Symbol table '.symtab' contains 10 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS tpb.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    2 
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 
     5: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 
     6: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    7 
     8: 0000000000000000     0 SECTION LOCAL  DEFAULT    4 
     9: 0000000000000000    10 FUNC    GLOBAL DEFAULT    1 tpb

你看到函数符号 tpatpb 都是 GLOBAL (= 适用于 link 年龄)并具有 DEFAULT 动态可见性,而不是 HIDDEN

下面是您自己的静态库的源代码,libus.a

$ cat usa.c
int usa(void)
{
    return 5;
}
$ cat usb.c
int usb(void)
{
    return 7;
}

你这样构建的:

$ gcc -fPIC -c -O1 -fvisibility=hidden usa.c usb.c
$ ar rcs libus.a usa.o usb.o

libus.a中的函数符号也是GLOBAL,但它们是动态的 能见度为 HIDDEN:-

$ readelf -s libus.a

File: libus.a(usa.o)

Symbol table '.symtab' contains 10 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS usa.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    2 
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 
     5: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 
     6: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    7 
     8: 0000000000000000     0 SECTION LOCAL  DEFAULT    4 
     9: 0000000000000000    10 FUNC    GLOBAL HIDDEN     1 usa

File: libus.a(usb.o)

Symbol table '.symtab' contains 10 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS usb.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    2 
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 
     5: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 
     6: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    7 
     8: 0000000000000000     0 SECTION LOCAL  DEFAULT    4 
     9: 0000000000000000    10 FUNC    GLOBAL HIDDEN     1 usb

这是您的共享库的源代码:

$ cat usc.c
extern int tpa(void);
extern int tpb(void);
extern int usa(void);
extern int usb(void);

int usc(void)
{
    return tpa() * tpb() * usa() * usb();
}

你编译的是:-

$ gcc -fPIC -c -O1 usc.c

现在您想要 link usc.olibtp.alibus.a 在您的共享库 libsus.so 中。如果你 照常做:

$ gcc -shared -o libsus.so usc.o -L. -ltp -lus

然后你发现:

$ readelf --dyn-syms libsus.so

Symbol table '.dynsym' contains 8 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __cxa_finalize
     2: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
     3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
     4: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     5: 0000000000001139    38 FUNC    GLOBAL DEFAULT   12 usc
     6: 000000000000115f    10 FUNC    GLOBAL DEFAULT   12 tpa
     7: 0000000000001169    10 FUNC    GLOBAL DEFAULT   12 tpb

来自 libus.aHIDDEN 可见性符号在动态中不存在 符号 table,但包含来自 libtp.aDEFAULT 个可见性符号, 你不想要的。

要排除后者,link您的共享库如下:

$ gcc -shared -o libsus.so usc.o -L. -ltp -lus -Wl,--exclude-libs=libtp.a

则动态符号table变为:

$ readelf --dyn-syms libsus.so

Symbol table '.dynsym' contains 6 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __cxa_finalize
     2: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
     3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
     4: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     5: 00000000000010f9    38 FUNC    GLOBAL DEFAULT   10 usc

随心所欲。

linker 选项 --exclude-libsdocumented:

--exclude-libs lib,lib,...

Specifies a list of archive libraries from which symbols should not be automatically exported. The library names may be delimited by commas or colons. Specifying --exclude-libs ALL excludes symbols in all archive libraries from automatic export. ... For ELF targeted ports, symbols affected by this option will be treated as hidden.

为了确保 tp* 符号定义 已经 link 编辑,您 仍然可以在共享库的完整符号 table 中看到它们:

$ readelf -s libsus.so | egrep 'FUNC.*(us|tp)(a|b|c)' 
     5: 00000000000010f9    38 FUNC    GLOBAL DEFAULT   10 usc
    41: 0000000000001133    10 FUNC    LOCAL  DEFAULT   10 usa
    44: 000000000000111f    10 FUNC    LOCAL  DEFAULT   10 tpa
    46: 000000000000113d    10 FUNC    LOCAL  DEFAULT   10 usb
    48: 0000000000001129    10 FUNC    LOCAL  DEFAULT   10 tpb
    50: 00000000000010f9    38 FUNC    GLOBAL DEFAULT   10 usc

就像显式隐藏的 us* 符号一样,它们变成 LOCAL,无法再使用 linkage。 (您在 grep 中看到两次 usc,因为它被列为全局符号和动态符号)。

从这里可以推断,我们不必费心去编译我们的 拥有 us* 代码和 -fvisibility=hidden,只要我们要存档 它在 libus.a 进一步 link 年龄。我们可以像这样 link 编辑共享库:

$ gcc -shared -o libsus.so usc.o -L. -ltp -lus -Wl,--exclude-libs=libtp.a,libus.a

效果相同


[1] 我明确指定 -fPIC 以确保生成位置无关的 我可以在 DSO 中 link 的目标代码,但这一直是 GCC 的默认设置 海湾合作委员会 6.