Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is function's length information of other shared lib in ELF?

Our project (C++, Linux, gcc, PowerPC) consists of several shared libraries. When releasing a new version of the package, only those libs should change whose source code was actually affected. With "change" I mean absolute binary identity (the checksum over the file is compared. Different checksum -> different version according to the policy). (I should mention that the whole project is always built at once, no matter if any code has changed or not per library).

Usually this can by achieved by hiding private parts of the included Header files and not changing the public ones.

However, there was a case where a mere delete was added to the destructor of a class TableManager (in the TableManager.cpp file!) of library libTableManager.so, and yet the binary/checksum of library libB.so (which uses class TableManager ) has changed.

TableManager.h:

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

TableManager.cpp:

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

By inspecting libB.so with readelf --all libB.so, looking at the .dynsym section, it turned out that the length of all functions, even the dynamically used ones from other libraries, are stored in libB! It looks like this (length is the 668 in the 3rd column):

527: 00000000 668 FUNC GLOBAL DEFAULT UND _ZN12TableManagerD1Ev

So my questions are:

  1. Why is the length of a function actually stored in the client lib? Wouldn't a start address be sufficient?
  2. Can this be suppressed somehow when compiling/linking of libB.so (kind of "stripping")? We would really like to reduce this degree of dependency...
like image 209
Ingmar Avatar asked Nov 29 '12 14:11

Ingmar


1 Answers

Bingo. It is actually kind of a "bug" in binutils which they found and fixed in 2008. The size information is actually not useful!

What Simon Baldwin wrote in the binutils mailing list describes exactly the problem ( emphases by me):

Currently, the size of an undefined ELF symbol is copied out of the object file or DSO that supplies the symbol, on linking. This size is unreliable, for example in the case of two DSOs, one linking to the other. The lower- level DSO could make an ABI-preserving change that alters the symbol size, with no hard requirement to rebuild the higher-level DSO. And if the higher- level DSO is rebuilt, tools that monitor file checksums will register a change due to the altered size of the undefined symbol, even though nothing else about the higher-level DSO has altered. This can lead to unnecessary and undesirable rebuild and change cascades in checksum-based systems.

We have the problem with an older system (binutils 2.16). I compared it with version 2.20 on the desktop system and - voilà - the lengths of shared global symbols were 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_

So I compared both binutils source codes, and - voilà again - there is the fix which Alan suggested in the mailing list:

enter image description here

Maybe we just apply the patch and recompile binutils since we need to stay with the olderish platform. Thanks for your patience.

like image 73
Ingmar Avatar answered Oct 14 '22 19:10

Ingmar