Hi all,
We provide a static library to our customers. We use certain third party libraries in our static library, which we include within the library itself because we don't want our customers to have to have their own versions of these libraries, nor do we want to deal with conflicting or untested versions.
In an attempt to avoid symbol conflicts, we use nmedit to limit the visibility of symbols in our static library to just our exported symbols. Basically the pattern we use is to include our compiled code and third party static libraries into an object file target. We then use the nmedit tool on the resulting object file product to limit symbol visibility to just the symbols we want to export. We do this via the nmedit "-s" option, providing a file with the list of symbols to export.
Unfortunately while testing this we've found that sometimes we get duplicate symbol link errors in test applications that use boost and our static library. That is, when a test application includes boost, and the static library also carries boost (but with symbols limited via nmedit), we still see conflicts. In our test case the common problematic symbol is __ZTIN5boost6system12system_errorE (or "typeinfo for boost::system::system_error" when unmangled), but I suspect that there are other cases that could happen as well.
The problem appears to be related to how coalesced symbols transform through our build process. That's why I'm posting the problem here instead of on the boost list.
To diagnose the problem, I used nm on the boost static libraries we're building and see several hits like the following:
0000000000012ee0 (__DATA,__datacoal_nt) weak external __ZTIN5boost6system12system_errorE
When I look for the same symbol in our object files that go into our symbol limited object target, I see several hits like this:
0000000000008a50 (__DATA,__datacoal_nt) weak private external __ZTIN5boost6system12system_errorE
The difference between the two is that the coalesced symbol from the boost libraries are non-private, while the same symbol from our object files are private. This could be because we build our code to have symbol visibility hidden (for both code and inlines). Although we build boost with the same compile options (-fvisibility=hidden and -fvisibility-inlines-hidden), but we end up with different visibility in the boost libraries than in our object files. Maybe the private attribute in the boost build is converted to non-private when the static libraries are created from the boost object files?
When the boost static libraries and our object files are combined in our object target, before we call nmedit to limit the symbols, I see the following in our object target:
0000000000502340 (__DATA,__datacoal_nt) weak external __ZTIN5boost6system12system_errorE
I guess the linker saw both "weak external" and "weak private external" and coalesced it into "weak external".
After running nmedit on the object target, specifying the set of symbols to keep global via the -s option and a file listing the symbol names, the symbol in question ends up like this:
0000000000502340 (__DATA,__datacoal_nt) non-external __ZTIN5boost6system12system_errorE
Basically what seems to be happening is nmedit is converting what would otherwise be a coalesced weak external into a static. At this point the symbol is non-private and static, so it can effectively be found and linked against in executable targets (which is wrong because we want the symbol hidden).
When I look for the symbol in the object files from our test application (which uses boost), I see the following:
000000000013f120 (__DATA,__datacoal_nt) weak external __ZTIN5boost6system12system_errorE
I think the link failure happens because the linker sees the "non-external" symbol in our static library and the "weak external" symbol in the test application objects and cannot coalesce them. Instead it's considered to be a duplicate symbol and an error.
I think what we want for coalesced symbols in our static library to be private non-external, not non-external. Maybe that the initial coalescing of the "weak external" in the boost libraries and the "weak private external" in our objects to "weak external" is where things go off the rails because the private aspect of the coalesced symbol has been stripped. But this is all a bit outside of my symbol knowledge comfort zone, so I'm not sure that I completely understand this.
Obviously what we want is for *all* symbols in our library to be private except for a well defined set. We want all third party library code, as well as our own code, to be hidden and private to the object target.
Does any one have any suggestions for resolving this issue?
Best,
|