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


概述
----

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] = ‘/0‘;
#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/n", 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)/n",
   (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/n", 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)/n",
         (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;
}


<script type="text/javascript"> </script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值