.eh_frame

When gcc generates code that handles exceptions, it produces tables that describe how to unwind the stack. These tables are found in the .eh_frame section. The format of the .eh_frame section is very similar to the format of a DWARF .debug_frame section. Unfortunately, it is not precisely identical. I don’t know of any documentation which describes this format. The following should be read in conjunction with the relevant section of the DWARF standard, available from http://dwarfstd.org.

The .eh_frame section is a sequence of records. Each record is either a CIE (Common Information Entry) or an FDE (Frame Description Entry). In general there is one CIE per object file, and each CIE is associated with a list of FDEs. Each FDE is typically associated with a single function. The CIE and the FDE together describe how to unwind to the caller if the current instruction pointer is in the range covered by the FDE.

There should be exactly one FDE covering each instruction which may be being executed when an exception occurs. By default an exception can only occur during a function call or a throw. When using the -fnon-call-exceptions gcc option, an exception can also occur on most memory references and floating point operations. When using -fasynchronous-unwind-tables, the FDE will cover every instruction, to permit unwinding from a signal handler.

The general format of a CIE or FDE starts as follows:

A CIE record continues as follows:

The next fields of the CIE depend on the augmentation string.

The remaining bytes are an array of DW_CFA_xxx opcodes which define the initial values for the frame table. This is then followed by DW_CFA_nop padding bytes as required to match the total length of the CIE.

An FDE starts with the length and ID described above, and then continues as follows.

The remaining bytes in the FDE are an array of DW_CFA_xxx opcodes which set values in the frame table for unwinding to the caller.

The DW_EH_PE_xxx encodings describe how to encode values in a CIE or FDE. The basic encoding is as follows:

In addition the above basic encodings, there are modifiers.

If you follow all that, and also read up on .debug_frame, then you have enough information to unwind the stack at runtime, e.g. to implement glibc’s backtrace function. Later I’ll describe the LSDA and the personality function, which work together to implement exception catching on top of stack unwinding.