Corefile文件格式(LinuxCoredump文件结构)

更新时间:2023-06-29 19:45:55 阅读: 评论:0

Corefile⽂件格式(LinuxCoredump⽂件结构)
[原创]转载请注明来源于CSDN _xiao。
Linux异常时会⽣成Coredump⽂件⽤于调试和分析,Coredump⽂件在嵌⼊式⼯作中对死机问题的帮助是⾮常⼤的。⽹上有很多⽣成core ⽂件的配置⽅法,但却很少涉及core⽂件的数据格式,这⾥通过linux的源码⾛读尝试解读Coredump⽂件的格式。
Coredump⽂件的整体格式是ELF格式,但在ELF格式定义中对Core⼦类别的定义是开放的,由各架构⾃⼰定义,因此Core⽂件在不同的体系中格式是有差别的。这⾥主要说明Linux下⽣成的Core⽂件,另外我们在⼯作中通常接触的指令架构是ARM和MIPS。
Coredump⽂件按ELF格式组织,不过它只有Executing View,没有Linking View,也就是只有Program Headers,没有Section Heders。所以⽤readelf命令查看时只能看到Segments⽽看不到Sections。异常产⽣时,Linux将死机进程的内存段的内容以Segment的形式保存到Coredump⽂件中(包括数据区,堆栈区,已分配的堆内容,以及死机时的帧结构数据等),对于代码段(属性为RX,即只读可执⾏的段),Linux不保存其内容,只保存代码段的地址信息;其它如线程信息、寄存器信息等辅助信息则保存在类型
为PT_NOTE的Segment段中。所以使⽤GDB⼯具查看时,需要原始的可执⾏⽂件来恢复代码区的内容。
Linux⽣成Coredump的相关代码在3.1.10\fs\binfmt_elf.c中。
从函数elf_core_dump看起。⾸先申请空间存放elfhdr⽂件头,然后通过current->mm->map_count得到当前进程已映射的内存段数量,计算core⽂件中需要⽣成的gment数量,这⾥会多保留⼀个gment供PT_NOTE使⽤。然后调⽤fill_note_info填充note段信息。
在fill_note_info⾥,先将所有的task放到info->thread_list链表中,然后遍历该链表,对每⼀个thread调⽤elf_dump_thread_status来保存该线程的状态。在elf_dump_thread_status中,先调⽤fill_prstatus获取线程状态,这包括收到的signal(对死机线程⽽⾔就是引
起coredump的信号了),pending和hold的signal,线程的pid,pgrp,ppid等各种数据。再调⽤elf_core_copy_task_regs获取线程的寄存器,这⾥对ARM和MIPS会调⽤各⾃的函数来保存寄存器,例如MIPS会调⽤3.1.10\arch\mips\kernel\binfmt_elfo32.c⾥
的elf32_core_copy_regs函数来获取MIPS寄存器的内容,再调⽤elf_core_copy_task_fpregs获取线程的浮点寄存器内容,在获取完信息后都会调⽤fill_note将这些信息放到类型为memelfnote的notes数组
中(后⾯会利⽤这个notes数组再将所有信息保存到PT_NOTE段中)。回到elf_core_dump,现在获得了所有的note,所以可以计算出整个note段的⼤⼩,以及计算所有Program Header的地址信息了。填充elf头,为所有的Program header申请空间,调⽤fill_elf_note_phdr将note的地址放到phdr4note头中,其类型设置
我的初心为PT_NOTE,然后遍历current->mm->mmap,为每⼀个已分配的内存段填充phdr,其类型设置为PT_LOAD,这⾥会调
⽤vma_dump_size检查vma->vm_flags标志来决定哪些内存的内容需要保存,对IO的映射内存是不保存的,共享内存,私有内存,和已写⼊的内存都会保存。处理完所有内存gment的头数据后,再调⽤memcpy_note依次将前⾯搜集的所有note的信息写⼊
到PT_NOTE的gment中,每个note信息⼀个个依次写⼊,按顺序排列在PT_NOTE段⾥,其头结构是elf32_note类型,即4字节
的name长度,4字节内容长度,4字节的类型描述,紧跟其后就name的内容和note的内容(注意这⾥name长度和内容长度会按4的倍数向上取整)。之后elf_core_dump开始初始化gzip,先调⽤compress_coredump将之前准备好的elfhdr,phdr4note,Program header,和PT_NOTE段的内容压缩写⼊,再重新遍历current->mm->mmap,对每⼀个内存段的每⼀页调⽤compress_coredump将其内
容压缩到⽂件中,每次压缩⼀个PAGE。最后写⼊gzip的尾部和校验和,整个coredump过程就结束了。
根据以上代码,Core⽂件的整体布局如下图1所⽰,它与普通ELF⽂件的差别是多了⼀个特定的PT_NOTE类型的段,⽤于存放线程信息和寄存器信息。
中国小康家庭标准
(图1)Core File⽂件的整体布局
从Coredump⽂件的结构看到,保存现场信息的时候没有对⼀个很关键的信息进⾏处理,那就是载⼊的动态链接库⽂件(*.so⽂件)的信息。我们的⼯程运⾏时通常都加载很多库,问题也常出现在这些动态库⾥,那么将加载的库正确地映射到对应的地址上就是⼀件⾄关重要的事情(包括程序运⾏时通过dlopen动态加载的库)。通常调试⼯具GDB载⼊Coredump⽂件时能把动态库加载到正确的地址上(前提是设置了正确的库查找路径),是怎么做到的呢?这涉及到ELF⽂件的动态加载和ld.so动态加载器的⼯作流程,关键信息已随主执⾏程序和ld.so动态库的数据区⼀起保存到了PT_NOTE段⾥,详细过程请看另⼀篇《GDB如何从Coredump⽂件恢复动态库信息》。
下⾯⽤⼀个实际例⼦看Coredump⽂件的数据结构。
图2是⼀个在mips平台上⽣成的Coredump⽂件的头部的⼗六进制数据。
(图2)⼀个实际的Coredump⽂件
⽂件开始是⼀个ELF Header,该头的结构定义为:
typedef struct elf32_hdr{
unsigned char e_ident[EI_NIDENT];    // 16字节标识
Elf32_Half e_type;
Elf32_Half e_machine;
Elf32_Word e_version;
Elf32_Addr e_entry;
Elf32_Off e_phoff;    // Program Headers的⽂件偏移地址
Elf32_Off e_shoff;
Elf32_Word e_flags;
Elf32_Half e_ehsize;    // ELF Header头结构的⼤⼩
Elf32_Half e_phentsize;    // 每个Program Header信息描述占⽤的⼤⼩
Elf32_Half e_phnum;    // Program Header的数量
Elf32_Half e_shentsize;
Elf32_Half e_shnum;
Elf32_Half e_shstrndx;
} Elf32_Ehdr;
根据该结构,解析Coredump⽂件的头信息如图3所⽰。
(图3)Coredump⽂件ELF头信息的解析
从图3的解析看到:e_type为0x04(ET_CORE),表⽰这是⼀个core⽂件;e_phoff为0x0034,表⽰Program Headers信息从⽂件的0x34地址开始;e_ehsize为0x34,表⽰此ELF Header⽂件头占⽤的字节为0x34;e_phentsize为0x20(32字节),表⽰每
个Program Header占⽤的⼤⼩为32字节;e_phnum为0x23,表⽰这个core⽂件共含有35个gment段;根据e_phoff信
息,Program Headers从0x34字节开始, 即紧跟在ELF Header之后开始。
再来分析Program Headers信息,根据ELF Header,每个Program Header占据的字节数为32字节,说明这是⼀个32位
的Elf32_Phdr(对应的还有64位的Elf64_Phdr,其size要⼤⼀些),Elf32_Phdr的定义如下:
typedef struct elf32_phdr{
Elf32_Word p_type;    // gment的类型
Elf32_Off p_offt;    // gment数据在⽂件中的偏移
Elf32_Addr p_vaddr;    // gment加载到内存中的虚拟地址
冬天问候语Elf32_Addr p_paddr;
Elf32_Word p_filesz;    // gment数据在⽂件中的数据⼤⼩
Elf32_Word p_memsz;    // gment数据在内存中占据的⼤⼩
Elf32_Word p_flags;    // gment的属性标志
Elf32_Word p_align;
国际分工} Elf32_Phdr;
根据这个定义,先分析第⼀个Program Header的信息,如图4所⽰。
by是什么意思
(图4)第1个Program Header信息的解析
从图4的解析看到:第1个Segment的p_type为0x04(PT_NOTE),表⽰这是⼀个描述note信息的段;p_offt为0x494,表⽰note信息段从⽂件的0x494字节开始;p_filesz为0x358,表⽰note信息共有856字节。由于note信息是辅助信息段,在原程序中并不存在于内存中,所以其p_vaddr,p_memsz,p_flags等均为0。
紧接着的是第⼆个Program Header信息(从地址0x34+0x20=0x54开始),其分析如图5所⽰。
(图5)第2个Program Header信息的解析
从图5的解析看到:第2个Segment的p_type为0x01(PT_LOAD),这是⼀个可加载的段;p_offt为0x1000,表⽰该段从⽂件
的0x1000字节开始;p_vaddr为0x400000,表⽰该段在原程序的内存中的虚拟地址为0x400000(对mips⽽⾔,此地址是主程序的加载地址);p_filesz为0,表⽰该段在⽂件中的⼤⼩为0,如前所述,由于该段是⼀个代码段,所以Core⽂件并没有保存其内容,因此该段在Core⽂件中的数据长度为0,调试时需要原程序的ELF档加载到0x400000地址才能分析此程序;p_memsz为0x10000,表⽰该段在原内存中占据4096的⼤⼩(即该段占据0x400000~0x40FFFF的地址空间);p_flags为0x05,即PF_R | PF_X,表⽰该段是⼀个只读可执⾏的代码段。
按如上⽅法继续下去就可以将所有的段信息解析出来。
实际中,我们不需要⾃⼰去解析⽂件,通过⼯具如readelf或objdump等都可以轻松地将上⾯的信息解析出来。图6是上⾯Coredump⽂件对应的readelf运⾏结果。
(图6)readelf读取Coredump⽂件小博士
再来尝试分析PT_NOTE段的内容。PT_NOTE段的内容与体系架构有关,如前所述,它是由许多note信息段依次拼接⽽成的。每个note信息段的头部定义为:
typedef struct elf32_note {
Elf32_Word n_namesz; /* Name size */
Elf32_Word n_descsz; /* Content size */
Elf32_Word n_type;  /* Content type */
} Elf32_Nhdr;
根据fill_note_info函数,第⼀个note信息是info->prstatus信息,在mips中,该结构体定义为(见⽂件binfmt_elfo32.c):
#define elf_prstatus elf_prstatus32
struct elf_prstatus32
{
struct elf_siginfo pr_info; /* Info associated with signal */
short pr_cursig;  /* Current signal */
unsigned int pr_sigpend; /* Set of pending signals */
unsigned int pr_sighold; /* Set of held signals */
pid_t pr_pid;
pid_t pr_ppid;
新华人寿招聘
职业技能等级证书查询pid_t pr_pgrp;
pid_t pr_sid;
struct compat_timeval pr_utime; /* Ur time */
struct compat_timeval pr_stime; /* System time */
struct compat_timeval pr_cutime;/* Cumulative ur time */
struct compat_timeval pr_cstime;/* Cumulative system time */
elf_gregt_t pr_reg; /* GP registers */
int pr_fpvalid;  /* True if math co-processor being ud.  */
};
其中的elf_siginfo结构定义为:
struct elf_siginfo
{
int si_signo;  /* signal number */
int si_code;  /* extra code */
int si_errno;  /* errno */
};
寄存器信息的结构定义为:
#define ELF_NGREG 45
typedef elf_greg_t elf_gregt_t[ELF_NGREG];
另外根据elf32_core_copy_regs函数,在elf_gregt_t寄存器数组中,前6个寄存器都是0(这⼏个只⽤于64位mips),从索引6的寄存器开始,依次存放R0到R31的寄存器数据。
所以按照上⾯的结构解析第⼀个note信息如图7所⽰。第⼀个note信息从地址0x494开始,其中name字段长度为5个字节,由于按4的倍数向上取整,所以实际占据了8个字节;信息内容的长度为256,即从0x4A8到0x5A7都是其内容,注意其中pr_cursig由于结构体的字节对齐被填充了2个字节。
根据图7的解析,死机时,R1寄存器的值为0x2ad2b007,R2寄存器的值为0,R3寄存器的值为0x2ac21840……,死机时线程收到的信号为11,即gment fault,表⽰段错误,⼀般是操作⾮法内存地址导致的,这些都与GDB解析的结果相符。
从地址0x5A8开始是第⼆个note的信息,按同样的⽅法继续解析,见图7的下半部分。

本文发布于:2023-06-29 19:45:55,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/89/1060538.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

上一篇:elf文件格式
标签:信息   内容   寄存器   内存   保存   地址
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图