From edf8e2af1453ce56c72b2f25a745e3734177a05d Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 7 Apr 2009 09:57:11 +0300 Subject: linux-user: implemented ELF coredump support for ARM target When target process is killed with signal (such signal that should dump core) a coredump file is created. This file is similar than coredump generated by Linux (there are few exceptions though). Riku Voipio: added support for rlimit Signed-off-by: Mika Westerberg Signed-off-by: Riku Voipio --- linux-user/elfload.c | 928 ++++++++++++++++++++++++++++++++++++++++++++++++- linux-user/linuxload.c | 50 +-- linux-user/main.c | 42 ++- linux-user/qemu.h | 13 +- linux-user/signal.c | 40 ++- linux-user/syscall.c | 6 + 6 files changed, 1035 insertions(+), 44 deletions(-) (limited to 'linux-user') diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 190ad143e0..8996c16126 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1,4 +1,6 @@ /* This is the Linux kernel elf-loading code, ported into user space */ +#include +#include #include #include @@ -6,8 +8,10 @@ #include #include #include +#include #include #include +#include #include "qemu.h" #include "disas.h" @@ -21,6 +25,8 @@ #undef ELF_ARCH #endif +#define ELF_OSABI ELFOSABI_SYSV + /* from personality.h */ /* @@ -160,7 +166,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i } #endif -#define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #endif @@ -198,6 +203,37 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i regs->ARM_r10 = infop->start_data; } +typedef uint32_t elf_greg_t; +typedef uint16_t target_uid_t; +typedef uint16_t target_gid_t; +typedef int32_t target_pid_t; + +#define ELF_NREG 18 +typedef elf_greg_t elf_gregset_t[ELF_NREG]; + +static void elf_core_copy_regs(elf_gregset_t *regs, const CPUState *env) +{ + (*regs)[0] = env->regs[0]; + (*regs)[1] = env->regs[1]; + (*regs)[2] = env->regs[2]; + (*regs)[3] = env->regs[3]; + (*regs)[4] = env->regs[4]; + (*regs)[5] = env->regs[5]; + (*regs)[6] = env->regs[6]; + (*regs)[7] = env->regs[7]; + (*regs)[8] = env->regs[8]; + (*regs)[9] = env->regs[9]; + (*regs)[10] = env->regs[10]; + (*regs)[11] = env->regs[11]; + (*regs)[12] = env->regs[12]; + (*regs)[13] = env->regs[13]; + (*regs)[14] = env->regs[14]; + (*regs)[15] = env->regs[15]; + + (*regs)[16] = cpsr_read((CPUState *)env); + (*regs)[17] = env->regs[0]; /* XXX */ +} + #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 @@ -418,7 +454,6 @@ static inline void init_thread(struct target_pt_regs *_regs, struct image_info * _regs->gpr[5] = pos; } -#define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #endif @@ -448,7 +483,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i regs->regs[29] = infop->start_stack; } -#define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #endif /* TARGET_MIPS */ @@ -470,7 +504,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i } -#define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #endif /* TARGET_MICROBLAZE */ @@ -492,7 +525,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i regs->regs[15] = infop->start_stack; } -#define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #endif @@ -512,7 +544,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i regs->erp = infop->entry; } -#define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 8192 #endif @@ -537,7 +568,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i regs->pc = infop->entry; } -#define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 8192 #endif @@ -562,7 +592,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i regs->unique, infop->start_data); } -#define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 8192 #endif /* TARGET_ALPHA */ @@ -680,6 +709,20 @@ static void bswap_sym(struct elf_sym *sym) } #endif +#ifdef USE_ELF_CORE_DUMP +static int elf_core_dump(int, const CPUState *); + +#ifdef BSWAP_NEEDED +static void bswap_note(struct elf_note *en) +{ + bswaptls(&en->n_namesz); + bswaptls(&en->n_descsz); + bswaptls(&en->n_type); +} +#endif /* BSWAP_NEEDED */ + +#endif /* USE_ELF_CORE_DUMP */ + /* * 'copy_elf_strings()' copies argument/envelope strings from user * memory to free pages in kernel mem. These are in a format ready @@ -904,6 +947,8 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, #endif #undef NEW_AUX_ENT + info->saved_auxv = sp; + sp = loader_build_argptr(envc, argc, sp, p, !ibcs); return sp; } @@ -1586,9 +1631,876 @@ int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, info->entry = elf_entry; +#ifdef USE_ELF_CORE_DUMP + bprm->core_dump = &elf_core_dump; +#endif + return 0; } +#ifdef USE_ELF_CORE_DUMP + +/* + * Definitions to generate Intel SVR4-like core files. + * These mostly have the same names as the SVR4 types with "elf_" + * tacked on the front to prevent clashes with linux definitions, + * and the typedef forms have been avoided. This is mostly like + * the SVR4 structure, but more Linuxy, with things that Linux does + * not support and which gdb doesn't really use excluded. + * + * Fields we don't dump (their contents is zero) in linux-user qemu + * are marked with XXX. + * + * Core dump code is copied from linux kernel (fs/binfmt_elf.c). + * + * Porting ELF coredump for target is (quite) simple process. First you + * define ELF_USE_CORE_DUMP in target ELF code (where init_thread() for + * the target resides): + * + * #define USE_ELF_CORE_DUMP + * + * Next you define type of register set used for dumping. ELF specification + * says that it needs to be array of elf_greg_t that has size of ELF_NREG. + * + * typedef elf_greg_t; + * #define ELF_NREG + * typedef elf_greg_t elf_gregset_t[ELF_NREG]; + * + * Then define following types to match target types. Actual types can + * be found from linux kernel (arch//include/asm/posix_types.h): + * + * typedef target_uid_t; + * typedef target_gid_t; + * typedef target_pid_t; + * + * Last step is to implement target specific function that copies registers + * from given cpu into just specified register set. Prototype is: + * + * static void elf_core_copy_regs(elf_gregset_t *regs, const CPUState *env); + * + * Parameters: + * regs - copy register values into here (allocated and zeroed by caller) + * env - copy registers from here + * + * Example for ARM target is provided in this file. + */ + +/* An ELF note in memory */ +struct memelfnote { + const char *name; + size_t namesz; + size_t namesz_rounded; + int type; + size_t datasz; + void *data; + size_t notesz; +}; + +struct elf_siginfo { + int si_signo; /* signal number */ + int si_code; /* extra code */ + int si_errno; /* errno */ +}; + +struct elf_prstatus { + struct elf_siginfo pr_info; /* Info associated with signal */ + short pr_cursig; /* Current signal */ + target_ulong pr_sigpend; /* XXX */ + target_ulong pr_sighold; /* XXX */ + target_pid_t pr_pid; + target_pid_t pr_ppid; + target_pid_t pr_pgrp; + target_pid_t pr_sid; + struct target_timeval pr_utime; /* XXX User time */ + struct target_timeval pr_stime; /* XXX System time */ + struct target_timeval pr_cutime; /* XXX Cumulative user time */ + struct target_timeval pr_cstime; /* XXX Cumulative system time */ + elf_gregset_t pr_reg; /* GP registers */ + int pr_fpvalid; /* XXX */ +}; + +#define ELF_PRARGSZ (80) /* Number of chars for args */ + +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 */ + target_ulong pr_flag; /* flags */ + target_uid_t pr_uid; + target_gid_t pr_gid; + target_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 */ +}; + +/* Here is the structure in which status of each thread is captured. */ +struct elf_thread_status { + TAILQ_ENTRY(elf_thread_status) ets_link; + struct elf_prstatus prstatus; /* NT_PRSTATUS */ +#if 0 + elf_fpregset_t fpu; /* NT_PRFPREG */ + struct task_struct *thread; + elf_fpxregset_t xfpu; /* ELF_CORE_XFPREG_TYPE */ +#endif + struct memelfnote notes[1]; + int num_notes; +}; + +struct elf_note_info { + struct memelfnote *notes; + struct elf_prstatus *prstatus; /* NT_PRSTATUS */ + struct elf_prpsinfo *psinfo; /* NT_PRPSINFO */ + + TAILQ_HEAD(thread_list_head, elf_thread_status) thread_list; +#if 0 + /* + * Current version of ELF coredump doesn't support + * dumping fp regs etc. + */ + elf_fpregset_t *fpu; + elf_fpxregset_t *xfpu; + int thread_status_size; +#endif + int notes_size; + int numnote; +}; + +struct vm_area_struct { + abi_ulong vma_start; /* start vaddr of memory region */ + abi_ulong vma_end; /* end vaddr of memory region */ + abi_ulong vma_flags; /* protection etc. flags for the region */ + TAILQ_ENTRY(vm_area_struct) vma_link; +}; + +struct mm_struct { + TAILQ_HEAD(, vm_area_struct) mm_mmap; + int mm_count; /* number of mappings */ +}; + +static struct mm_struct *vma_init(void); +static void vma_delete(struct mm_struct *); +static int vma_add_mapping(struct mm_struct *, abi_ulong, + abi_ulong, abi_ulong); +static int vma_get_mapping_count(const struct mm_struct *); +static struct vm_area_struct *vma_first(const struct mm_struct *); +static struct vm_area_struct *vma_next(struct vm_area_struct *); +static abi_ulong vma_dump_size(const struct vm_area_struct *); +static int vma_walker(void *priv, unsigned long start, unsigned long end, + unsigned long flags); + +static void fill_elf_header(struct elfhdr *, int, uint16_t, uint32_t); +static void fill_note(struct memelfnote *, const char *, int, + unsigned int, void *); +static void fill_prstatus(struct elf_prstatus *, const TaskState *, int); +static int fill_psinfo(struct elf_prpsinfo *, const TaskState *); +static void fill_auxv_note(struct memelfnote *, const TaskState *); +static void fill_elf_note_phdr(struct elf_phdr *, int, off_t); +static size_t note_size(const struct memelfnote *); +static void free_note_info(struct elf_note_info *); +static int fill_note_info(struct elf_note_info *, long, const CPUState *); +static void fill_thread_info(struct elf_note_info *, const CPUState *); +static int core_dump_filename(const TaskState *, char *, size_t); + +static int dump_write(int, const void *, size_t); +static int write_note(struct memelfnote *, int); +static int write_note_info(struct elf_note_info *, int); + +#ifdef BSWAP_NEEDED +static void bswap_prstatus(struct elf_prstatus *); +static void bswap_psinfo(struct elf_prpsinfo *); + +static void bswap_prstatus(struct elf_prstatus *prstatus) +{ + prstatus->pr_info.si_signo = tswapl(prstatus->pr_info.si_signo); + prstatus->pr_info.si_code = tswapl(prstatus->pr_info.si_code); + prstatus->pr_info.si_errno = tswapl(prstatus->pr_info.si_errno); + prstatus->pr_cursig = tswap16(prstatus->pr_cursig); + prstatus->pr_sigpend = tswapl(prstatus->pr_sigpend); + prstatus->pr_sighold = tswapl(prstatus->pr_sighold); + prstatus->pr_pid = tswap32(prstatus->pr_pid); + prstatus->pr_ppid = tswap32(prstatus->pr_ppid); + prstatus->pr_pgrp = tswap32(prstatus->pr_pgrp); + prstatus->pr_sid = tswap32(prstatus->pr_sid); + /* cpu times are not filled, so we skip them */ + /* regs should be in correct format already */ + prstatus->pr_fpvalid = tswap32(prstatus->pr_fpvalid); +} + +static void bswap_psinfo(struct elf_prpsinfo *psinfo) +{ + psinfo->pr_flag = tswapl(psinfo->pr_flag); + psinfo->pr_uid = tswap16(psinfo->pr_uid); + psinfo->pr_gid = tswap16(psinfo->pr_gid); + psinfo->pr_pid = tswap32(psinfo->pr_pid); + psinfo->pr_ppid = tswap32(psinfo->pr_ppid); + psinfo->pr_pgrp = tswap32(psinfo->pr_pgrp); + psinfo->pr_sid = tswap32(psinfo->pr_sid); +} +#endif /* BSWAP_NEEDED */ + +/* + * Minimal support for linux memory regions. These are needed + * when we are finding out what memory exactly belongs to + * emulated process. No locks needed here, as long as + * thread that received the signal is stopped. + */ + +static struct mm_struct *vma_init(void) +{ + struct mm_struct *mm; + + if ((mm = qemu_malloc(sizeof (*mm))) == NULL) + return (NULL); + + mm->mm_count = 0; + TAILQ_INIT(&mm->mm_mmap); + + return (mm); +} + +static void vma_delete(struct mm_struct *mm) +{ + struct vm_area_struct *vma; + + while ((vma = vma_first(mm)) != NULL) { + TAILQ_REMOVE(&mm->mm_mmap, vma, vma_link); + qemu_free(vma); + } + qemu_free(mm); +} + +static int vma_add_mapping(struct mm_struct *mm, abi_ulong start, + abi_ulong end, abi_ulong flags) +{ + struct vm_area_struct *vma; + + if ((vma = qemu_mallocz(sizeof (*vma))) == NULL) + return (-1); + + vma->vma_start = start; + vma->vma_end = end; + vma->vma_flags = flags; + + TAILQ_INSERT_TAIL(&mm->mm_mmap, vma, vma_link); + mm->mm_count++; + + return (0); +} + +static struct vm_area_struct *vma_first(const struct mm_struct *mm) +{ + return (TAILQ_FIRST(&mm->mm_mmap)); +} + +static struct vm_area_struct *vma_next(struct vm_area_struct *vma) +{ + return (TAILQ_NEXT(vma, vma_link)); +} + +static int vma_get_mapping_count(const struct mm_struct *mm) +{ + return (mm->mm_count); +} + +/* + * Calculate file (dump) size of given memory region. + */ +static abi_ulong vma_dump_size(const struct vm_area_struct *vma) +{ + /* if we cannot even read the first page, skip it */ + if (!access_ok(VERIFY_READ, vma->vma_start, TARGET_PAGE_SIZE)) + return (0); + + /* + * Usually we don't dump executable pages as they contain + * non-writable code that debugger can read directly from + * target library etc. However, thread stacks are marked + * also executable so we read in first page of given region + * and check whether it contains elf header. If there is + * no elf header, we dump it. + */ + if (vma->vma_flags & PROT_EXEC) { + char page[TARGET_PAGE_SIZE]; + + copy_from_user(page, vma->vma_start, sizeof (page)); + if ((page[EI_MAG0] == ELFMAG0) && + (page[EI_MAG1] == ELFMAG1) && + (page[EI_MAG2] == ELFMAG2) && + (page[EI_MAG3] == ELFMAG3)) { + /* + * Mappings are possibly from ELF binary. Don't dump + * them. + */ + return (0); + } + } + + return (vma->vma_end - vma->vma_start); +} + +static int vma_walker(void *priv, unsigned long start, unsigned long end, + unsigned long flags) +{ + struct mm_struct *mm = (struct mm_struct *)priv; + + /* + * Don't dump anything that qemu has reserved for internal use. + */ + if (flags & PAGE_RESERVED) + return (0); + + vma_add_mapping(mm, start, end, flags); + return (0); +} + +static void fill_note(struct memelfnote *note, const char *name, int type, + unsigned int sz, void *data) +{ + unsigned int namesz; + + namesz = strlen(name) + 1; + note->name = name; + note->namesz = namesz; + note->namesz_rounded = roundup(namesz, sizeof (int32_t)); + note->type = type; + note->datasz = roundup(sz, sizeof (int32_t));; + note->data = data; + + /* + * We calculate rounded up note size here as specified by + * ELF document. + */ + note->notesz = sizeof (struct elf_note) + + note->namesz_rounded + note->datasz; +} + +static void fill_elf_header(struct elfhdr *elf, int segs, uint16_t machine, + uint32_t flags) +{ + (void) memset(elf, 0, sizeof(*elf)); + + (void) 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; + elf->e_ident[EI_OSABI] = ELF_OSABI; + + elf->e_type = ET_CORE; + elf->e_machine = machine; + elf->e_version = EV_CURRENT; + elf->e_phoff = sizeof(struct elfhdr); + elf->e_flags = flags; + elf->e_ehsize = sizeof(struct elfhdr); + elf->e_phentsize = sizeof(struct elf_phdr); + elf->e_phnum = segs; + +#ifdef BSWAP_NEEDED + bswap_ehdr(elf); +#endif +} + +static void fill_elf_note_phdr(struct elf_phdr *phdr, int sz, off_t offset) +{ + phdr->p_type = PT_NOTE; + phdr->p_offset = offset; + phdr->p_vaddr = 0; + phdr->p_paddr = 0; + phdr->p_filesz = sz; + phdr->p_memsz = 0; + phdr->p_flags = 0; + phdr->p_align = 0; + +#ifdef BSWAP_NEEDED + bswap_phdr(phdr); +#endif +} + +static size_t note_size(const struct memelfnote *note) +{ + return (note->notesz); +} + +static void fill_prstatus(struct elf_prstatus *prstatus, + const TaskState *ts, int signr) +{ + (void) memset(prstatus, 0, sizeof (*prstatus)); + prstatus->pr_info.si_signo = prstatus->pr_cursig = signr; + prstatus->pr_pid = ts->ts_tid; + prstatus->pr_ppid = getppid(); + prstatus->pr_pgrp = getpgrp(); + prstatus->pr_sid = getsid(0); + +#ifdef BSWAP_NEEDED + bswap_prstatus(prstatus); +#endif +} + +static int fill_psinfo(struct elf_prpsinfo *psinfo, const TaskState *ts) +{ + char *filename, *base_filename; + unsigned int i, len; + + (void) memset(psinfo, 0, sizeof (*psinfo)); + + len = ts->info->arg_end - ts->info->arg_start; + if (len >= ELF_PRARGSZ) + len = ELF_PRARGSZ - 1; + if (copy_from_user(&psinfo->pr_psargs, ts->info->arg_start, len)) + return -EFAULT; + for (i = 0; i < len; i++) + if (psinfo->pr_psargs[i] == 0) + psinfo->pr_psargs[i] = ' '; + psinfo->pr_psargs[len] = 0; + + psinfo->pr_pid = getpid(); + psinfo->pr_ppid = getppid(); + psinfo->pr_pgrp = getpgrp(); + psinfo->pr_sid = getsid(0); + psinfo->pr_uid = getuid(); + psinfo->pr_gid = getgid(); + + filename = strdup(ts->bprm->filename); + base_filename = strdup(basename(filename)); + (void) strncpy(psinfo->pr_fname, base_filename, + sizeof(psinfo->pr_fname)); + free(base_filename); + free(filename); + +#ifdef BSWAP_NEEDED + bswap_psinfo(psinfo); +#endif + return (0); +} + +static void fill_auxv_note(struct memelfnote *note, const TaskState *ts) +{ + elf_addr_t auxv = (elf_addr_t)ts->info->saved_auxv; + elf_addr_t orig_auxv = auxv; + abi_ulong val; + void *ptr; + int i, len; + + /* + * Auxiliary vector is stored in target process stack. It contains + * {type, value} pairs that we need to dump into note. This is not + * strictly necessary but we do it here for sake of completeness. + */ + + /* find out lenght of the vector, AT_NULL is terminator */ + i = len = 0; + do { + get_user_ual(val, auxv); + i += 2; + auxv += 2 * sizeof (elf_addr_t); + } while (val != AT_NULL); + len = i * sizeof (elf_addr_t); + + /* read in whole auxv vector and copy it to memelfnote */ + ptr = lock_user(VERIFY_READ, orig_auxv, len, 0); + if (ptr != NULL) { + fill_note(note, "CORE", NT_AUXV, len, ptr); + unlock_user(ptr, auxv, len); + } +} + +/* + * Constructs name of coredump file. We have following convention + * for the name: + * qemu__-