我想知道GCC如何为C ++程序实现异常处理。我无法在网上找到一篇易于理解且不言自明的文章(尽管Visual C ++有很多这样的文章)。我所知道的是GCC的实现被称为DWARF异常处理。
我编写了一个小型C ++程序,并使用以下命令将其转换为程序集:
g ++ 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
答案 0 :(得分:15)
Itanium ABI(gcc,clang和其他一些人都遵循)指定异常处理应遵循Zero-Cost strategy。
零成本策略的想法是推动未在主程序执行路径上保留的侧表中的所有异常处理(因此不会破坏指令缓存)。这些表由程序点索引。
此外,DWARF信息(实际上是调试信息)用于展开堆栈。此功能通常作为库提供,例如libunwind,源代码对程序集很不敏感(因此非常特定于平台)。
优点:
try
/ catch
区块的费用为0(如果没有,则为最快)throw
语句的成本为0(只要不采用)缺点:
if
策略慢10倍),因为边表通常不在缓存中,然后运行昂贵的计算以了解哪个catch
子句实际匹配(基于在RTTI)对于所有主流编译器而言,它是32位和64位平台上非常流行的策略实现...除了MSVC 32位(如果我没记错的话)。
答案 1 :(得分:5)
.eh_frame
布局在LSB documentation中进行了简要介绍。 Ian Lance Taylor(黄金链接器的作者)也撰写了一些博文on .eh_frame
和.gcc_except_table
layout。
有关类似参考的说明,请查看我的Recon 2012 slides(从37左右开始)。
编辑:这是您示例中的评论结构。首先,.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
接下来,FDE 3引用的LSDA(语言特定数据区域):
.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