概述
----
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;
}