为什么ELF中其他共享库的函数长度信息?

时间:2012-11-29 14:47:51

标签: c++ linux elf powerpc

我们的项目(C ++,Linux,gcc,PowerPC)由几个共享库组成。在发布新版本的软件包时,只有那些库应该更改其源代码实际受到影响。 “更改”是指绝对二进制身份(对文件的校验和进行比较。不同的校验和 - >根据策略的不同版本)。 (我应该提一下,无论每个库是否有任何代码更改,整个项目总是立即生成。)

通常可以通过隐藏包含的Header文件的私有部分而不是更改公共文件来实现。

然而,有一种情况是,仅仅delete被添加到库libTableManager.so的类TableManager(在TableManager.cpp文件中!)的析构函数中,而且库libB的二进制/校验和.so(使用类TableManager)已更改

TableManager.h:

class TableManager 
{
public:
    TableManager();
    ~TableManager();
private:
    int* myPtr;
}

TableManager.cpp:

TableManager::~TableManager()
{
    doSomeCleanup();
    delete myPtr;     // this delete has been added
}

通过使用readelf --all libB.so检查libB.so,查看.dynsym部分,结果发现所有函数的 length ,甚至是其他库中动态使用的函数都存储了在libB中!它看起来像这样(长度是第3列中的668):

527: 00000000 668 FUNC GLOBAL DEFAULT UND _ZN12TableManagerD1Ev

所以我的问题是:

  1. 为什么函数的 length 实际存储在客户端lib中?起始地址不足够吗?
  2. 在编译/链接libB.so(某种“剥离”)时,能否以某种方式抑制它?我们真的希望减少这种程度的依赖......

2 个答案:

答案 0 :(得分:11)

宾果。它实际上是binutils中的一个“bug”,他们在2008年发现并修复了它。大小信息实际上没用;

binutils邮件列表中的Simon Baldwin wrote完全描述了问题(由我强调):

  

目前,未定义的ELF符号的大小将被复制出来   在链接时提供符号的目标文件或DSO。 这个尺寸是   不可靠,例如在两个DSO的情况下,一个链接到   其他。较低级别的DSO可以使ABI保留更改   改变符号大小,没有重要的要求   更高级别的DSO。如果重建更高级别的DSO,那就是工具   监视文件校验和将由于大小的改变而注册更改   未定义的符号,即使没有别的   更高级别的DSO已经改变。这可能导致不必要的和   在基于校验和的系统中不良重建和更改级联。

我们遇到旧系统的问题(binutils 2.16)。我将它与桌面系统上的2.20版进行了比较,并且 - vo - 共享全局符号的长度为0:

157: 00000000     0 FUNC    GLOBAL DEFAULT  UND _ZN12TableManagerD1Ev
158: 00000000     0 FUNC    GLOBAL DEFAULT  UND _ZNSs6assignERKSs@GLIBCXX_3.4 (2)
159: 00000000     0 FUNC    GLOBAL DEFAULT  UND sleep@GLIBC_2.0 (6)
160: 00000000     0 FUNC    GLOBAL DEFAULT  UND _ZN4Gpio11setErrorLEDENS_

所以我比较了两个binutils的源代码,再次 - 瞧瞧 - 还有Alan在邮件列表中提出的解决方案:

enter image description here

也许我们只是应用补丁并重新编译binutils,因为我们需要继续使用旧版平台。谢谢你的耐心等待。

答案 1 :(得分:8)

您需要仔细阅读加载程序的代码,但我认为在这种情况下,我们可以对该长度字段的目标做出相当合理的猜测。

加载器需要获取将要放入进程的所有函数,并将它们映射到内存地址。因此,它为第一个函数提供了一个地址。然后,第二个在第一个结束之后 - 但要知道“第一个结束”,它需要知道第一个函数有多长。

我可以看到它有两种方法可以达到这个长度:它可以在文件中编码(就像你在ELF中看到的那样),或者它可以打开包含该函数的文件,并获得那里的长度。

后者似乎(对我而言)有两个相当明显的缺点。第一个是速度 - 打开所有这些额外的文件,解析它们的标题等,只是为了获得函数的长度几乎肯定比从当前文件中读取每个函数的额外四个字节要慢。第二个是方便:只要你不调用文件中的任何函数,就根本不需要该文件。如果你直接从文件中读取长度(例如,像Windows通常用DLL那样),你需要在目标系统上存在该文件,即使它实际上从未使用过。

编辑:由于有些人显然错过了“意图完成”(显然太过)微妙的含义,让我完全清楚:我有理由相信这个领域实际上并没有(而且从来没有)。< / p> 然而,任何认为使这个答案错误的人都需要回到编程101并了解接口和实现之间的区别。

在这种情况下,文件格式定义了一个接口 - 一组加载器可以使用的功能。在Linux的特定情况下,似乎从未使用过该字段。

然而,这并不会改变该领域仍然存在的事实,也不会改变OP询问其存在的原因。简单地说“它没有被使用”,虽然本身就是真的,但是/它没有回答他提出的问题。