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

我们的项目(C ++,Linux,gcc,PowerPC)由几个共享库组成。 当发布新版本的包时,只有那些库应该改变其源代码实际上受到影响。 “改变”是指绝对的二进制标识 (比较文件上的校验和,不同的校验和 – 根据策略不同的版本)。 (我应该提到,整个项目总是一次build成,无论代码是否更改或不是每个库)。

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

但是,有一种情况是,在库libTableManager.so的类TableManager的析构函数(在TableManager.cpp文件中!)中添加了一个简单的delete操作,而库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 } 

通过检查libB.so与readelf --all libB.so ,看看.dynsym部分,事实certificate,所有函数的长度 ,即使是从其他库中dynamic使用的长度 ,都存储在libB! 看起来像这样(长度是第三栏的668):

527: 00000000 668 FUNC GLOBAL DEFAULT UND _ZN12TableManagerD1Ev

所以我的问题是:

  1. 为什么实际存储在客户端lib中的函数的长度 ? 起始地址是否足够?
  2. 编译/链接的libB.so(“剥离”的种类)时可以以某种方式压制? 我们真的想减less这种依赖程度…

答对了。 这实际上是他们在2008年发现并修复的binutils中的一个“bug”。 尺寸信息实际上并没有用处

西蒙·鲍德温( Simon Baldwin )在binutils邮件列表中写到的正是这个问题(我强调的是):

目前,在链接时,未定义的ELF符号的大小从提供符号的目标文件或DSO中复制出来。 这个大小是不可靠的 ,例如在两个DSO的情况下,一个连接到另一个。 较低级别的DSO可以进行ABI保持改变,改变符号大小,而不需要重建更高级别的DSO。 如果更高级别的DSO被重建,那么监视文件校验和的工具将会由于未定义符号的大小发生变化而发生变化,即使没有更高级别的DSO已经改变。 这可能会导致在基于校验和的系统中不必要的和不希望的重建和改变级联。

我们有一个较旧的系统(binutils 2.16)的问题。 我将它与桌面系统上的版本2.20进行了比较,并且 – 共享全局符号的长度为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在邮件列表中建议的修正:

在这里输入图像描述

也许我们只是应用补丁,并重新编译binutils,因为我们需要留在较旧的平台。 谢谢你的耐心。

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

加载器需要把所有将被放入进程的函数,并映射到内存地址。 所以,它给第一个函数一个地址。 然后,第二个在第一个结束之后 – 但是要知道“第一个结束”,它需要知道第一个功能是多久。

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

后者似乎(对我)有两个相当明显的缺点。 首先是速度 – 打开所有这些额外的文件,解析它们的头文件等,只是为了得到函数的长度几乎肯定比从当前文件中读取每个函数多出四个字节。 第二个是便利性:只要不调用文件中的任何函数,就不需要该文件存在。 如果直接从文件中读取长度(例如,像Windows通常使用DLL那样),则即使目标系统没有实际使用,也需要该文件存在于目标系统上。

编辑:由于有些人显然错过了“意图完成”的(显然是太)微妙的含义,让我完全清楚:我相当肯定这个领域是不(实际上从来没有)实际使用。

然而,任何认为这个答案都是错误的人都需要回到程序设计101,学习一个接口和一个实现之间的区别。

在这种情况下,文件格式定义了一个接口 – 一个加载器可以使用的一组功能。 在Linux的具体情况下,似乎这个字段没有被使用过。

然而,这并没有改变这一领域依然存在的事实,也没有改变选举委员会问为什么存在的事实。 简单地说,“它不被使用”本身是真实的,不会回答他所问的问题。