Rich Felker
2014-02-25 23:11:11 UTC
I've found a subtle but serious problem in libpcc: all of the symbols
have default visibility rather than hidden. This leads to serious
problems building and using shared libraries. The scenario goes like
this:
Program P is linked with version 1 of library L (call it L1).
Both P and L1 contain (for example) 64-bit division requiring
__divdi3.
Since L1 uses __divdi3, the function gets linked into L1 *and*
exported as part of L1's dynamic symbol table.
Thus, when P is linked against L1, __divdi3 is found in L1 and
therefore resolved to the symbol in L1. Since L1 is searched before
-lpcc on the linker command line, P does not get its own copy of
__divdi3 from libpcc.
Now, L gets upgraded to version 2 (L2) which no longer uses 64-bit
division. So L2 does not contain __divdi3.
When P gets run with L1 replaced by L2 (note: in my scenarion, both
versions of L are intended to be compatible and expose the same public
API/ABI), the dynamic linker bails out with an unresolved reference to
__divdi3.
This exact same problem existed in ancient versions of gcc/libgcc, and
it's still present if you pass --disable-shared to gcc's configure
script (such a gcc build is not suitable for production of shared
libraries). On modern gcc, all symbols in libgcc.a are hidden, so that
if/when they get linked into shared libraries, they don't get exported
to the main program or other libraries, and thus everything that needs
libgcc functions gets its own copy. libpcc should be doing the same.
I looked for an easy global way to fix this, e.g. objcopy magic on the
output or #pragma GCC visibility push(hidden), but objcopy lacks such
a feature and pcc silently ignores this pragma. So I think the fix is
just adding __attribute__((__visibility__("hidden"))) to each function
definition. Is this acceptable?
Rich
have default visibility rather than hidden. This leads to serious
problems building and using shared libraries. The scenario goes like
this:
Program P is linked with version 1 of library L (call it L1).
Both P and L1 contain (for example) 64-bit division requiring
__divdi3.
Since L1 uses __divdi3, the function gets linked into L1 *and*
exported as part of L1's dynamic symbol table.
Thus, when P is linked against L1, __divdi3 is found in L1 and
therefore resolved to the symbol in L1. Since L1 is searched before
-lpcc on the linker command line, P does not get its own copy of
__divdi3 from libpcc.
Now, L gets upgraded to version 2 (L2) which no longer uses 64-bit
division. So L2 does not contain __divdi3.
When P gets run with L1 replaced by L2 (note: in my scenarion, both
versions of L are intended to be compatible and expose the same public
API/ABI), the dynamic linker bails out with an unresolved reference to
__divdi3.
This exact same problem existed in ancient versions of gcc/libgcc, and
it's still present if you pass --disable-shared to gcc's configure
script (such a gcc build is not suitable for production of shared
libraries). On modern gcc, all symbols in libgcc.a are hidden, so that
if/when they get linked into shared libraries, they don't get exported
to the main program or other libraries, and thus everything that needs
libgcc functions gets its own copy. libpcc should be doing the same.
I looked for an easy global way to fix this, e.g. objcopy magic on the
output or #pragma GCC visibility push(hidden), but objcopy lacks such
a feature and pcc silently ignores this pragma. So I think the fix is
just adding __attribute__((__visibility__("hidden"))) to each function
definition. Is this acceptable?
Rich