dropping galias



Hi

I've been doing some research today on the possibility of dropping the
generation and use of galias.h and galiasdef.c.  I've only looked into
libglib so far.

My main beef is that all of glib gets rebuilt every time the symbol
table changes in any way (ie: glib.symbols is modified).  My secondary
beef is that it's a huge hack.

My main conclusion from what I've discovered is that anything that we
could do to improve that situation is at least as hacky as what we're
doing already.

This is what I have to report:

There are 3 ways to do visibility overrides (ranging from
highest-precedence to lowest):

  1) per-symbol via __attribute__.  This is what G_GNUC_INTERNAL is
     doing.

  2) per-section via GCC #pragma:

    #pragma GCC visibility push (hidden)
    ... hidden symbols declared here ...
    #pragma GCC visibility pop

  3) per-compilation-unit using -fvisibility= command line option to
     GCC.


There are 3 main kinds of visibility:

  "default": this more or less means "public"
  "hidden": not visible outside of the library
  "protected": visible outside of the library, but some of the
               advantages of hidden

Ideally, I'd like to do the following:

  1) have the default symbol visibility be "hidden"
  2) have symbols declared in public .h files be "protected"

#1

It seems that #1 can be accomplished easily by using -fvisibility=hidden
as a CFLAG, but that's a bad idea: having visibility set to "hidden"
when processing the system header files leads to glib not using its PLT
when calling them which leads to relocations being performed all over
the executable code.  Totally unacceptable.  The recommended workaround
for this is to use #pragma to surround #includes of system header files
to restore default visibility.  If we were to do that then I'd like to
have one big .h file in glib that #includes all the system header files
that are ever needed by any part of glib and do the magic there.  This
would also allow pre-compiling that big header, but I think this is a
bad idea for now.

We can, in effect, get the "hidden" behaviour for continuing to do what
we do now -- use G_GNUC_INTERNAL and run 'make check' a lot to ensure
that things don't accidentally slip through the cracks.


#2

This is far more interesting.  My first idea was to set it up so that
the G_BEGIN_DECLS and G_END_DECLS already in our header files are
#defined to be #pragma GCC visibility push (protected) (and 'pop' for
END).  This fails because you can't have #pragma inside #define.  C99
brings in _Pragma() however and you can use that, so:

# define G_BEGIN_DECLS _Pragma("GCC visibility push(protected)")
# define G_END_DECLS _Pragma("GCC visibility pop")

when building glib itself.  Cool.


Problem is this: the C language requires that two references to the
address of a function return a pointer that compares equal.  The idea is
so we could have a function like this:

/* returns %TRUE if the given callback is g_free */
gboolean is_gfree (GDestroyNotify freefunc) {
  return freefunc == g_free;
}

This already doesn't work with glib because, due to our aliasing we are
actually comparing 'g_free' to '__IA_g_free'.  Since we went out of our
way to do that to ourselves, though, it's our own bed to lie in.

If you try to do the same thing with a 'protected' symbol then you get
into all kinds of trouble.  Because the symbol is "protected" the access
inside of glib will be to the function address (bypassing the GOT)
whereas the access outside of glib will be to the GOT address.  This
means that the function pointer won't have the same value.  In practice,
this causes GCC to output a relocation that the linker then says is
invalid.

The GCC and binutils people seem to be at a bit of a disagreement over
who should fix it or if it should be fixed at all.  See
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=19520 .  Probably, GCC
should use the GOT address when taking addresses of protected functions
and then it would all work out OK.

Of course, we don't care about this, because it already doesn't work for
us.


I don't think we should expect a fix any time soon, and even if we did,
we wouldn't be able to use it in our build system until it was very
widely deployed.  So we're left with the idea of working around it.

  1) Patch the assembly or object files somehow: if we change the   
     get-address accesses to internal functions to use the GOT then the
     linker stops complaining.  I don't think I need to explain why this
     isn't a realistic solution.

  2) Do some special hacks for files that declare functions that have
     their address taken by other files in glib.  These hacks would
     basically be equivalent to the current '__IA_g_free' thing we're
     doing, but to a much lesser extent.  The following list is 
     exhaustive:

       g_str_hash, g_str_equal, g_direct_hash, g_direct_equal
       g_option_group_free
       g_poll
       g_scanner_error, g_scanner_warn
       g_free
       g_thread_pool_new  [but not really -- can easily be fixed]



One more thing:  if we some day turn on -fvisibility=hidden, mixed with
using G_BEGIN/END_DECLS to override symbols back to being visible, we
have another problem to contend with:  glib is built with
G_DISABLE_DEPRECATED which means that a bunch of symbols that we want to
appear on our public ABI are not declared in header files when building
glib.  This means that those symbols don't get marked as having the
increased visibility (by being between G_BEGIN/END_DECLS).  Fail.

This also has the effect that even without -fvisibility=hidden, those
symbols would end up as public rather than "protected", but since we
never call them ourselves, that would be OK (no PLT entries, no
performance loss).





If we did do anything, this is what I would like to do:

  - use the BEGIN/END_DECLS _Pragma() thing for "protected" visibility

  - have a file listing the functions that need special treatment
   (listed above)

  - give those few exceptional cases the current galias treatment


All of this is subject to using GCC, of course (but so are our current
aliasing hacks).


Cheers



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]