首页 > 解决方案 > 对链接到 .so 文件的第 3 方 .a 文件隐藏符号

问题描述

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

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

有什么方法可以在事后将这些符号标记为“隐藏”,以便它们不会出现在我的 .so 文件的符号列表中?我已经看过了objdumpobjcopy但是我很难使用这些术语。

我尝试过的其他事情:

标签: csymbolsbinutils.so.a

解决方案


这是一个如何解决您的问题的工作示例。

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

$ 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.o和的符号表tpb.o是:-

$ 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 (= 可用于链接)并且具有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

现在你要链接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

动态符号表中不存在可见性符号,但包含了可见HIDDEN性符号,这是您不想要的。libus.aDEFAULTlibtp.a

要排除后者,请按如下方式链接您的共享库:

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

那么动态符号表就变成了:

$ 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

如你所愿。

--exclude-libs链接器选项记录在案

--exclude-libs 库,库,...

指定不应自动从中导出符号的存档库列表。库名称可以用逗号或冒号分隔。指定 --exclude-libs ALL 会从自动导出中排除所有存档库中的符号。... 对于 ELF 目标端口,受此选项影响的符号将被视为隐藏。

为了确保tp*符号定义被链接,您仍然可以在共享库的完整符号表中看到它们:

$ 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,不可用于进一步链接。(您在 中看到usc两次,grep因为它被列为全局符号和动态符号)。

正如您可以从中推断的那样,我们不必费心用 编译我们自己的us*代码-fvisibility=hidden,只要我们将其存档以libus.a进行进一步的链接。我们可以像这样链接共享库:

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

具有相同的效果。


[1] 我-fPIC明确指定以确保生成可以在 DSO 中链接的与位置无关的目标代码,但这是自 GCC 6 以来的 GCC 默认设置。


推荐阅读