I would like to know how GCC implements exception handling for C++ programs. I couldn\'t find an easy-to-understand and self-explanatory article on the Web (although there are m
The Itanium ABI (which both gcc, clang and a number of others follow) specify that exception handling should follow the Zero-Cost strategy.
The idea of the Zero-Cost strategy is to push all exception handling in side-tables that are not kept on the main program execution path (and thus not trashing the instruction cache). These tables are indexed by the program point.
Furthermore, DWARF information (which is debug information really) is used to unwind the stack. This functionality is usually provided as a library such as libunwind for example, the source code is chokeful of assembly (and thus very platform specific).
Advantages:
try
/catch
block (as fast as if there was none)throw
statement in a function (as long as it is not taken)Disadvantage:
if
strategy) because the side tables are usually not in cache and then there are expensive computations to run to know which catch
clause actually matches (based on RTTI)It is a very popular strategy implement on both 32 bits and 64 bits platform for all major compilers... except MSVC 32 bits (if I remember correctly).
.eh_frame
layout is described briefly in the LSB documentation. Ian Lance Taylor (author of the gold linker) also made some blog posts on .eh_frame and .gcc_except_table layout.
For a more reference-like description, check my Recon 2012 slides (start at 37 or so).
EDIT: here's the commented structures from your sample. First, the .eh_table
(some parts omitted for clarity):
.Lframe1: # start of CFI 1
.long .LECIE1-.LSCIE1 # length of CIE 1 data
.LSCIE1: # start of CIE 1 data
.long 0 # CIE id
.byte 0x1 # Version
.string "zPL" # augmentation string:
# z: has augmentation data
# P: has personality routine pointer
# L: has LSDA pointer
.uleb128 0x1 # code alignment factor
.sleb128 -4 # data alignment factor
.byte 0x8 # return address register no.
.uleb128 0x6 # augmentation data length (z)
.byte 0 # personality routine pointer encoding (P): DW_EH_PE_ptr|DW_EH_PE_absptr
.long __gxx_personality_v0 # personality routine pointer (P)
.byte 0 # LSDA pointer encoding: DW_EH_PE_ptr|DW_EH_PE_absptr
.byte 0xc # Initial CFI Instructions
[...]
.align 4
.LECIE1: # end of CIE 1
[...]
.LSFDE3: # start of FDE 3
.long .LEFDE3-.LASFDE3 # length of FDE 3
.LASFDE3: # start of FDE 3 data
.long .LASFDE3-.Lframe1 # Distance to parent CIE from here
.long .LFB1 # initial location
.long .LFE1-.LFB1 # range length
.uleb128 0x4 # Augmentation data length (z)
.long .LLSDA1 # LSDA pointer (L)
.byte 0x4 # CFI instructions
.long .LCFI2-.LFB1
[...]
.align 4
.LEFDE3: # end of FDE 3
Next, the LSDA (language-specific data area) referenced by FDE 3:
.LLSDA1: # LSDA 1
.byte 0xff # LPStart encoding: DW_EH_PE_omit
.byte 0 # TType encoding: DW_EH_PE_ptr|DW_EH_PE_absptr
.uleb128 .LLSDATT1-.LLSDATTD1 # TType offset
.LLSDATTD1: # LSDA 1 action table
.byte 0x1 # call site encoding: DW_EH_PE_uleb128|DW_EH_PE_absptr
.uleb128 .LLSDACSE1-.LLSDACSB1 # call site table length
.LLSDACSB1: # LSDA 1 call site entries
.uleb128 .LEHB0-.LFB1 # call site 0 start
.uleb128 .LEHE0-.LEHB0 # call site 0 length
.uleb128 .L8-.LFB1 # call site 0 landing pad
.uleb128 0x1 # call site 0 action (1=action 1)
.uleb128 .LEHB1-.LFB1 # call site 1 start
.uleb128 .LEHE1-.LEHB1 # call site 1 length
.uleb128 0 # call site 1 landing pad
.uleb128 0 # call site 1 action (0=no action)
.LLSDACSE1: # LSDA 1 action table entries
.byte 0x1 # action 1 filter (1=T1 typeinfo)
.byte 0 # displacement to next action (0=end of chain)
.align 4
.long _ZTIi # T1 typeinfo ("typeinfo for int")
.LLSDATT1: # LSDA 1 TTBase