为什么GCC编译的C程序需要.eh_框架部分?
测试在32位x86 Linux上进行,带有为什么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
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
中,而是在GLIBClibc\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
会更快失败。