コアダンプファイル
コアファイルはプロセスが起動したローダ下の、コールバック関数で作成されます。従って実行ファーマットによってその扱いは異なります。ここでは一般的なELFのコアファイルを見てみました。
ELFプロセスが吐き出したコアファイルは、ELF形式で作成されます。ELF形式とはヘッダセクション:プログラムセクション:セグメントセクションで構成されるファイルです。ヘッダセクションはELFファイルそのもの情報、プログラムセクションはメモリーにロードされる内容、セグメントセクションはプログラムセクションから参照されるデータ群が収められています。
なお、コアファイルは実行するためのものでないため、セグメントセクションはありません。ヘッダセクションとプログラムセクションのELFファイルとなります。
下は、readelfコマンドで見たコアファイルです。ELF Headerの注目すべき項目は、Number of program headers:の15と、Number of section headersの0と言うところです。
コアファイルでは、Program HeadersのTypeは、NOTEとLOAD(ELFが定義するタイプです。)の2つだけです。NOTE内はCORE/LINUXと言う名(これはELFコアファイルが定義するタイプです。)カテゴリで、ここには、CPUのレジスタを含めたプロセス情報が設定されており、LOADにプロセスの実行メモリーイメージが設定されています。
[root@localhost ~]# readelf -a core
コアダンプはdo_coredump()で行われます。current->binfmtはプロセスが起動したローダとなり、ELFならstruct linux_binfmt elf_formatが設定されて、elf_format->core_dump()をコールする事で、コアファイルが作成されます。
まず、 down_write(&mm->mmap_sem)でセマフォを取得し、 init_completion(&mm->core_done)で完了通知を仕掛けます。これで、コアファイル作成中にタスク構造体が削除されなく、complete_all(&mm->core_done)のタイミングで削除されるようにするため(たぶん)。coredump_wait(mm)は、先のdown_write(&mm->mmap_sem)でウエイトしている他のスレッドを、まず動作させ、最新のメモリーイメージがコアファイルとして作成するようにしています(たぶん)。
format_corename()で取得したファイル名を作成し、取得したファイルの種々のチェック(書込可とか/リンクされてないとか)を行った後、binfmt->core_dump(signr, regs, file)でコアを作成します。
で、NOTEにCOREとしてNT_PRSTATUS/NT_PRPSINFO/NT_TASKSTRUCT/NT_AUXV/NT_PRFPREGと、LINUXとしてNT_PRXFPREGを設定した後、まず、DUMP_WRITE(elf)でELFヘッダーを書き込みます。
次に、NOTEの内容を含むプログラムヘッダーエントリーを出力して、PT_LOADでcurrent->mm->mmapのプロセスアドレス空間をページサイズ単位で書き出し、最後にNOTEの内容を課k出します。もし、スレッドがあれば以降に、同じようにスレッドの情報も書き出します。
ELFプロセスが吐き出したコアファイルは、ELF形式で作成されます。ELF形式とはヘッダセクション:プログラムセクション:セグメントセクションで構成されるファイルです。ヘッダセクションはELFファイルそのもの情報、プログラムセクションはメモリーにロードされる内容、セグメントセクションはプログラムセクションから参照されるデータ群が収められています。
なお、コアファイルは実行するためのものでないため、セグメントセクションはありません。ヘッダセクションとプログラムセクションのELFファイルとなります。
下は、readelfコマンドで見たコアファイルです。ELF Headerの注目すべき項目は、Number of program headers:の15と、Number of section headersの0と言うところです。
コアファイルでは、Program HeadersのTypeは、NOTEとLOAD(ELFが定義するタイプです。)の2つだけです。NOTE内はCORE/LINUXと言う名(これはELFコアファイルが定義するタイプです。)カテゴリで、ここには、CPUのレジスタを含めたプロセス情報が設定されており、LOADにプロセスの実行メモリーイメージが設定されています。
[root@localhost ~]# readelf -a core
ELF Header: Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: CORE (Core file) Machine: Intel 80386 Version: 0x1 Entry point address: 0x0 Start of program headers: 52 (bytes into file) Start of section headers: 0 (bytes into file) Flags: 0x0 Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 15 Size of section headers: 0 (bytes) Number of section headers: 0 Section header string table index: 0 Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align NOTE 0x000214 0x00000000 0x00000000 0x0022c 0x00000 0 LOAD 0x001000 0x08048000 0x00000000 0x01000 0x01000 R E 0x1000 LOAD 0x002000 0x08049000 0x00000000 0x01000 0x01000 RW 0x1000 LOAD 0x003000 0x4ea28000 0x00000000 0x01000 0x21000 R E 0x1000 LOAD 0x004000 0x4ea49000 0x00000000 0x01000 0x01000 R 0x1000 LOAD 0x005000 0x4ea4a000 0x00000000 0x01000 0x01000 RW 0x1000 LOAD 0x006000 0x4ea51000 0x00000000 0x01000 0x1a7000 R E 0x1000 LOAD 0x007000 0x4ebf8000 0x00000000 0x00000 0x01000 0x1000 LOAD 0x007000 0x4ebf9000 0x00000000 0x02000 0x02000 R 0x1000 LOAD 0x009000 0x4ebfb000 0x00000000 0x01000 0x01000 RW 0x1000 LOAD 0x00a000 0x4ebfc000 0x00000000 0x03000 0x03000 RW 0x1000 LOAD 0x00d000 0xb7750000 0x00000000 0x01000 0x01000 RW 0x1000 LOAD 0x00e000 0xb7763000 0x00000000 0x02000 0x02000 RW 0x1000 LOAD 0x010000 0xb7765000 0x00000000 0x01000 0x01000 R E 0x1000 LOAD 0x011000 0xbf7fe000 0x00000000 0x22000 0x22000 RW 0x1000 Notes at offset 0x00000214 with length 0x0000022c: Owner Data size Description CORE 0x00000090 NT_PRSTATUS (prstatus structure) CORE 0x0000007c NT_PRPSINFO (prpsinfo structure) CORE 0x000000a0 NT_AUXV (auxiliary vector) LINUX 0x00000030 Unknown note type: (0x00000200)
コアダンプはdo_coredump()で行われます。current->binfmtはプロセスが起動したローダとなり、ELFならstruct linux_binfmt elf_formatが設定されて、elf_format->core_dump()をコールする事で、コアファイルが作成されます。
まず、 down_write(&mm->mmap_sem)でセマフォを取得し、 init_completion(&mm->core_done)で完了通知を仕掛けます。これで、コアファイル作成中にタスク構造体が削除されなく、complete_all(&mm->core_done)のタイミングで削除されるようにするため(たぶん)。coredump_wait(mm)は、先のdown_write(&mm->mmap_sem)でウエイトしている他のスレッドを、まず動作させ、最新のメモリーイメージがコアファイルとして作成するようにしています(たぶん)。
format_corename()で取得したファイル名を作成し、取得したファイルの種々のチェック(書込可とか/リンクされてないとか)を行った後、binfmt->core_dump(signr, regs, file)でコアを作成します。
#define ELF_EXEC_PAGESIZE 4096 static struct linux_binfmt elf_format = { .module = THIS_MODULE, .load_binary = load_elf_binary, .load_shlib = load_elf_library, .core_dump = elf_core_dump, .min_coredump = ELF_EXEC_PAGESIZE }; int do_coredump(long signr, int exit_code, struct pt_regs * regs) { char corename[CORENAME_MAX_SIZE + 1]; struct mm_struct *mm = current->mm; struct linux_binfmt * binfmt; struct inode * inode; struct file * file; int retval = 0; lock_kernel(); binfmt = current->binfmt; if (!binfmt || !binfmt->core_dump) goto fail; down_write(&mm->mmap_sem); if (!mm->dumpable) { up_write(&mm->mmap_sem); goto fail; } mm->dumpable = 0; init_completion(&mm->core_done); current->signal->group_exit = 1; current->signal->group_exit_code = exit_code; coredump_wait(mm); if (current->rlim[RLIMIT_CORE].rlim_cur < binfmt->min_coredump) goto fail_unlock; format_corename(corename, core_pattern, signr); file = filp_open(corename, O_CREAT | 2 | O_NOFOLLOW, 0600); if (IS_ERR(file)) goto fail_unlock; inode = file->f_dentry->d_inode; if (inode->i_nlink > 1) goto close_fail; /* multiple links - don't dump */ if (d_unhashed(file->f_dentry)) goto close_fail; if (!S_ISREG(inode->i_mode)) goto close_fail; if (!file->f_op) goto close_fail; if (!file->f_op->write) goto close_fail; if (do_truncate(file->f_dentry, 0) != 0) goto close_fail; retval = binfmt->core_dump(signr, regs, file); current->signal->group_exit_code |= 0x80; close_fail: filp_close(file, NULL); fail_unlock: complete_all(&mm->core_done); fail: unlock_kernel(); return retval; }ELFのコアファイルの実際の処理となります。まず、ダンプする内容のバッファを確保して、このプロセスにスレッドがあれば、elf_dump_thread_status()でスレッドの状態(タスク構造体とか、レジスタ)を、thread_listにリストします。
で、NOTEにCOREとしてNT_PRSTATUS/NT_PRPSINFO/NT_TASKSTRUCT/NT_AUXV/NT_PRFPREGと、LINUXとしてNT_PRXFPREGを設定した後、まず、DUMP_WRITE(elf)でELFヘッダーを書き込みます。
次に、NOTEの内容を含むプログラムヘッダーエントリーを出力して、PT_LOADでcurrent->mm->mmapのプロセスアドレス空間をページサイズ単位で書き出し、最後にNOTEの内容を課k出します。もし、スレッドがあれば以降に、同じようにスレッドの情報も書き出します。
static int elf_core_dump(long signr, struct pt_regs * regs, struct file * file) { #define NUM_NOTES 6 int has_dumped = 0; mm_segment_t fs; int segs; size_t size = 0; int i; struct vm_area_struct *vma; struct elfhdr *elf = NULL; off_t offset = 0, dataoff; unsigned long limit = current->rlim[RLIMIT_CORE].rlim_cur; int numnote; struct memelfnote *notes = NULL; struct elf_prstatus *prstatus = NULL; /* NT_PRSTATUS */ struct elf_prpsinfo *psinfo = NULL; /* NT_PRPSINFO */ struct task_struct *g, *p; LIST_HEAD(thread_list); struct list_head *t; elf_fpregset_t *fpu = NULL; #ifdef ELF_CORE_COPY_XFPREGS elf_fpxregset_t *xfpu = NULL; #endif int thread_status_size = 0; elf_addr_t *auxv; elf = kmalloc(sizeof(*elf), GFP_KERNEL); if (!elf) goto cleanup; prstatus = kmalloc(sizeof(*prstatus), GFP_KERNEL); if (!prstatus) goto cleanup; psinfo = kmalloc(sizeof(*psinfo), GFP_KERNEL); if (!psinfo) goto cleanup; notes = kmalloc(NUM_NOTES * sizeof(struct memelfnote), GFP_KERNEL); if (!notes) goto cleanup; fpu = kmalloc(sizeof(*fpu), GFP_KERNEL); if (!fpu) goto cleanup; #ifdef ELF_CORE_COPY_XFPREGS xfpu = kmalloc(sizeof(*xfpu), GFP_KERNEL); if (!xfpu) goto cleanup; #endif /* capture the status of all other threads */ if (signr) { read_lock(&tasklist_lock); do_each_thread(g,p) if (current->mm == p->mm && current != p) { int sz = elf_dump_thread_status(signr, p, &thread_list); if (!sz) { read_unlock(&tasklist_lock); goto cleanup; } else thread_status_size += sz; } while_each_thread(g,p); read_unlock(&tasklist_lock); } /* now collect the dump for the current */ memset(prstatus, 0, sizeof(*prstatus)); fill_prstatus(prstatus, current, signr); elf_core_copy_regs(&prstatus->pr_reg, regs); segs = current->mm->map_count; #ifdef ELF_CORE_EXTRA_PHDRS segs += ELF_CORE_EXTRA_PHDRS; #endif /* Set up header */ fill_elf_header(elf, segs+1); /* including notes section */ has_dumped = 1; current->flags |= PF_DUMPCORE; fill_note(notes +0, "CORE", NT_PRSTATUS, sizeof(*prstatus), prstatus); fill_psinfo(psinfo, current->group_leader, current->mm); fill_note(notes +1, "CORE", NT_PRPSINFO, sizeof(*psinfo), psinfo); fill_note(notes +2, "CORE", NT_TASKSTRUCT, sizeof(*current), current); numnote = 3; auxv = (elf_addr_t *) current->mm->saved_auxv; i = 0; do i += 2; while (auxv[i - 2] != AT_NULL); fill_note(¬es[numnote++], "CORE", NT_AUXV, i * sizeof (elf_addr_t), auxv); /* Try to dump the FPU. */ if ((prstatus->pr_fpvalid = elf_core_copy_task_fpregs(current, regs, fpu))) fill_note(notes + numnote++, "CORE", NT_PRFPREG, sizeof(*fpu), fpu); #ifdef ELF_CORE_COPY_XFPREGS if (elf_core_copy_task_xfpregs(current, xfpu)) fill_note(notes + numnote++, "LINUX", NT_PRXFPREG, sizeof(*xfpu), xfpu); #endif fs = get_fs(); set_fs(KERNEL_DS); DUMP_WRITE(elf, sizeof(*elf)); offset += sizeof(*elf); /* Elf header */ offset += (segs+1) * sizeof(struct elf_phdr); /* Program headers */ /* Write notes phdr entry */ { struct elf_phdr phdr; int sz = 0; for (i = 0; i < numnote; i++) sz += notesize(notes + i); sz += thread_status_size; fill_elf_note_phdr(&phdr, sz, offset); offset += sz; DUMP_WRITE(&phdr, sizeof(phdr)); } /* Page-align dumped data */ dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE); /* Write program headers for segments dump */ for (vma = current->mm->mmap; vma != NULL; vma = vma->vm_next) { struct elf_phdr phdr; size_t sz; sz = vma->vm_end - vma->vm_start; phdr.p_type = PT_LOAD; phdr.p_offset = offset; phdr.p_vaddr = vma->vm_start; phdr.p_paddr = 0; phdr.p_filesz = maydump(vma) ? sz : 0; phdr.p_memsz = sz; offset += phdr.p_filesz; phdr.p_flags = vma->vm_flags & VM_READ ? PF_R : 0; if (vma->vm_flags & VM_WRITE) phdr.p_flags |= PF_W; if (vma->vm_flags & VM_EXEC) phdr.p_flags |= PF_X; phdr.p_align = ELF_EXEC_PAGESIZE; DUMP_WRITE(&phdr, sizeof(phdr)); } #ifdef ELF_CORE_WRITE_EXTRA_PHDRS ELF_CORE_WRITE_EXTRA_PHDRS; #endif /* write out the notes section */ for (i = 0; i < numnote; i++) if (!writenote(notes + i, file)) goto end_coredump; /* write out the thread status notes section */ list_for_each(t, &thread_list) { struct elf_thread_status *tmp = list_entry(t, struct elf_thread_status, list); for (i = 0; i < tmp->num_notes; i++) if (!writenote(&tmp->notes[i], file)) goto end_coredump; } DUMP_SEEK(dataoff); for (vma = current->mm->mmap; vma != NULL; vma = vma->vm_next) { unsigned long addr; if (!maydump(vma)) continue; for (addr = vma->vm_start; addr < vma->vm_end; addr += PAGE_SIZE) { struct page* page; struct vm_area_struct *vma; if (get_user_pages(current, current->mm, addr, 1, 0, 1, &page, &vma) <= 0) { DUMP_SEEK (file->f_pos + PAGE_SIZE); } else { if (page == ZERO_PAGE(addr)) { DUMP_SEEK (file->f_pos + PAGE_SIZE); } else { void *kaddr; flush_cache_page(vma, addr); kaddr = kmap(page); DUMP_WRITE(kaddr, PAGE_SIZE); kunmap(page); } page_cache_release(page); } } } #ifdef ELF_CORE_WRITE_EXTRA_DATA ELF_CORE_WRITE_EXTRA_DATA; #endif if ((off_t) file->f_pos != offset) { /* Sanity check */ printk("elf_core_dump: file->f_pos (%ld) != offset (%ld)\n", (off_t) file->f_pos, offset); } end_coredump: set_fs(fs); cleanup: while(!list_empty(&thread_list)) { struct list_head *tmp = thread_list.next; list_del(tmp); kfree(list_entry(tmp, struct elf_thread_status, list)); } kfree(elf); kfree(prstatus); kfree(psinfo); kfree(notes); kfree(fpu); #ifdef ELF_CORE_COPY_XFPREGS kfree(xfpu); #endif return has_dumped; #undef NUM_NOTES }
補足
要は、下記のCPUに関する情報、およびカーネルのプロセス情報、そしてプロセスの実行メモリーイメージを書き込んだファイルと言うこと、コアファイルト言う物は、こんな感じのファイルだと。言う事でした。struct elf_prstatus { #if 0 long pr_flags; /* XXX Process flags */ short pr_why; /* XXX Reason for process halt */ short pr_what; /* XXX More detailed reason */ #endif struct elf_siginfo pr_info; /* Info associated with signal */ short pr_cursig; /* Current signal */ unsigned long pr_sigpend; /* Set of pending signals */ unsigned long pr_sighold; /* Set of held signals */ #if 0 struct sigaltstack pr_altstack; /* Alternate stack info */ struct sigaction pr_action; /* Signal action for current sig */ #endif pid_t pr_pid; pid_t pr_ppid; pid_t pr_pgrp; pid_t pr_sid; struct timeval pr_utime; /* User time */ struct timeval pr_stime; /* System time */ struct timeval pr_cutime; /* Cumulative user time */ struct timeval pr_cstime; /* Cumulative system time */ #if 0 long pr_instr; /* Current instruction */ #endif elf_gregset_t pr_reg; /* GP registers */ int pr_fpvalid; /* True if math co-processor being used. */ }; struct elf_prpsinfo { char pr_state; /* numeric process state */ char pr_sname; /* char for pr_state */ char pr_zomb; /* zombie */ char pr_nice; /* nice val */ unsigned int pr_flag; /* flags */ __u16 pr_uid; __u16 pr_gid; pid_t pr_pid, pr_ppid, pr_pgrp, pr_sid; /* Lots missing */ char pr_fname[16]; /* filename of executable */ char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */ }; struct user_fxsr_struct { unsigned short cwd; unsigned short swd; unsigned short twd; unsigned short fop; long fip; long fcs; long foo; long fos; long mxcsr; long reserved; long st_space[32]; /* 8*16 bytes for each FP-reg = 128 bytes */ long xmm_space[32]; /* 8*16 bytes for each XMM-reg = 128 bytes */ long padding[56]; };ちなみに、binfmt flatのコアダンプは、current->comm, current->pid, (int) signrをprintkするだけで、binfmt miscはコアはありません。
static struct linux_binfmt flat_format = { .module = THIS_MODULE, .load_binary = load_flat_binary, .core_dump = flat_core_dump, .min_coredump = PAGE_SIZE }; static int flat_core_dump(long signr, struct pt_regs * regs, struct file *file) { printk("Process %s:%d received signr %d and should have core dumped\n", current->comm, current->pid, (int) signr); return(1); } static struct linux_binfmt misc_format = { .module = THIS_MODULE, .load_binary = load_misc_binary, };