为什么GCC编译的C程序需要.eh_框架部分?

为什么GCC编译的C程序需要.eh_框架部分?,c,assembly,exception-handling,x86,elf,C,Assembly,Exception Handling,X86,Elf,测试在32位x86 Linux上进行,带有gcc4.6.3 当使用gcc编译C程序并使用readelf检查节信息时, 我可以看到里面的.eh\u frame部分和.eh\u frame\u hdr部分 例如,下面是二进制程序Perlbench的部分信息 readelf -S perlbench There are 28 section headers, starting at offset 0x102e48: Section Headers: [Nr] Name T

测试在32位x86 Linux上进行,带有
gcc
4.6.3

当使用
gcc
编译
C
程序并使用
readelf
检查节信息时, 我可以看到里面的
.eh\u frame
部分和
.eh\u frame\u hdr
部分

例如,下面是二进制程序
Perlbench
的部分信息

readelf -S perlbench

There are 28 section headers, starting at offset 0x102e48:

Section Headers:
[Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
[ 0]                   NULL            00000000 000000 000000 00      0   0  0
[ 1] .interp           PROGBITS        08048154 000154 000013 00   A  0   0  1
[ 2] .note.ABI-tag     NOTE            08048168 000168 000020 00   A  0   0  4
[ 3] .note.gnu.build-i NOTE            08048188 000188 000024 00   A  0   0  4
[ 4] .gnu.hash         GNU_HASH        080481ac 0001ac 000044 04   A  5   0  4
[ 5] .dynsym           DYNSYM          080481f0 0001f0 0007b0 10   A  6   1  4
[ 6] .dynstr           STRTAB          080489a0 0009a0 0003d6 00   A  0   0  1
[ 7] .gnu.version      VERSYM          08048d76 000d76 0000f6 02   A  5   0  2
[ 8] .gnu.version_r    VERNEED         08048e6c 000e6c 0000a0 00   A  6   2  4
[ 9] .rel.dyn          REL             08048f0c 000f0c 000028 08   A  5   0  4
[10] .rel.plt          REL             08048f34 000f34 000388 08   A  5  12  4
[11] .init             PROGBITS        080492bc 0012bc 00002e 00  AX  0   0  4
[12] .plt              PROGBITS        080492f0 0012f0 000720 04  AX  0   0 16
[13] .text             PROGBITS        08049a10 001a10 0cf86c 00  AX  0   0 16
[14] .fini             PROGBITS        0811927c 0d127c 00001a 00  AX  0   0  4
[15] .rodata           PROGBITS        081192a0 0d12a0 017960 00   A  0   0 32
[16] .eh_frame_hdr     PROGBITS        08130c00 0e8c00 003604 00   A  0   0  4
[17] .eh_frame         PROGBITS        08134204 0ec204 01377c 00   A  0   0  4
[18] .ctors            PROGBITS        08148f0c 0fff0c 000008 00  WA  0   0  4
[19] .dtors            PROGBITS        08148f14 0fff14 000008 00  WA  0   0  4
[20] .jcr              PROGBITS        08148f1c 0fff1c 000004 00  WA  0   0  4
[21] .dynamic          DYNAMIC         08148f20 0fff20 0000d0 08  WA  6   0  4
[22] .got              PROGBITS        08148ff0 0ffff0 000004 04  WA  0   0  4
[23] .got.plt          PROGBITS        08148ff4 0ffff4 0001d0 04  WA  0   0  4
[24] .data             PROGBITS        081491e0 1001e0 002b50 00  WA  0   0 32
[25] .bss              NOBITS          0814bd40 102d30 002b60 00  WA  0   0 32
[26] .comment          PROGBITS        00000000 102d30 00002a 01  MS  0   0  1
[27] .shstrtab         STRTAB          00000000 102d5a 0000ec 00      0   0  1
据我所知,这两部分用于处理异常,它生成了描述如何展开堆栈的表


但是对于
C++
程序,他们使用
eh_框架
gcc_异常表
节来管理异常,那么为什么编译器要将
eh_框架
eh_框架
节放在
C
程序编译的
ELF
中呢,最初的原因主要是政治性的——添加基于矮化的展开(<代码> .EHFrase >)的人希望它是一个始终存在的特性,因此它可以用于实现除C++异常之外的所有类型的东西,包括:

  • backtrace()
  • \uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
  • 内置返回地址(n)
    ,用于
    n>0
  • pthread\u cleanup\u push
    ,根据
    \uuuu属性((\uuu cleanup\uuu(f)))实现
但是,如果您不需要这些东西,
.eh\u frame
的大小增加了15-30%,没有任何好处。您可以使用
-fno异步展开表
为单个翻译单元禁用生成
.eh_frame
,这在很大程度上消除了大小成本,尽管您仍然有一些剩余的
crtbegin.o
等。以后不能使用
strip
命令对其进行剥离;由于
.eh_frame
是程序加载部分中的一个部分(这是整个点),因此剥离它会以在运行时中断二进制文件的方式修改二进制文件。有关事物如何破碎的示例,请参见


请注意,DWARF表也用于调试,但出于此目的,它们不需要位于程序的可加载部分。使用
-fno异步展开表
不会中断调试,因为只要
-g
也传递给编译器,表仍然会生成;它们只是存储在二进制文件的一个单独的、不可加载的、可剥离的部分,
.debug_frame

x86-64 ABI
.eh frames
。这是为了允许在任何地方展开堆栈而添加的,因为在某些情况下,如在分析工具中,系统范围内都需要这样做。因此,在x86_64上,GCC默认为EH帧生成,并尝试使其处处精确,而在大多数其他ABI上,它仅在需要EH帧时默认为EH帧生成,并且仅在可能触发EH并因此展开的程序点处精确。我怀疑
GCC
也使用它们来实现
\u属性__((清理(…)
。可能重复“该问题中的参考?”我不明白你的意思。你指的是评论吗?为什么问题不同?我可能错了。用另一种方式结束是另一种可能:这稍微好一些,但另一种是更老的、艰难的选择。关于用户名,留给meta或twitter讨论。干杯。一个小小的更正:
crtbegin.o
包含没有CFI,至少在RedHat和Ubuntu上是这样。剩余的CFI很可能来自
\uu libc\u csu\u init
\uu libc\u csu\u fini
。这些不是在
crtbegin.o
中,而是在GLIBC
libc\u非共享的非共享部分。a
嗨,我想知道
eh\u框架的使用是否是GCC指定的专长ure?我试着用
clang
gcc
来编译一段C代码。gcc生成的
eh_框架
部分大约是
0x5cfc
,而
clang
@PeterCordes的部分只有
a4
:这是UB,即使不是,也几乎肯定会破坏C库的契约——大多数情况下都是这样使用回调的C库代码假定回调返回而不是<代码> LojMP(/Cord>),并且传播异常将等同于 LojJMP 。如果C++代码使用异常被称为C的回调,则回调需要捕获所有异常并将其转换为C调用方的错误返回。@彼得科尔德:“C++异常应该能够通过C传播,即使它是未定义的行为”,这是一个政治立场(我认为这是站不住脚的立场,因为我上面描述的原因——即使它被定义了,它也只能用于非常纯的C函数,而不需要清除错误)@PeterCordes:Re:text segment和实际与虚拟内存使用情况,你是对的,在带有mmu的系统上,仅仅因为文本被映射并不意味着它必须被加载。在这种情况下,所有浪费的都是存储空间(这仍然会让人恼火,因为你的Docker映像要上载30%,但不那么糟糕)但是,在没有mmu的系统上,整个映射文本必须在ram中。即使在有mmu的32位系统上,虚拟地址空间也是相当宝贵的资源;用不需要的垃圾填充它意味着
malloc
会更快失败。