【问题标题】:Hide symbols from a 3rd party .a file that is linked into a .so file对链接到 .so 文件的第 3 方 .a 文件隐藏符号
【发布时间】:2020-08-19 05:23:10
【问题描述】:

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

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

有什么方法可以在事后将这些符号标记为“隐藏”,这样它们就不会出现在我的 .so 文件的符号列表中?我查看了objdumpobjcopy,但我对这些术语感到困惑。

我尝试过的其他事情:

【问题讨论】:

  • 您如何精确地编译最终的.so 文件?编译后你通常想strip它。
  • 是的,我运行 strip,但它不会删除动态符号表中的符号。至于我们如何构建 .so 文件,它隐藏在 CMake 的几层之下。 g++ / gcc 在编译单个文件时肯定有 -fvisibility=hidden 。不确定 .so 文件是否需要任何东西,但它似乎适用于我编译的代码。只是这个第三方库让我很难过。

标签: c symbols binutils .so .a


【解决方案1】:

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

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

$ 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的符号表是:-

$ 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

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

$ 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 可见性符号在动态中不存在 符号表,但包含来自 libtp.aDEFAULT 可见性符号, 你不想要的。

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

$ 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-libsdocumented

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

指定不应自动导出符号的存档库列表。 库名称可以用逗号或冒号分隔。指定 --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,无法进一步链接。 (您在grep 中看到两次usc,因为它被列为全局符号和动态符号)。

正如您可以从中推断的那样,我们不必费心编译我们的 拥有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。

【讨论】:

  • 精心制作的响应,完美运行。我需要 --exclude-libs 语句。谢谢!
猜你喜欢
  • 2018-09-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-15
相关资源
最近更新 更多