This is the mail archive of the
gcc@gcc.gnu.org
mailing list for the GCC project.
Re: A shared version of libgcc
- To: gcc at gcc dot gnu dot org
- Subject: Re: A shared version of libgcc
- From: Mark Kettenis <kettenis at wins dot uva dot nl>
- Date: Mon, 1 May 2000 18:07:49 +0200
- CC: mark at codesourcery dot com, drepper at cygnus dot com, rth at cygnus dot com
From: Mark Mitchell <mark at codesourcery dot com>
Date: Sat, 29 Apr 2000 19:03:24 -0700
I've decided that we have no choice but to bite the bullet and do this
in the new release. Users will be no worse off: they can always
choose to link statically with libgcc.a, and accept the consequences
(i.e., that simultaneously linking with shared libraries probably
won't work right.)
[...]
I appreciate everyone's input on this issue.
I don't consider my decision final -- if there are serious new issues
raised, we should certainly consider them.
I think simply converting the current libgcc into a shared library is
a bad idea. Introducing a shared library of some sort, is probably
inevitable, but it should be done with the greates possible care since
it has the possibility of great disruption on systems where GCC is the
system compiler. In the worst, case bad choices made by the GCC team
could mean that people will be forced to recompile every single binary
on their system. Therefore, I think that before starting any coding,
a more detailed investigation is needed. I also haven't heard from
Ulrich Drepper (the GNU C Library maintainer) yet. I think his input
in the discussion would be very important.
Anyhow, here's my attempt to sum up the current problems that prompted
the whole shared libgcc idea, and possible solutions. I included a
section about issues specifically related to the GNU C Library, since
that's used on the systems I care about. Comments are welcome, and if
people think it is useful, I'll improve the document to take those
comments into account.
Mark
----------------------------------------------------------------------
First, let me say that I'm not convinced that a shared libgcc is the
right thing to do for GCC 3.0. I think for instance that it is
possible to avoid using a shared libgcc for standard C programs. This
would be a great win since it reduces depencies between different
components.
The solutions are a bit ELF and i386 centered, but most of the issues
are relevant for other targets too.
Symbols in libgcc and their problems
====================================
1. Simple utility functions without interdependencies or access to
global variables[1]. Examples are __ashldi3, __udivmoddi4.
The problem with these functions is that code used to build a
shared library might need one of these functions. Therefore shared
libraries are linked with libgcc. However, if no measures are
taken, these functions are re-exported from the shared library.
This means that they have become part of that library's ABI, often
without the author/builder actually knowing it. Even worse, the
functions that are re-exported might depend on the GCC release used
to compile the code. The particular functions that are now
provided by the shared library are left out from programs linked
against this shared library.
The problem doesn't actually surface until the shared library is
recompiled and somehow loses a dependency on one of the functions
in libgcc (perhaps because the actual implemantation changed, or a
different compiler was used). Or in a binary-only setting, when a
program linked against a "standard" library is distributed and the
developer used a shared library that contained a different set of
symbols from libgcc than the actual shared library installed on the
user's system.
Assuming that these functions contain no serious bugs, it is
absolutely no problem if more than one (potentially different) copy
of these functions is used in one program. Most of these functions
are small, so a slight increase in the size of binaries won't be
noticed. Therefore these functions can stay in a static library if
we can control re-exporting these functions from shared libraries.
2. More complex utility functions, that depend on other functions or
access global variables. Examples are the functions for profiling
basic blocks, for example __bb_init_func, and the frame functions,
that is __deregister_frame, __deregister_frame_info,
__frame_state_for, __register_frame, __register_frame_info,
__register_frame_info_table and __register_frame_table. A third
group of functions in this class are the interfaces that implement
the actual exception handling mechanism, for example __throw.
The profiling functions are probably not really important; they're
only used in specially prepared code for debugging purposes that's
not widely distributed in binary form.
The frame functions are a PITA. We had serious problems with those
in the past on Linux. If we don't think carefully, and plan ahead
we will have serious problems with these functions again! Let's
look a bit closer at them.
The problem with re-exporting symbols from libgcc is still present,
but it is worsened by the fact that there must be only one copy of
these functions that's actually used. In fact, it is the copy that
comes from the most recent libgcc that must be used. This probably
means that on systems that support shared libraries, these
functions must live in a shared library too.
Note that these functions are only needed for code compiled with
-fexceptions. Or perhaps it is better to say that these functions
are only needed if exception handling is actually used, that is, if
the program or one of the libraries it uses references
__frame_state_for. The registration of frame unwind info is
registered by calling __register_frame_info in crtbegin.o (or
crtbeginS.o). But if the target supports it the current code uses
weak references to avoid sucking the frame code in if it's not needed.
It worries me quite a bit to see that the module containing the
frame functions references several of the pthreads functions. This
means that libgcc is tied to a particular implementation of those
functions. This isn't too bad while libgcc is a static library,
but could lead to all kinds of badness if libgcc is turned into a
shared library, and used in multithreaded libraries.
Concerning the other exception handling functions: It is probably
the easiest thing to put these functions together with the frame
functions in the same shared library.
3. Language specific symbols.
Right now if the C++ compiler is built, some C++ code is added to
libgcc. So those parts of libgcc introduce a dependency on the C++
ABI. While the intention is that the GCC 3.0 C++ ABI will be
stable, it is always possible that it might be needed to tweak it a
bit to fix a bug. Keeping the C++ ABI specific parts in a
different (shared) library gives you the freedom to do that without
forcing everyone to recompile every single program on their Linux
system.
--------
[1] Some functions in libgcc call standard ISO C functions. Calling
abort() is probably fine since its ABI is in principle entirely
defined by function calling conventions. A function calling
abort() would still classify as a simple utility function. But if
a function in libgcc calls more complex ISO C functions they
probably wouldn't.
Adressing those problems
========================
Let's first look at the problem of re-exporting symbols in class 1 from
shared libraries. Here's a list of possible options:
* Do nothing about it and leave the current status quo, that has been
present ever since people have produced shared libraries using
GCC. This places a burden on people building shared libraries to
make sure that they keep re-exporting the same set of symbols from
libgcc.
* Put those symbols in a shared library, and link all shared
libraries and programs that use any of those symbols against this
library. Finding out whether to link with the shared libgcc or not
might be tricky. Leaving that up to the link-editor doesn't work
for sure. On systems where GCC isn't the system compiler we can
probably link programs that are only linked against shared
libraries that come with the system with a static libgcc.
* Make use of some special magic to prevent shared libraries from
re-exporting symbols from libgcc.a. On ELF systems using the GNU
linker-editor or systems using a modern Solaris linker this can be
done with version maps (note that this can be done without actually
versioning the symbols). On ELF systems that support it,
STV_HIDDEN coult be used. Other systems might have other
possibilities to give these symbols only local scope. Of course
this means that shared libraries that are already re-exporting
symbols from libgcc will no longer do that by default. Programs
linked against that shared library might have to be relinked, or
people might build shared library with a special GCC option that
makes the shared library in question re-export the symbols in
question. Forcing people to think about the re-exporting problem
might not be such a bad idea.
* Perhaps it is possibly to make symbols invisible to the
link-editor, but still visible to the dynamic linker? I believe I
managed to do that in the past on Linux, but I may have been
exploiting a bug in the link-editor and/or dynamic linker. If this
is really possible, I think this would be the preferred option.
Now what are the options for the problem with the frame functions?
* Put those functions in a shared library, and link all shared
libraries and programs that use any of those functions against this
library. Since __register_frame_info is referenced by crtbegin.o
this effectively means everything will be linked against the shared
libgcc. Again on systems where GCC isn't the system compiler, we
can use the static libgcc if a program is only linked against
system libraries.
* On ELF systems we can put those functions in a shared library, and
link only shared libraries and programs that use __frame_state_for
against this library. This means that only programs that actually
use exception handling (or where one of the loaded shared libraries
use exception handling) will depend on the shared libgcc.
* On ELF systems, we might include the frame functions in shared
libraries that need them (i.e. those that actually use exception
handling), but set DT_AUXILIARY to a shared library containing the
frame functions. This will allow things to work if the shared
libgcc isn't found, but it only works if those libraries re-export
the frame functions and if programs linked with that library do not
contain the frame functions.
* On ELF systems where we have some control over libc, we can put the
frame functions in libc, and set DT_AUXILIARY for libc to a shared
libgcc. That way programs and shared libraries that are linked
against libc do not have to be explicitly linked against a shared
libgcc, whereas we leave the option open to install a shared libgcc
if the frame functions will ever need to be changed.
And what about the C++ specific symbols? Since we control libstdc++,
we have some additional flexibility here:
* Keep those in a static library. If they're also included in
libstdc++ this static library will only be used for C++ programs
that don't use the standard C++ library.
* Include those symbols in libstdc++ and link all C++ programs
against libstdc++.
* Include those symbols in a special C++ runtime shared library.
* Include those symbols in both libstdc++ and the special C++ runtime
shared library. That way one doesn't have to distribute two
libraries for C++ programs that use the standard C++ library.
Systems that use the GNU C Library
==================================
Since I am somewhat involved with the Hurd, I'd like to stress that
the GNU C Library is used on Linux and the Hurd! On both systems, GCC
is the system compiler which means that there are additional issues to
take into account. In order to avoid GCC 3.0 to depend on an
unreleased version of the GNU C Library, planning ahead is probably
essential.
Because of the problems mentioned above, the GNU C Library contains
the following symbols from class 1:
__divdi3; __moddi3; __udivdi3; __umoddi3;
These symbols were present in glibc-2.0, which was released before
symbol version was available to prevent re-exporting these symbols.
The library also contains the following symbols from class 2:
__register_frame; __register_frame_table; __deregister_frame;
__register_frame_info; __deregister_frame_info; __frame_state_for;
__register_frame_info_table;
These symbols are present to ensure that a single copy is used. The
idea is that every program is linked with -lc, and therefore will use
these symbols from libc.so instead of picking them up from libgcc.a.
Since glibc-2.1, all these symbols are versioned (their version name
is GLIBC_2.0). It is not possible to remove those symbols from libc.
However, it might be possible to (ab)use symbol versioning to make
sure that new programs don't pick them up from libc.so[2]. But I don't
think it is worth the trouble doing this for the symbols in class 1.
The frame functions pose a bigger problem, since their presece in libc
will make it difficult to make sure that the newest version of those
functions is used at all times.
Possible solutions for this problem:
* Force people to recompile libc if the frame functions are changed.
This should be avoided, since recompiling libc isn't really easy,
and therefore this would restrict the freedom of people to install
a different version of GCC.
* Make the definitions of the frame functions included in libc weak.
This will make sure that they will be overridden by copies included
in programs or other shared libraries. This means that once these
functions are really changed one needs to start linking programs
against a shared libgcc that provides appropriately versioned symbols.
* Use the DT_AUXILIARY trick mentioned above. This means that that
the shared libgcc should provide appropriately versioned symbols.
Since this ties libc to libgcc, this probably means that you cannot
change libgcc's soname without bumping libc's soname. Of course
you can use symbol versioning to avoid that.
* It might be possible to make the link-editor ignore the frame
symbols in libc[2].
-----
[2] However, as I said before I might be fooled by a bug in the
link-editor and/or dynamic linker.
Conclusions
===========
Simply turning the complete libgcc as we know it into a shared library
is not a good idea. We should at least separate out bits that depend
on additional programming language dependent ABI's (such as the C++
parts). Moreover, I think we should do our very best to avoid a
needless dependency on an additional shared library. I think this
should be possible for plain C code, and that it is worth the trouble
to try to achieve this.