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.

Reply via email to