ELF Sections for Exception Handling

In ELF binary, there are two sections to support exception handling routines that are predominately used by C++ applications: .eh_frame and  .eh_frame_hdr. However, System V Application Binary Interface (ABI) for AMD64 mandates to have those sections even they are written in C.

a) .eh_frame section

The .eh_frame section has the same structure with .debug_frame, which follows DWARF format. It represents the table that describes how to set registers to restore the previous call frame at runtime. DWARF designers allow for having flexible mechanism to be able to unwind the stack with various expressions including constant values, arithmetic, memory dereference, register contents, and control flow.

The .eh_frame section contains at least one or more Call Frame Information (CFI) records. Each CFI consists of two entry forms: Common Information Entry (CIE) and Frame Description Entry (FDE). Every CFI has a single CIE and one or more FDEs. CFI usually corresponds to a single object file. Likewise, so does FDE to a single function. However, there might be multiple CIEs and FDEs corresponding to the parts of a function when the function has a non-contiguous range in a virtual memory space. The following shows the fields of each entry in detail.

CIE Fields Data Format Description
length  4 bytes Total length of the CIE except this field
CIE_id  4 or 8 bytes 0 for .eh_frame
Version  1 byte Value 1
Augmentation  A null-terminated UTF-8 string 0 if no augmetation
Code alignment factor  unsigned LEB128 Usually 1
Data alignment factor  signed LEB128 Usually -4 (encoded as 0x7C)
return_address_register  unsigned LEB128 Dwarf number of the return register
Augmentation data length  unsigned LEB128 Present if Augmentation has ‘z’
Initial instructions array of bytes Dwarf Call Frame Instructions
padding array of bytes DW_CFA_nop instructions to match the length
FDE Fields Data Format Description
Length  4 bytes Total length of the FDE except this field; 0 means end of all records
CIE pointer  4 or 8 bytes Distance to the nearest preceding (parent) CIE
Initial location  various bytes Reference to the function corresponding to the FDE
Range length  various bytes Size of the function corresponding to the FDE
Augmentation data length  unsigned LEB128 Present if CIE Augmentation is non-empty
Instructions array of bytes Dwarf Call Frame Instructions

Here is an example of parsing a CIE and FDE (with a nice Skochinsky’s script for IDA Pro). The sizes of the following CIE and FDE are 0x14 and 0x34 respectively. The FDE has a CIE pointer pointing to the parent CIE (at 0x4090A0). The function location corresponding to this FDE starts from 0x400c70 to 0x4010c0, whose size is 0x450.

b) .eh_frame_hdr section

The .eh_frame_hdr section contains a series of attributes, followed by the table of multiple pairs of (initial location, pointer to the FDE in the .eh_frame). The entries are sorted by functions that allows to perform a quick binary search of O(log n). 

The next figure is a brief illustration of the relationship of two sections.

Note that the attribute, table_enc, describes how the table has been encoded. It consists of lower 4 bits for value and upper 4 bits for encoding as follows: DWARF Exception Header value format (lower 4 bits) and DWARF Exception Header Encoding (upper 4 bits)

References
https://refspecs.linuxfoundation.org/LSB_1.3.0/gLSB/gLSB/ehframehdr.htm
http://www.dwarfstd.org/doc/DWARF4.pdf
http://www.hexblog.com/wp-content/uploads/2012/06/Recon-2012-Skochinsky-Compiler-Internals.pdf
https://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf

Leave a Reply

Your email address will not be published. Required fields are marked *