分析ELF程序的核心堆积(core dump)过程

概述


----




1)
当进程接收到某些异常信号导致退出时, 会在进程的当前目录下生成一个名为"core"的文件,


将进程


退出时的运行状态堆积在该文件中.




2) ELF
的堆积文件与应用程序和共享库的结构相同, 包括ELF文件头, 程序段描述符表和各段数据.


堆积


文件的第一个段为注释段, 它顺次堆积了处理机状态, 进程状态, 任务结构映象,


浮点寄存器映象. 其余


的各个段为可加载段, 依次描述了进程虚存空间中各个虚存块的映象.




代码


----




; arch/i386/kernel/signal.c:




int do_signal(struct pt_regs *regs, sigset_t *oldset)


{


       ...


                    ;
产生核心堆积的信号


                    case SIGQUIT: case SIGILL: case SIGTRAP:


                    case SIGABRT: case SIGFPE: case SIGSEGV:


                    case SIGBUS: case SIGSYS: case SIGXCPU: case SIGXFSZ:


                           if (do_coredump(signr, regs))


                                  exit_code |= 0x80;


                           /* FALLTHRU */


                    default:


                           sigaddset(¤t->pending.signal, signr);


                           recalc_sigpending(current);


                           current->flags |= PF_SIGNALED;


                           do_exit(exit_code);


                           /* NOTREACHED */


       ...


}




; fs/exec.c


int do_coredump(long signr, struct pt_regs * regs)


{


       struct linux_binfmt * binfmt;


       char corename[6+sizeof(current->comm)];


       struct file * file;


       struct inode * inode;




       lock_kernel();


       binfmt = current->binfmt;
取当前进程的程序加载器


       if (!binfmt || !binfmt->core_dump)


       7      goto fail;


       if (!current->dumpable || atomic_read(¤t->mm->mm_users) != 1)


              goto fail;
当该线程是进程的最后一个线程时,才能堆积


       current->dumpable = 0;


       if (current->rlim[RLIMIT_CORE].rlim_cur < binfmt->min_coredump)


              goto fail;
堆积文件的允许长度




       memcpy(corename,"core.", 5);


#if 0


       memcpy(corename+5,current->comm,sizeof(current->comm));


#else


       corename[4] = '';


#endif


       file = filp_open(corename, O_CREAT | 2 | O_NOFOLLOW, 0600);
堆积文件名不能为符号链接


       if (IS_ERR(file))


              goto fail;


       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;


       if (!binfmt->core_dump(signr, regs, file))


              goto close_fail;


       unlock_kernel();


       filp_close(file, NULL);


       return 1;




close_fail:


       filp_close(file, NULL);


fail:


       unlock_kernel();


       return 0;


}




; fs/binfmt_elf.c




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 long pr_flag;     /* flags */


       __kernel_uid_t      pr_uid;


       __kernel_gid_t      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 */


};




#define DUMP_WRITE(addr, nr)     


       if ((size += (nr)) > limit || !dump_write(file, (addr), (nr)))


              goto end_coredump;


#define DUMP_SEEK(off)    


       if (!dump_seek(file, (off)))


              goto end_coredump;




struct memelfnote


{


       const char *name;


       int type;


       unsigned int datasz;


       void *data;


};




ELF
堆积文件的生成


-----------------


static int elf_core_dump(long signr, struct pt_regs * regs, struct file * file)


{


       int has_dumped = 0;


       mm_segment_t fs;


       int segs;


       size_t size = 0;


       int i;


       struct vm_area_struct *vma;


       struct elfhdr elf;


       off_t offset = 0, dataoff;


       unsigned long limit = current->rlim[RLIMIT_CORE].rlim_cur;


       int numnote = 4;


       struct memelfnote notes[4];


       struct elf_prstatus prstatus;     /* NT_PRSTATUS */


       elf_fpregset_t fpu;        /* NT_PRFPREG */


       struct elf_prpsinfo psinfo;       /* NT_PRPSINFO */




       segs = current->mm->map_count;
进程的虚拟内存块数目




#ifdef DEBUG


       printk("elf_core_dump: %d segs %lu limit

", segs, limit);


#endif




       /* Set up header */


       memcpy(elf.e_ident, ELFMAG, SELFMAG);


       elf.e_ident[EI_CLASS] = ELF_CLASS;


       elf.e_ident[EI_DATA] = ELF_DATA;


       elf.e_ident[EI_VERSION] = EV_CURRENT;


       memset(elf.e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);




       elf.e_type = ET_CORE;
堆积文件类型


       elf.e_machine = ELF_ARCH;


       elf.e_version = EV_CURRENT;


       elf.e_entry = 0;


       elf.e_phoff = sizeof(elf);
程序头段偏移量


       elf.e_shoff = 0;


       elf.e_flags = 0;


       elf.e_ehsize = sizeof(elf); ELF
头大小


       elf.e_phentsize = sizeof(struct elf_phdr);
程序段描述符大小


       elf.e_phnum = segs+1;             /* Include notes */
程序段描述符数目


       elf.e_shentsize = 0;


       elf.e_shnum = 0;


       elf.e_shstrndx = 0;




       fs = get_fs();


       set_fs(KERNEL_DS);




       has_dumped = 1;


       current->flags |= PF_DUMPCORE;
正在进行核心堆积




       DUMP_WRITE(&elf, sizeof(elf));
写入ELF


       offset += sizeof(elf);    
作为文件指针          /* Elf header */


       offset += (segs+1) * sizeof(struct elf_phdr);   /* Program headers */


              ;
保留段描述符表空间, 移动到段数据区, 为注释段数据的开始


       /*


        * Set up the notes in similar form to SVR4 core dumps made


        * with info from their /proc.


        */


       memset(&psinfo, 0, sizeof(psinfo));


       memset(&prstatus, 0, sizeof(prstatus));




       notes[0].name = "CORE";   


       notes[0].type = NT_PRSTATUS;
建立处理器状态段


       notes[0].datasz = sizeof(prstatus);


       notes[0].data = &prstatus;


       prstatus.pr_info.si_signo = prstatus.pr_cursig = signr;
退出信号


       prstatus.pr_sigpend = current->pending.signal.sig[0];
活动的信号集


       prstatus.pr_sighold = current->blocked.sig[0];
阻塞的信号集


       psinfo.pr_pid = prstatus.pr_pid = current->pid;
进程号


       psinfo.pr_ppid = prstatus.pr_ppid = current->p_pptr->pid;
父进程号


       psinfo.pr_pgrp = prstatus.pr_pgrp = current->pgrp;
进程组号


       psinfo.pr_sid = prstatus.pr_sid = current->session;
登录号


       prstatus.pr_utime.tv_sec = CT_TO_SECS(current->times.tms_utime);
进程的用户执行时间


       prstatus.pr_utime.tv_usec = CT_TO_USECS(current->times.tms_utime);


       prstatus.pr_stime.tv_sec = CT_TO_SECS(current->times.tms_stime);
进程的内核执行时间


       prstatus.pr_stime.tv_usec = CT_TO_USECS(current->times.tms_stime);


       prstatus.pr_cutime.tv_sec = CT_TO_SECS(current->times.tms_cutime);


子进程的用户执行时间


       prstatus.pr_cutime.tv_usec = CT_TO_USECS(current->times.tms_cutime);


       prstatus.pr_cstime.tv_sec = CT_TO_SECS(current->times.tms_cstime);


子进程的内核执行时间


       prstatus.pr_cstime.tv_usec = CT_TO_USECS(current->times.tms_cstime);




       /*


        * This transfers the registers from regs into the standard


        * coredump arrangement, whatever that is.


        */


#ifdef ELF_CORE_COPY_REGS


       ELF_CORE_COPY_REGS(prstatus.pr_reg, regs)
保存处理器寄存器


#else


       if (sizeof(elf_gregset_t) != sizeof(struct pt_regs))


       {


              printk("sizeof(elf_gregset_t) (%ld) != sizeof(struct pt_regs) (%ld)

",


                    (long)sizeof(elf_gregset_t), (long)sizeof(struct pt_regs));


       }


       else


              *(struct pt_regs *)&prstatus.pr_reg = *regs;


#endif




#ifdef DEBUG


       dump_regs("Passed in regs", (elf_greg_t *)regs);


       dump_regs("prstatus regs", (elf_greg_t *)&prstatus.pr_reg);


#endif




       notes[1].name = "CORE";


       notes[1].type = NT_PRPSINFO;
建立进程信息段


       notes[1].datasz = sizeof(psinfo);


       notes[1].data = &psinfo;


       i = current->state ? ffz(~current->state) + 1 : 0;


       psinfo.pr_state = i;
进程状态号


       psinfo.pr_sname = (i < 0 || i > 5) ? '.' : "RSDZTD"[ i ];
进程状态名


       psinfo.pr_zomb = psinfo.pr_sname == 'Z';
进程是否已僵化


       psinfo.pr_nice = current->nice;
进程优先数


       psinfo.pr_flag = current->flags;
进程标志


       psinfo.pr_uid = NEW_TO_OLD_UID(current->uid);
用户号


       psinfo.pr_gid = NEW_TO_OLD_GID(current->gid);
用户组号


       {


              int i, len;




              set_fs(fs);




              len = current->mm->arg_end - current->mm->arg_start;


              if (len >= ELF_PRARGSZ)


                    len = ELF_PRARGSZ-1;


              copy_from_user(&psinfo.pr_psargs,


                          (const char *)current->mm->arg_start, len);
复制进程用户参数表


              for(i = 0; i < len; i++)


                    if (psinfo.pr_psargs[ i ] == 0)


                           psinfo.pr_psargs[ i ] = ' ';


              psinfo.pr_psargs[len] = 0;
转变成单一字符串




              set_fs(KERNEL_DS);


       }


       strncpy(psinfo.pr_fname, current->comm, sizeof(psinfo.pr_fname));
进程名




       notes[2].name = "CORE";


       notes[2].type = NT_TASKSTRUCT;
堆积任务结构映象


       notes[2].datasz = sizeof(*current);


       notes[2].data = current;




       /* Try to dump the FPU. */


       prstatus.pr_fpvalid = dump_fpu (regs, &fpu);


       if (!prstatus.pr_fpvalid)


       {


              numnote--;


       }


       else
如果进程使用了浮点处理器,则堆积FPU寄存器映象


       {


              notes[3].name = "CORE";


              notes[3].type = NT_PRFPREG;


              notes[3].datasz = sizeof(fpu);


              notes[3].data = &fpu;


       }


      


       /* Write notes phdr entry */


       {


              struct elf_phdr phdr;


              int sz = 0;




              for(i = 0; i < numnote; i++)


                    sz += notesize(¬es[ i ]);
注释段的总长度




              phdr.p_type = PT_NOTE;


              phdr.p_offset = offset;
注释段在ELF中的起始位置


              phdr.p_vaddr = 0;


              phdr.p_paddr = 0;


              phdr.p_filesz = sz;


              phdr.p_memsz = 0;


              phdr.p_flags = 0;


              phdr.p_align = 0;




              offset += phdr.p_filesz;


              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));


       }




       for(i = 0; i < numnote; i++)


              if (!writenote(¬es[ i ], file))
写入注释段的各个片段


                    goto end_coredump;




       set_fs(fs);




       DUMP_SEEK(dataoff);
页对齐




       for(vma = current->mm->mmap; vma != NULL; vma = vma->vm_next) {


              unsigned long addr;


              ;
写入各个堆积段数据


              if (!maydump(vma))


                    continue;


#ifdef DEBUG


              printk("elf_core_dump: writing %08lx %lx

", addr, len);


#endif


              for (addr = vma->vm_start;


                   addr < vma->vm_end;


                   addr += PAGE_SIZE) {


                    pgd_t *pgd;


                    pmd_t *pmd;


                    pte_t *pte;




                    pgd = pgd_offset(vma->vm_mm, addr);
取虚存块内地址所在的页目录


                    pmd = pmd_alloc(pgd, addr);
取地址所在的中间页目录


      


                    if (!pmd)


                           goto end_coredump;


                    pte = pte_alloc(pmd, addr);
取地址所在的页表项


                    if (!pte)


                           goto end_coredump;


                    if (!pte_present(*pte) &&


                        pte_none(*pte)) {
当页表项为空,表示该地址没有访问过


                           DUMP_SEEK (file->f_pos + PAGE_SIZE);


                    } else {


                           DUMP_WRITE((void*)addr, PAGE_SIZE);


                    }


              }


       }




       if ((off_t) file->f_pos != offset) {


              /* Sanity check */


              printk("elf_core_dump: file->f_pos (%ld) != offset (%ld)

",


                     (off_t) file->f_pos, offset);


       }




 end_coredump:


       set_fs(fs);


       return has_dumped;


}


static int writenote(struct memelfnote *men, struct file *file)


{


       struct elf_note en;
注释段片段的头




       en.n_namesz = strlen(men->name);


       en.n_descsz = men->datasz;


       en.n_type = men->type;




       DUMP_WRITE(&en, sizeof(en));
写入注释头


       DUMP_WRITE(men->name, en.n_namesz);
写入注释名


       /* XXX - cast from long long to long to avoid need for libgcc.a */


       DUMP_SEEK(roundup((unsigned long)file->f_pos, 4));
字边界上对齐


       DUMP_WRITE(men->data, men->datasz);
写入注释段数据


       DUMP_SEEK(roundup((unsigned long)file->f_pos, 4));    /* XXX */




       return 1;


}


测试某个内存块是否可以堆积


--------------------------


static inline int maydump(struct vm_area_struct *vma)


{


       if (!(vma->vm_flags & (VM_READ|VM_WRITE|VM_EXEC)))


              return 0;
能读写和执行的虚存块可堆积




       /* Do not dump I/O mapped devices! -DaveM */


       if(vma->vm_flags & VM_IO)


              return 0;
设备映射的内存不可堆积


#if 1


       if (vma->vm_flags & (VM_WRITE|VM_GROWSUP|VM_GROWSDOWN))


              return 1;
可写的并且能向上或向扩展的虚存块可堆积


       if (vma->vm_flags & (VM_READ|VM_EXEC|VM_EXECUTABLE|VM_SHARED))


              return 0;
共享的读写和执行的虚存块不可堆积


#endif


       return 1;


}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值