为什么,Linux,下的,backtrace,可以在不使用栈帧的情况下正常工作?
- 网络资讯
- 2024-09-26 00:01:01
gcc 编译出来的 ELF 会包含一个 .eh_frame 节,里面包含了编译过程中产生的一张表 call frame table:
$ readelf -wF ./a.out ... 000000c0 0000000000000044 000000c4 FDE cie=00000000 pc=0000000000001260..00000000000012c5 LOC CFA rbx rbp r12 r13 r14 r15 ra 0000000000001260 rsp+8 u u u u u u c-8 0000000000001266 rsp+16 u u u u u c-16 c-8 000000000000126f rsp+24 u u u u c-24 c-16 c-8 0000000000001274 rsp+32 u u u c-32 c-24 c-16 c-8 0000000000001279 rsp+40 u u c-40 c-32 c-24 c-16 c-8 000000000000127d rsp+48 u c-48 c-40 c-32 c-24 c-16 c-8 0000000000001285 rsp+56 c-56 c-48 c-40 c-32 c-24 c-16 c-8 000000000000128c rsp+64 c-56 c-48 c-40 c-32 c-24 c-16 c-8 00000000000012ba rsp+56 c-56 c-48 c-40 c-32 c-24 c-16 c-8 00000000000012bb rsp+48 c-56 c-48 c-40 c-32 c-24 c-16 c-8 00000000000012bc rsp+40 c-56 c-48 c-40 c-32 c-24 c-16 c-8 00000000000012be rsp+32 c-56 c-48 c-40 c-32 c-24 c-16 c-8 00000000000012c0 rsp+24 c-56 c-48 c-40 c-32 c-24 c-16 c-8 00000000000012c2 rsp+16 c-56 c-48 c-40 c-32 c-24 c-16 c-8 00000000000012c4 rsp+8 c-56 c-48 c-40 c-32 c-24 c-16 c-8这一节会和 .rodata 一起被加载进内存:
$ readelf -l ./a.out ... Section to Segment mapping: 段节... 00 01 .interp 02 .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt 03 .init .plt .text .fini 04 .rodata .eh_frame_hdr .eh_frame 05 .init_array .fini_array .dynamic .got .got.plt .data .bss 06 .dynamic 07 .note.gnu.property 08 .note.gnu.build-id .note.ABI-tag 09 .note.gnu.property 10 .eh_frame_hdr 11 12 .init_array .fini_array .dynamic .got这张表中的每一个条目都记载了某一个特定的 pc 位置的 CFA (canonical frame address,在 x86_64 上就是上一级调用者的 rsp),callee-saved 寄存器保存的位置(rbx, rbp, r12-15)和返回地址的位置(ra)
比如说这一行
LOC CFA rbx rbp r12 r13 r14 r15 ra 00000000000012ba rsp+56 c-56 c-48 c-40 c-32 c-24 c-16 c-8意思就是程序执行到 pc=0x12ba(应该是可执行文件加载地址+0x12ba) 的时候,可以通过当前的 rsp+56 计算出 CFA (上一级调用者的 rsp),然后通过 CFA - 8,就可以找到返回地址在栈上的位置 ra,然后把 *ra 当成新的 pc,CFA 当成新的 rsp,在表里查找新 pc 对应的条目就又可以找到再上一层的返回地址
整个 backtrace 的过程就是先获得 pc,dl_iterate_phdr 看这个 pc 落在哪个 so 还是可执行文件中,读对应 ELF 的 .eh_frame 里这个 pc 的条目,获得各种寄存器的位置,然后一直重复下去
如果强行把 .eh_frame 去除掉,backtrace(3) 就不工作了
$ strip -R .eh_frame ./a.out $ ./a.out LINUX_BACKTRACE: current func and offset: ./a.out(bt2+0x2c) [0x55c1cdb85195]为什么,Linux,下的,backtrace,可以在不使用栈帧的情况下正常工作?由讯客互联网络资讯栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“为什么,Linux,下的,backtrace,可以在不使用栈帧的情况下正常工作?”