airs-notes/gcc-exception-frames.md

57 lines
3.2 KiB
Markdown
Raw Normal View History

2021-01-12 20:17:52 +00:00
# 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.