コアダンプファイル


コアファイルはプロセスが起動したローダ下の、コールバック関数で作成されます。従って実行ファーマットによってその扱いは異なります。ここでは一般的な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
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(&notes[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,
};

最終更新 2013/04/29 20:35:01 - north
(2013/04/29 20:35:01 作成)


検索

アクセス数
3689284
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。