https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87190
Bug ID: 87190 Summary: Feedback on documentation for symbol visibility Product: gcc Version: unknown Status: UNCONFIRMED Severity: normal Priority: P3 Component: web Assignee: unassigned at gcc dot gnu.org Reporter: noloader at gmail dot com Target Milestone: --- This is general feedback on symbol visibility documentation. I feel like the docs are lacking some important information and it makes a resulting shared object appear to not meet expectations. The docs I am referring to: * https://gcc.gnu.org/wiki/Visibility * https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html At the highest level, the docs say to use -fvisibility=hidden (and perhaps -fvisibility-inlines-hidden) to hide all symbols. Then the user is expected to use DLL_PUBLIC (from the Visibility wiki) to unhide symbols and create a public API. This works as expected when a shared object does not depend on other libraries. Something like: $ cat bar.c extern "C" DLL_PUBLIC int DoBar(int n) { ... return 0; } $ gcc -fvisibility=hidden -shared bar.c -o libbar.so The result is something like the following, which is expected. It is consistent with the documents. $ nm -gCD libbar.so | grep ' T ' $ 00000000000184e8 T _fini $ 00000000000069d0 T _init $ 00000000000095b0 T DoBar Now, here's where the problem crops in. Suppose Bar depends on Foo and Foo was not built with visibility. This is very common, even among some distro provided libraries. Remember the docs say _all_ symbols are private (not _some_ symbols). $ gcc -fvisibility=hidden -shared bar.c ./libfoo.a -o libbar.so The results will be similar to the following. Notice the count grows from 3 to the number of symbols available in libfoo.a. $ nm -gCD libbar.so | grep ' T ' | wc -l 816 In the second example GCC drove compile and link but not all symbols were private. To make them private we have to add linker options: $ gcc -fvisibility=hidden -shared bar.c ./libfoo.a -o libbar.so -Wl,--exclude-libs,All This is the point of contention for me because the docs say all symbols are private unless DLL_PUBLIC is used (from the Visibility wiki) to unhide symbols. I think the docs could be more clear on both the GCC options page and the Visibility wiki. I think two ro three things should be stated for completeness. First, instead of saying all symbols are private when using the options, maybe the docs should say "symbols in the translation unit being compiled are private, and not all symbols in the program or library". Second, the docs might clearly state -fvisibility=hidden (and friends) only applies to the compiler. The compiler does not pass down "-fvisibility=hidden"-like options to the linker. Third, the docs should say something like "additional linker switches may be required to hide all symbols in a shared object. For the GNU linker LD, LDFLAGS should include -Wl,--exclude-libs,All. Other linkers may require additional options." The second suggestion may seem obvious but it is not. We are told to use the compiler to drive link, and the compiler doing so with -fsanitize=undefined or -fsanitize=address works as expected. That is we don't need to manually add libraries by hand. I was looking for some past/similar issues but I did not find a good one. Maybe no one is using the feature or no one noticed. I did find this one, which seems to be a symptom of the confusion: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=20218. It is fascinating reading Andrew Pinski and H.J. Lu discussing the behavior and externalities like the ELF specs (I did not even consider the effects of the ELF specification). But at the end of the day the GCC docs say all the symbols are private but it is possible to arrive at a program or shared library where some symbols are private. If someone is willing to make the leap that GCC should take the -fvisibility=hidden compiler option and turn it into a linker option like -Wl,--exclude-libs,All (when driving link), then this could be a GCC issue. I'd personally like to see it happen, but I am not willing to go that far and make the leap.