Coredump Format
Table of Contents
1. Coredump Format
coredump 是一个 elf 文件, 但它并不包含任何 section, 只包含了几个 segment, 用来保存寄存器, vma 等信息.
1.1. Format
它的格式大约是:
Ehdr struct Elf32_Ehdr { unsigned char e_ident[EI_NIDENT]; // ELF magic Elf32_Half e_type; // ET_CORE Elf32_Half e_machine; // EM_ARM ... Elf32_Word e_version; Elf32_Addr e_entry; // elf entry, coredump 不包含该信息, 所以为 0 Elf32_Off e_phoff; // 后面 program header 相对于文件的 offset, // 一般来说 program header 紧接在 elf header, 包含多个 program header entry, 所以它的值一般为 sizeof(Elf32_Ehdr) Elf32_Off e_shoff; // coredump 不包含 segment, 所以这里为 0 Elf32_Word e_flags; Elf32_Half e_ehsize; // elf header 的大小, 一般为 sizoef(ELf32_Ehdr) Elf32_Half e_phentsize; // 一个 program header entry 的大小, 一般为 sizeof(Elf32_Phdr) Elf32_Half e_phnum; // 包含多少个 program header entry (即包含多少个 segment) Elf32_Half e_shentsize; // section header 的大小, coredump 中为 0 Elf32_Half e_shnum; Elf32_Half e_shstrndx; } ------------------------------ Phdr // 第一个 program header entry, 一般类型为 PT_NOTE typedef struct { Elf32_Word p_type; // 对应的 segment 的类型, coredump 中一般包含一个 PT_NOTE 类型的 segment 和 N 个 PT_LOAD 类型的 segment // 其中 PT_NOTE segment 用来保存 note 信息 (寄存器, proc 信息, mapping 信息), PT_LOAD 用来 dump vma Elf32_Off p_offset; // 对应的 segment 相对于文件的 offset, 这个 offset 在 phdr 之后, -------------------------------------------------------------+ // 在生成 coredump 时, 对 p_offset 的处理是比较麻烦的地方, | // 在计算某个 phdr 的 offset 时, 需要知道前面所有的 segment 的 p_filesz, 以确定它的 offset | Elf32_Addr p_vaddr; // 对于 PT_LOAD 类型的 segment, p_vaddr 和 p_memsz 表示 vma 在内存中被映射的位置, PT_NOTE 类型的 segment 没有这个信息 | Elf32_Addr p_paddr; | Elf32_Word p_filesz; // 若 segment 有内容保存在 coredump 中, 则 p_offset 和 p_filesz 表示 segment 在文件中保存的位置, 使用 coredump 时 | // 可以根据这两个值从 coredump 中读取 segment 的内容. PT_NOTE 的内容一定会保存在 coredump 文件中, 但 PT_LOAD 的内容不 | // 一定, 例如, 一般来说, 以 VM_SHARED 方式映射 so 的 vma 是不需要保存在 coredump 中的, 因为可以从原始的 so 获得它的内容, | // 另外, madvise(DONT_DUMP) 的 vma 也不会保存在 coredump 中 | Elf32_Word p_memsz; | Elf32_Word p_flags; // 对于 PT_LOAD, 表示 vma 的权限 | Elf32_Word p_align; // PT_LOAD 类型的 segment header 需要是 PAGE_SIZE align, 所以 p_align 会是 PAGE_SIZE | } Elf32_Phdr; | // 第二个 program header entry, 一般类型为 PT_LOAD, 对应一个 vma 的信息 | // 这里要求 PAGE_SIZE 对齐 | typedef struct { | Elf32_Word p_type; | Elf32_Off p_offset; -------------------------------------------------------------------------------------------------+ | Elf32_Addr p_vaddr; | | Elf32_Addr p_paddr; | | Elf32_Word p_filesz; | | Elf32_Word p_memsz; | | Elf32_Word p_flags; | | Elf32_Word p_align; | | } Elf32_Phdr; | | ... | | ------------------------------ | | Segment | | note segment <---------------------------------------------------------------------------------------------------------------+--------------------------+ // note segment 包含多个 note, 每个 note 包含 Nhdr, name 和 desc | Elf32_Nhdr | typedef struct { | Elf32_Word n_namesz; // note 名字的长度, 一般来说均为 name 均为 "CORE", 所以长度为 5 | Elf32_Word n_descsz; // note desc 的长度 | Elf32_Word n_type; // note 类型, coredump 中一般会用来如下几个类型: NT_PRSTATUS, NT_PRPSINFO, NT_AUXV 和 NT_FILE | } Elf32_Nhdr; | name // 4 bytes 对齐 | desc // 4 bytes 对齐 | ... // 其它的 note | vma segment 1 <--------------------------------------------------------------------------------------------------------------+ vma segment 2 ...
1.2. Note
coredump 中最重要的内容是各种 note 信息
1.2.1. NT_PRSTATUS
NT_PRSTATUS 的 desc 部分的结构与 struct elf_prstatus 一致:
struct elf_prstatus { struct elf_siginfo pr_info; /* 产生 coredump 的信号 */ short int pr_cursig; unsigned long int pr_sigpend; unsigned long int pr_sighold; pid_t pr_pid; /* tid */ pid_t pr_ppid; pid_t pr_pgrp; pid_t pr_sid; struct timeval pr_utime; struct timeval pr_stime; struct timeval pr_cutime; struct timeval pr_cstime; elf_gregset_t pr_reg; /* coredump 时各个寄存器的值, 和 * sighandler 的 ucontext 中的值是一致 * 的 */ int pr_fpvalid; };
每个 prstatus 表示一个线程的状态, 所以多线程的进程产生的 coredump 会包含多个 prstatus
1.2.2. NT_PRPSINFO
NT_PRPSINFO 的 desc 部分由 struct elf_prpsinfo 表示:
struct elf_prpsinfo { char pr_state; char pr_sname; char pr_zomb; char pr_nice; unsigned long int pr_flag; unsigned int pr_uid; unsigned int pr_gid; int pr_pid, pr_ppid, pr_pgrp, pr_sid; char pr_fname[16]; /* exec 的名字 */ char pr_psargs[ELF_PRARGSZ]; /* args, 格式为 "argv1[space]argv2[space]..." */ };
1.2.3. NT_AUXV
NT_AUXV 表示 auxiliary vector, 使用 gdb info aux 查看, 其格式为 uintptr_t auxv[], 数组中依次保存着 key1, val1, key2, val2 …
1.2.4. NT_FILE
NT_FILE 保存 mapping 信息, 使用 gdb 的 info proc mapping 可以查看, 其格式为:
long count -- how many files are mapped long page_size -- units for file offset array of [COUNT] elements of long start long end long file offset followed by COUNT filenames in ASCII: "FILE1" NUL "FILE2" NUL...
1.3. 参考
- qemu elfload.c
- kernel elf_core_dump