57 lines
3.2 KiB
Markdown
57 lines
3.2 KiB
Markdown
# GCC Exception Frames
|
|
|
|
When an exception is thrown in C++ and caught by one of the calling functions,
|
|
the supporting libraries need to unwind the stack. With gcc this is done using
|
|
a variant of DWARF debugging information. The unwind information is loaded at
|
|
runtime, but is not read unless an exception is thrown. That means that the
|
|
unwind library needs to have some way of finding the appropriate unwind
|
|
information at runtime.
|
|
|
|
On some systems, this is done by registering the exception frame information
|
|
when the program starts. The registration is done with a variant of the
|
|
handling of C++ constructors. This becomes interesting when one shared library
|
|
can throw an exception which is caught by another shared library. It is
|
|
possible for such a case to arise when the executable itself never throws
|
|
exceptions and therefore has no frames to register. Obviously the unwinder
|
|
needs to be able to find the unwind information for both shared libraries,
|
|
which means that both shared libraries need to use the same registration
|
|
functions. With gcc this is normally ensured by putting the unwind code in a
|
|
shared library, `libgcc_s.so`. Each shared library, and sometimes the
|
|
executable, will use `libgcc_s.so`. That ensures a single copy of the
|
|
registration and unwind functions, so the library will be able to reliably
|
|
unwind across shared libraries. With gcc the use of `libgcc_s.so` can be
|
|
controlled with the `-shared-libgcc` and `-static-libgcc` options. Normally the
|
|
right thing will happen by default.
|
|
|
|
That approach has a cost: there is an extra shared library, and there is a
|
|
small cost of registering the unwind information at program startup or library
|
|
load time (and unregistering it if a shared library is unloaded via dlclose).
|
|
There is now a better way, which requires linker support.
|
|
|
|
Both gold and the GNU linker support the command line option `--eh-frame-hdr`.
|
|
With this option, when the linker sees the `.eh_frame` sections used to hold
|
|
the unwind information, it automatically builds a header. This header is a
|
|
sorted array mapping program counter addresses to unwind information. The
|
|
header is recorded as a program segment of type `PT_GNU_EH_FRAME`. (This is a
|
|
little bit ugly since the `.eh_frame` sections are recognized only by name;
|
|
ideally they should have a special section type.)
|
|
|
|
At runtime, the unwind library can use the `dl_iterate_phdr` function to find
|
|
the program segments of the executable and all currently loaded shared
|
|
libraries. It can use that to find the `PT_GNU_EH_FRAME` segments, and use the
|
|
sorted array in those segments to quickly find the unwind information.
|
|
|
|
This approach means that no registration functions are required. It also means
|
|
that it is not necessary to have a single shared library, since
|
|
`dl_iterate_phdr` is available no matter which shared library throws the
|
|
exception.
|
|
|
|
This all only works if you have a linker which supports generating
|
|
`PT_GNU_EH_FRAME` sections, if all the shared libraries and the executable are
|
|
linked by such a linker, and if you have a working `dl_iterate_phdr` function
|
|
in your C library or dynamic linker. I think that pretty much restricts this
|
|
approach to GNU/Linux and possibly other free operating systems. For those
|
|
scenarios, I hope that gcc will soon be able to stop using `libgcc_s.so` by
|
|
default.
|
|
|