2013-09-07 6 views
8

Я хотел бы знать, как GCC реализует обработку исключений для программ на C++. Я не мог найти легкую для понимания и самоочевидную статью в Интернете (хотя таких статей для Visual C++ много). Все, что я знаю, это то, что реализация GCC называется обработкой исключений DWARF.Реализация обработки исключений GCC C++

Я написал небольшой C++, программы и переводить его в сборку с помощью команды:

г ++ main.cpp -S -masm = Intel -fno-dwarf2-CFI-ASM

main.cpp и main.s файлы приведены здесь. Может ли кто-нибудь объяснить содержание файла main.s, особенно разделы .gcc_except_table и .eh_frame строки за строкой? (Моя ОС - Ubuntu 13.04 32-бит.) Спасибо!

main.cpp:

void f() 
{ 
    throw 1; 
} 

int main() 
{ 
    int j; 
    try { 
     f(); 
    } catch (int i) { 
     j = i; 
    } 
    return 0; 
} 

main.s:

.file "main.cpp" 
.intel_syntax noprefix 
.text 
.globl _Z1fv 
.type _Z1fv, @function 
_Z1fv: 
.LFB0: 
    push ebp 
.LCFI0: 
    mov ebp, esp 
.LCFI1: 
    sub esp, 24 
    mov DWORD PTR [esp], 4 
    call __cxa_allocate_exception 
    mov DWORD PTR [eax], 1 
    mov DWORD PTR [esp+8], 0 
    mov DWORD PTR [esp+4], OFFSET FLAT:_ZTIi 
    mov DWORD PTR [esp], eax 
    call __cxa_throw 
.LFE0: 
    .size _Z1fv, .-_Z1fv 
    .globl main 
    .type main, @function 
main: 
.LFB1: 
    push ebp 
.LCFI2: 
    mov ebp, esp 
.LCFI3: 
    and esp, -16 
    sub esp, 32 
.LEHB0: 
    call _Z1fv 
.LEHE0: 
.L7: 
    mov eax, 0 
    jmp .L9 
.L8: 
    cmp edx, 1 
    je .L6 
    mov DWORD PTR [esp], eax 
.LEHB1: 
    call _Unwind_Resume 
.LEHE1: 
.L6: 
    mov DWORD PTR [esp], eax 
    call __cxa_begin_catch 
    mov eax, DWORD PTR [eax] 
    mov DWORD PTR [esp+24], eax 
    mov eax, DWORD PTR [esp+24] 
    mov DWORD PTR [esp+28], eax 
    call __cxa_end_catch 
    jmp .L7 
.L9: 
    leave 
.LCFI4: 
    ret 
.LFE1: 
    .globl __gxx_personality_v0 
    .section .gcc_except_table,"a",@progbits 
    .align 4 
.LLSDA1: 
    .byte 0xff 
    .byte 0 
    .uleb128 .LLSDATT1-.LLSDATTD1 
.LLSDATTD1: 
    .byte 0x1 
    .uleb128 .LLSDACSE1-.LLSDACSB1 
.LLSDACSB1: 
    .uleb128 .LEHB0-.LFB1 
    .uleb128 .LEHE0-.LEHB0 
    .uleb128 .L8-.LFB1 
    .uleb128 0x1 
    .uleb128 .LEHB1-.LFB1 
    .uleb128 .LEHE1-.LEHB1 
    .uleb128 0 
    .uleb128 0 
.LLSDACSE1: 
    .byte 0x1 
    .byte 0 
    .align 4 
    .long _ZTIi 
.LLSDATT1: 
    .text 
    .size main, .-main 
    .section .eh_frame,"a",@progbits 
.Lframe1: 
    .long .LECIE1-.LSCIE1 
.LSCIE1: 
    .long 0 
    .byte 0x1 
    .string "zPL" 
    .uleb128 0x1 
    .sleb128 -4 
    .byte 0x8 
    .uleb128 0x6 
    .byte 0 
    .long __gxx_personality_v0 
    .byte 0 
    .byte 0xc 
    .uleb128 0x4 
    .uleb128 0x4 
    .byte 0x88 
    .uleb128 0x1 
    .align 4 
.LECIE1: 
.LSFDE1: 
    .long .LEFDE1-.LASFDE1 
.LASFDE1: 
    .long .LASFDE1-.Lframe1 
    .long .LFB0 
    .long .LFE0-.LFB0 
    .uleb128 0x4 
    .long 0 
    .byte 0x4 
    .long .LCFI0-.LFB0 
    .byte 0xe 
    .uleb128 0x8 
    .byte 0x85 
    .uleb128 0x2 
    .byte 0x4 
    .long .LCFI1-.LCFI0 
    .byte 0xd 
    .uleb128 0x5 
    .align 4 
.LEFDE1: 
.LSFDE3: 
    .long .LEFDE3-.LASFDE3 
.LASFDE3: 
    .long .LASFDE3-.Lframe1 
    .long .LFB1 
    .long .LFE1-.LFB1 
    .uleb128 0x4 
    .long .LLSDA1 
    .byte 0x4 
    .long .LCFI2-.LFB1 
    .byte 0xe 
    .uleb128 0x8 
    .byte 0x85 
    .uleb128 0x2 
    .byte 0x4 
    .long .LCFI3-.LCFI2 
    .byte 0xd 
    .uleb128 0x5 
    .byte 0x4 
    .long .LCFI4-.LCFI3 
    .byte 0xc5 
    .byte 0xc 
    .uleb128 0x4 
    .uleb128 0x4 
    .align 4 
.LEFDE3: 
    .ident "GCC: (Ubuntu/Linaro 4.7.3-1ubuntu1) 4.7.3" 
    .section .note.GNU-stack,"",@progbits 
+3

GCC использует Itanium ABI, который [описывает как обрабатываются исключения] (http://mentorembedded.github.io/cxx-abi/abi-eh.html#cxx-throw). –

+1

ссылка выше мертва .. @KerrekSB – FaceBro

+1

@FaceBro: Хорошо, возможно https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html –

ответ

5

.eh_frame расположение кратко описано в LSB documentation. Ян Лэнс Тейлор (автор золотого линкера) также сделал несколько сообщений в блоге on .eh_frame и .gcc_except_table layout.

Для получения более подробного описания ознакомьтесь с моим Recon 2012 slides (начало в 37 или около того).

EDIT: вот прокомментированные структуры из вашего образца. Во-первых, .eh_table (некоторые части опущены для ясности):

.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 

Далее, LSDA (для конкретного языка область данных) ссылается 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 
14

Itanium ABI (который, как GCC, лязг и ряд других следовать) указывают, что обработка исключений должен соответствовать Zero-Cost strategy.

Идея стратегии Zero-Cost заключается в том, чтобы вытолкнуть все обработки исключений в боковых таблицах, которые не сохраняются на главном пути выполнения программы (и, таким образом, не уничтожают кеш команд). Эти таблицы индексируются по программной точке.

Кроме того, для размотки стека используется информация DWARF (которая действительно является отладочной информацией). Эта функциональность обычно предоставляется в виде библиотеки, такой как libunwind, например, исходный код блокирует сборку (и, следовательно, очень специфичную для платформы).

Преимущество:

  • 0-стоимость для ввода try/catch блока (так быстро, как если бы не было ни одного)
  • 0-стоимость, имеющий throw заявления, в функции (до тех пор, как она есть не принимается)

Неудобство:

  • Slow в случае исключения (10x медленнее, чем if стратегии), так как боковые столы, как правило, не в кэше, а затем есть дорогие вычисления для запуска, чтобы знать, какие catch пункт фактически спички (на основе RTTI)

It является очень популярной стратегией реализации как на 32-битной, так и на 64-битной платформе для всех основных компиляторов ... кроме MSBC 32 бит (если я правильно помню).