2013-09-07 48 views
8

Tôi muốn biết cách GCC thực hiện xử lý ngoại lệ cho các chương trình C++. Tôi không thể tìm thấy một bài viết dễ hiểu và tự giải thích trên Web (mặc dù có rất nhiều bài viết như vậy cho Visual C++). Tất cả những gì tôi biết là việc thực thi GCC được gọi là xử lý ngoại lệ DWARF.Thực hiện xử lý ngoại lệ GCC C++

Tôi đã viết một C nhỏ ++ chương trình và dịch nó thành lắp ráp bằng lệnh:

g ++ main.cpp -S -masm = intel -fno-dwarf2-CFI-asm

Các Các tệp main.cppmain.s được cung cấp tại đây. Bất cứ ai có thể vui lòng giải thích nội dung của tập tin main.s, đặc biệt là các phần .gcc_except_table.eh_frame theo từng dòng? (Hệ điều hành của tôi là Ubuntu 13.04 32-bit.) Cảm ơn!

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 sử dụng Itanium ABI, mà [mô tả cách xử lý ngoại lệ] (http://mentorembedded.github.io/cxx-abi/abi-eh.html#cxx-throw). –

+1

liên kết ở trên đã chết .. @KerrekSB – FaceBro

+1

@FaceBro: OK, có thể https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html –

Trả lời

5

.eh_frame bố cục được mô tả ngắn gọn trong LSB documentation. Ian Lance Taylor (tác giả của liên kết vàng) cũng đã thực hiện một số bài đăng trên blog on .eh_frame.gcc_except_table layout.

Để có mô tả giống như tham khảo hơn, hãy kiểm tra Recon 2012 slides (bắt đầu từ 37 trở lên).

EDIT: đây là cấu trúc nhận xét từ mẫu của bạn. Thứ nhất, .eh_table (một số bộ phận bỏ qua cho rõ ràng):

.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 

Tiếp theo, LSDA (khu vực dữ liệu ngôn ngữ cụ thể) tham chiếu bởi 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

Các Itanium ABI (mà cả hai gcc, kêu vang và một số người khác làm theo) xác định rằng xử lý ngoại lệ phải theo dõi Zero-Cost strategy.

Ý tưởng về chiến lược Zero-Cost là đẩy tất cả xử lý ngoại lệ trong các bảng phụ không được giữ trên đường dẫn thực thi chương trình chính (và do đó không phải truy xuất bộ đệm hướng dẫn). Các bảng này được lập chỉ mục theo điểm chương trình.

Hơn nữa, thông tin DWARF (thông tin gỡ lỗi thực sự) được sử dụng để thư giãn ngăn xếp. Chức năng này thường được cung cấp như một thư viện chẳng hạn như libunwind ví dụ, mã nguồn bị xáo trộn về lắp ráp (và do đó rất cụ thể về nền tảng).

Ưu điểm:

  • 0-chi phí để nhập try/catch khối (càng nhanh càng tốt nếu có không có)
  • 0 chi phí để có một tuyên bố throw trong một hàm (miễn là nó là không được thực hiện)

Nhược điểm:

  • chậm trong trường hợp ngoại lệ (10x chậm hơn so với một chiến lược if) vì các bảng phụ thường không trong bộ nhớ cache và sau đó có tính toán đắt tiền để chạy để biết được catch khoản thực sự trận đấu (dựa trên RTTI)

Nó là một chiến lược rất phổ biến thực hiện trên cả 32 bit và 64 bit nền tảng cho tất cả các trình biên dịch chính ... ngoại trừ MSVC 32 bit (nếu tôi nhớ chính xác).