modified: arch/x86/kernel/gencore.c modified: arch/x86/kernel/include/elfcore.h modified: kernel/include/process.h modified: kernel/process.c
372 lines
8.9 KiB
C
372 lines
8.9 KiB
C
#include <ihk/debug.h>
|
|
#include <kmalloc.h>
|
|
#include <cls.h>
|
|
#include <list.h>
|
|
#include <process.h>
|
|
#include <string.h>
|
|
#include <elfcore.h>
|
|
|
|
#define align32(x) ((((x) + 3) / 4) * 4)
|
|
|
|
#define DEBUG_PRINT_GENCORE
|
|
|
|
#ifdef DEBUG_PRINT_GENCORE
|
|
#define dkprintf(...) kprintf(__VA_ARGS__)
|
|
#define ekprintf(...) kprintf(__VA_ARGS__)
|
|
#else
|
|
#define dkprintf(...)
|
|
#define ekprintf(...) kprintf(__VA_ARGS__)
|
|
#endif
|
|
|
|
/*
|
|
* Generate a core file image, which consists of many chunks.
|
|
* Returns an allocated table, an etnry of which is a pair of the address
|
|
* of a chunk and its length.
|
|
*/
|
|
|
|
/* ELF header */
|
|
|
|
void fill_elf_header(Elf64_Ehdr *eh, int segs)
|
|
{
|
|
eh->e_ident[EI_MAG0] = 0x7f;
|
|
eh->e_ident[EI_MAG1] = 'E';
|
|
eh->e_ident[EI_MAG2] = 'L';
|
|
eh->e_ident[EI_MAG3] = 'F';
|
|
eh->e_ident[EI_CLASS] = ELFCLASS64;
|
|
eh->e_ident[EI_DATA] = ELFDATA2LSB;
|
|
eh->e_ident[EI_VERSION] = El_VERSION;
|
|
eh->e_ident[EI_OSABI] = ELFOSABI_NONE;
|
|
eh->e_ident[EI_ABIVERSION] = El_ABIVERSION_NONE;
|
|
eh->e_type = ET_CORE;
|
|
eh->e_machine = EM_K10M;
|
|
eh->e_version = EV_CURRENT;
|
|
eh->e_entry = 0; /* Do we really need this? */
|
|
eh->e_phoff = 64; /* fixed */
|
|
eh->e_shoff = 0; /* no section header */
|
|
eh->e_flags = 0;
|
|
eh->e_ehsize = 64; /* fixed */
|
|
eh->e_phentsize = 56; /* fixed */
|
|
eh->e_phnum = segs;
|
|
eh->e_shentsize = 0;
|
|
eh->e_shnum = 0;
|
|
eh->e_shstrndx = 0;
|
|
}
|
|
|
|
/* prstatus NOTE */
|
|
|
|
int get_prstatus_size(void)
|
|
{
|
|
return sizeof(struct note) + align32(sizeof("CORE"))
|
|
+ align32(sizeof(struct elf_prstatus64));
|
|
}
|
|
|
|
void fill_prstatus(struct note *head, struct process *proc, void *regs0)
|
|
{
|
|
void *name;
|
|
struct elf_prstatus64 *prstatus;
|
|
struct x86_regs *regs = regs0;
|
|
register unsigned long _r12 asm("r12");
|
|
register unsigned long _r13 asm("r13");
|
|
register unsigned long _r14 asm("r14");
|
|
register unsigned long _r15 asm("r15");
|
|
|
|
head->namesz = sizeof("CORE");
|
|
head->descsz = sizeof(struct elf_prstatus64);
|
|
head->type = NT_PRSTATUS;
|
|
name = (void *) (head + 1);
|
|
memcpy(name, "CORE", sizeof("CORE"));
|
|
prstatus = (struct elf_prstatus64 *)(name + align32(sizeof("CORE")));
|
|
|
|
/*
|
|
We ignore following entries for now.
|
|
|
|
struct elf_siginfo pr_info;
|
|
short int pr_cursig;
|
|
a8_uint64_t pr_sigpend;
|
|
a8_uint64_t pr_sighold;
|
|
pid_t pr_pid;
|
|
pid_t pr_ppid;
|
|
pid_t pr_pgrp;
|
|
pid_t pr_sid;
|
|
struct prstatus64_timeval pr_utime;
|
|
struct prstatus64_timeval pr_stime;
|
|
struct prstatus64_timeval pr_cutime;
|
|
struct prstatus64_timeval pr_cstime;
|
|
*/
|
|
|
|
prstatus->pr_reg[0] = _r15;
|
|
prstatus->pr_reg[1] = _r14;
|
|
prstatus->pr_reg[2] = _r13;
|
|
prstatus->pr_reg[3] = _r12;
|
|
prstatus->pr_reg[4] = regs->rbp;
|
|
prstatus->pr_reg[5] = regs->rbx;
|
|
prstatus->pr_reg[6] = regs->r11;
|
|
prstatus->pr_reg[7] = regs->r10;
|
|
prstatus->pr_reg[8] = regs->r9;
|
|
prstatus->pr_reg[9] = regs->r8;
|
|
prstatus->pr_reg[10] = regs->rax;
|
|
prstatus->pr_reg[11] = regs->rcx;
|
|
prstatus->pr_reg[12] = regs->rdx;
|
|
prstatus->pr_reg[13] = regs->rsi;
|
|
prstatus->pr_reg[14] = regs->rdi;
|
|
prstatus->pr_reg[15] = regs->rax; /* ??? */
|
|
prstatus->pr_reg[16] = regs->rip;
|
|
prstatus->pr_reg[17] = regs->cs;
|
|
prstatus->pr_reg[18] = regs->rflags;
|
|
prstatus->pr_reg[19] = regs->rsp;
|
|
prstatus->pr_reg[20] = regs->ss;
|
|
prstatus->pr_reg[21] = rdmsr(MSR_FS_BASE);
|
|
prstatus->pr_reg[22] = rdmsr(MSR_GS_BASE);
|
|
/* There is no ds, es, fs and gs. */
|
|
|
|
prstatus->pr_fpvalid = 0; /* We assume no fp */
|
|
}
|
|
|
|
/* prpsinfo NOTE */
|
|
|
|
int get_prpsinfo_size(void)
|
|
{
|
|
return sizeof(struct note) + align32(sizeof("CORE"))
|
|
+ align32(sizeof(struct elf_prpsinfo64));
|
|
}
|
|
|
|
void fill_prpsinfo(struct note *head, struct process *proc, void *regs)
|
|
{
|
|
void *name;
|
|
struct elf_prpsinfo64 *prpsinfo;
|
|
|
|
head->namesz = sizeof("CORE");
|
|
head->descsz = sizeof(struct elf_prpsinfo64);
|
|
head->type = NT_PRPSINFO;
|
|
name = (void *) (head + 1);
|
|
memcpy(name, "CORE", sizeof("CORE"));
|
|
prpsinfo = (struct elf_prpsinfo64 *)(name + align32(sizeof("CORE")));
|
|
|
|
prpsinfo->pr_state = proc->status;
|
|
prpsinfo->pr_pid = proc->pid;
|
|
|
|
/*
|
|
We leave most of the fields unfilled.
|
|
|
|
char pr_state;
|
|
char pr_sname;
|
|
char pr_zomb;
|
|
char pr_nice;
|
|
a8_uint64_t pr_flag;
|
|
unsigned int pr_uid;
|
|
unsigned int pr_gid;
|
|
int pr_ppid, pr_pgrp, pr_sid;
|
|
char pr_fname[16];
|
|
char pr_psargs[ELF_PRARGSZ];
|
|
*/
|
|
}
|
|
|
|
/* auxv NOTE */
|
|
|
|
int get_auxv_size(void)
|
|
{
|
|
return sizeof(struct note) + align32(sizeof("CORE"))
|
|
+ sizeof(unsigned long) * AUXV_LEN;
|
|
}
|
|
|
|
void fill_auxv(struct note *head, struct process *proc, void *regs)
|
|
{
|
|
void *name;
|
|
void *auxv;
|
|
|
|
head->namesz = sizeof("CORE");
|
|
head->descsz = sizeof(unsigned long) * AUXV_LEN;
|
|
head->type = NT_AUXV;
|
|
name = (void *) (head + 1);
|
|
memcpy(name, "CORE", sizeof("CORE"));
|
|
auxv = name + align32(sizeof("CORE"));
|
|
memcpy(auxv, proc->saved_auxv, sizeof(unsigned long) * AUXV_LEN);
|
|
}
|
|
|
|
/* whole NOTE segment */
|
|
|
|
int get_note_size(void)
|
|
{
|
|
return get_prstatus_size() + get_prpsinfo_size()
|
|
+ get_auxv_size();
|
|
}
|
|
|
|
void fill_note(void *note, struct process *proc, void *regs)
|
|
{
|
|
fill_prstatus(note, proc, regs);
|
|
note += get_prstatus_size();
|
|
fill_prpsinfo(note, proc, regs);
|
|
note += get_prpsinfo_size();
|
|
fill_auxv(note, proc, regs);
|
|
}
|
|
|
|
/* whole core image */
|
|
|
|
int gencore(struct process *proc, void *regs,
|
|
struct coretable **coretable, int *chunks)
|
|
{
|
|
struct coretable *ct;
|
|
Elf64_Ehdr eh;
|
|
Elf64_Phdr *ph;
|
|
void *note;
|
|
struct vm_range *range;
|
|
struct process_vm *vm = proc->vm;
|
|
int segs = 1; /* the first one is for NOTE */
|
|
int notesize, phsize;
|
|
unsigned int offset = 0;
|
|
int i;
|
|
|
|
if (vm == NULL) {
|
|
dkprintf("no vm found.\n");
|
|
return -1;
|
|
}
|
|
|
|
list_for_each_entry(range, &vm->vm_range_list, list) {
|
|
dkprintf("start:%x end:%x flag:%x objoff:%x\n",
|
|
range->start, range->end, range->flag, range->objoff);
|
|
/* We omit reserved areas because they are only for
|
|
mckernel's internal use. */
|
|
if ((range->flag && VR_RESERVED) != 0)
|
|
continue;
|
|
segs++;
|
|
}
|
|
dkprintf("we have %d segs including one for NOTE.\n\n", segs);
|
|
|
|
{
|
|
struct vm_regions region = proc->vm->region;
|
|
|
|
dkprintf("text: %x-%x\n", region.text_start, region.text_end);
|
|
dkprintf("data: %x-%x\n", region.data_start, region.data_end);
|
|
dkprintf("brk: %x-%x\n", region.brk_start, region.brk_end);
|
|
dkprintf("map: %x-%x\n", region.map_start, region.map_end);
|
|
dkprintf("stack: %x-%x\n", region.stack_start, region.stack_end);
|
|
dkprintf("user: %x-%x\n\n", region.user_start, region.user_end);
|
|
}
|
|
|
|
dkprintf("now generate a core file image\n");
|
|
|
|
#define DUMMY
|
|
|
|
#ifndef DUMMY
|
|
|
|
offset += sizeof(eh);
|
|
fill_elf_header(&eh, segs);
|
|
|
|
notesize = get_note_size();
|
|
note = kmalloc(notesize, IHK_MC_AP_NOWAIT);
|
|
if (note == NULL) {
|
|
dkprintf("could not alloc NOTE for core.\n");
|
|
goto fail;
|
|
}
|
|
fill_note(note, proc, regs);
|
|
|
|
/* program header table */
|
|
phsize = sizeof(Elf64_Phdr) * segs;
|
|
ph = kmalloc(phsize, IHK_MC_AP_NOWAIT);
|
|
if (ph == NULL) {
|
|
dkprintf("could not alloc a program header table.\n");
|
|
goto fail;
|
|
}
|
|
|
|
offset += phsize;
|
|
|
|
/* prgram header for NOTE segment is exceptional */
|
|
ph[0].p_type = PT_NOTE;
|
|
ph[0].p_flags = 0;
|
|
ph[0].p_offset = offset;
|
|
ph[0].p_vaddr = 0;
|
|
ph[0].p_paddr = 0;
|
|
ph[0].p_filesz = 0;
|
|
ph[0].p_memsz = notesize;
|
|
ph[0].p_align = 1024;
|
|
|
|
/* program header for each memory chunk */
|
|
i = 1;
|
|
list_for_each_entry(range, &vm->vm_range_list, list) {
|
|
unsigned long flag = range->flag;
|
|
unsigned long size = range->end - range->start;
|
|
|
|
if ((range->flag && VR_RESERVED) != 0)
|
|
continue;
|
|
|
|
ph[i].p_type = PT_LOAD;
|
|
ph[i].p_flags = ((flag & VR_PROT_READ) ? PF_R : 0)
|
|
| ((flag & VR_PROT_WRITE) ? PF_W : 0)
|
|
| ((flag & VR_PROT_EXEC) ? PF_X : 0);
|
|
ph[i].p_offset = offset;
|
|
ph[i].p_vaddr = range->start;
|
|
ph[i].p_paddr = 0;
|
|
ph[i].p_filesz = 0; /* How can we know this? */
|
|
ph[i].p_memsz = size;
|
|
ph[i].p_align = 1024; /* ??? */
|
|
i++;
|
|
offset += size;
|
|
}
|
|
|
|
/* coretable to send to host */
|
|
ct = kmalloc(sizeof(struct coretable) * (segs + 2), IHK_MC_AP_NOWAIT);
|
|
if (!ct) {
|
|
dkprintf("could not alloc a coretable.\n");
|
|
goto fail;
|
|
}
|
|
|
|
ct[0].addr = virt_to_phys(&eh); /* ELF header */
|
|
ct[0].len = 64;
|
|
|
|
ct[1].addr = virt_to_phys(ph); /* program header table */
|
|
ct[1].len = phsize;
|
|
|
|
ct[2].addr = virt_to_phys(note); /* NOTE segment */
|
|
ct[2].len = notesize;
|
|
|
|
i = 3; /* memory segments */
|
|
list_for_each_entry(range, &vm->vm_range_list, list) {
|
|
if ((range->flag && VR_RESERVED) != 0)
|
|
continue;
|
|
ct[i].addr = virt_to_phys(range->start);
|
|
ct[i].len = range->end - range->start;
|
|
i++;
|
|
}
|
|
*chunks = segs + 2;
|
|
|
|
#else /* dummy */
|
|
*coretable = kmalloc(sizeof(struct coretable) * 3, IHK_MC_AP_NOWAIT);
|
|
if (!*coretable) {
|
|
dkprintf("could not alloc a coretable.\n");
|
|
goto fail;
|
|
}
|
|
|
|
ct[0].len = 8;
|
|
ct[0].addr = virt_to_phys("this is ");
|
|
ct[1].len = 7;
|
|
ct[1].addr = virt_to_phys("a test ");
|
|
ct[2].len = 15;
|
|
ct[2].addr = virt_to_phys("for coredump.\n");
|
|
|
|
dkprintf("generated a core table.\n");
|
|
|
|
*chunks = 3;
|
|
#endif
|
|
*coretable = ct;
|
|
return 0;
|
|
|
|
fail:
|
|
if (ct)
|
|
kfree(ct);
|
|
if (ph)
|
|
kfree(ph);
|
|
if (note)
|
|
kfree(note);
|
|
return -1;
|
|
}
|
|
|
|
/* Free all the allocated spaces. */
|
|
|
|
void freecore(struct coretable **coretable)
|
|
{
|
|
struct coretable *ct = *coretable;
|
|
kfree(phys_to_virt(ct[2].addr)); /* NOTE segment */
|
|
kfree(phys_to_virt(ct[1].addr)); /* ph */
|
|
kfree(*coretable);
|
|
}
|