Commit edf8e2af authored by Mika Westerberg's avatar Mika Westerberg Committed by Riku Voipio

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: default avatarMika Westerberg <mika.westerberg@iki.fi>
Signed-off-by: default avatarRiku Voipio <riku.voipio@iki.fi>
parent 88a8c984
......@@ -735,6 +735,8 @@ extern unsigned long qemu_host_page_mask;
#define PAGE_RESERVED 0x0020
void page_dump(FILE *f);
int walk_memory_regions(void *,
int (*fn)(void *, unsigned long, unsigned long, unsigned long));
int page_get_flags(target_ulong address);
void page_set_flags(target_ulong start, target_ulong end, int flags);
int page_check_range(target_ulong start, target_ulong len, int flags);
......
......@@ -1081,7 +1081,23 @@ typedef struct elf64_shdr {
#define EI_CLASS 4
#define EI_DATA 5
#define EI_VERSION 6
#define EI_PAD 7
#define EI_OSABI 7
#define EI_PAD 8
#define ELFOSABI_NONE 0 /* UNIX System V ABI */
#define ELFOSABI_SYSV 0 /* Alias. */
#define ELFOSABI_HPUX 1 /* HP-UX */
#define ELFOSABI_NETBSD 2 /* NetBSD. */
#define ELFOSABI_LINUX 3 /* Linux. */
#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */
#define ELFOSABI_AIX 7 /* IBM AIX. */
#define ELFOSABI_IRIX 8 /* SGI Irix. */
#define ELFOSABI_FREEBSD 9 /* FreeBSD. */
#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */
#define ELFOSABI_MODESTO 11 /* Novell Modesto. */
#define ELFOSABI_OPENBSD 12 /* OpenBSD. */
#define ELFOSABI_ARM 97 /* ARM */
#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */
#define ELFMAG0 0x7f /* EI_MAG */
#define ELFMAG1 'E'
......@@ -1108,6 +1124,7 @@ typedef struct elf64_shdr {
#define NT_PRFPREG 2
#define NT_PRPSINFO 3
#define NT_TASKSTRUCT 4
#define NT_AUXV 6
#define NT_PRXFPREG 0x46e62b7f /* copied from gdb5.1/include/elf/common.h */
......
......@@ -2131,36 +2131,36 @@ int tlb_set_page_exec(CPUState *env, target_ulong vaddr,
return 0;
}
/* dump memory mappings */
void page_dump(FILE *f)
/*
* Walks guest process memory "regions" one by one
* and calls callback function 'fn' for each region.
*/
int walk_memory_regions(void *priv,
int (*fn)(void *, unsigned long, unsigned long, unsigned long))
{
unsigned long start, end;
PageDesc *p = NULL;
int i, j, prot, prot1;
PageDesc *p;
int rc = 0;
fprintf(f, "%-8s %-8s %-8s %s\n",
"start", "end", "size", "prot");
start = -1;
end = -1;
start = end = -1;
prot = 0;
for(i = 0; i <= L1_SIZE; i++) {
if (i < L1_SIZE)
p = l1_map[i];
else
p = NULL;
for(j = 0;j < L2_SIZE; j++) {
if (!p)
prot1 = 0;
else
prot1 = p[j].flags;
for (i = 0; i <= L1_SIZE; i++) {
p = (i < L1_SIZE) ? l1_map[i] : NULL;
for (j = 0; j < L2_SIZE; j++) {
prot1 = (p == NULL) ? 0 : p[j].flags;
/*
* "region" is one continuous chunk of memory
* that has same protection flags set.
*/
if (prot1 != prot) {
end = (i << (32 - L1_BITS)) | (j << TARGET_PAGE_BITS);
if (start != -1) {
fprintf(f, "%08lx-%08lx %08lx %c%c%c\n",
start, end, end - start,
prot & PAGE_READ ? 'r' : '-',
prot & PAGE_WRITE ? 'w' : '-',
prot & PAGE_EXEC ? 'x' : '-');
rc = (*fn)(priv, start, end, prot);
/* callback can stop iteration by returning != 0 */
if (rc != 0)
return (rc);
}
if (prot1 != 0)
start = end;
......@@ -2168,10 +2168,33 @@ void page_dump(FILE *f)
start = -1;
prot = prot1;
}
if (!p)
if (p == NULL)
break;
}
}
return (rc);
}
static int dump_region(void *priv, unsigned long start,
unsigned long end, unsigned long prot)
{
FILE *f = (FILE *)priv;
(void) fprintf(f, "%08lx-%08lx %08lx %c%c%c\n",
start, end, end - start,
((prot & PAGE_READ) ? 'r' : '-'),
((prot & PAGE_WRITE) ? 'w' : '-'),
((prot & PAGE_EXEC) ? 'x' : '-'));
return (0);
}
/* dump memory mappings */
void page_dump(FILE *f)
{
(void) fprintf(f, "%-8s %-8s %-8s %s\n",
"start", "end", "size", "prot");
walk_memory_regions(f, dump_region);
}
int page_get_flags(target_ulong address)
......
This diff is collapsed.
......@@ -115,6 +115,7 @@ static int prepare_binprm(struct linux_binprm *bprm)
abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp,
abi_ulong stringp, int push_ptr)
{
TaskState *ts = (TaskState *)thread_env->opaque;
int n = sizeof(abi_ulong);
abi_ulong envp;
abi_ulong argv;
......@@ -133,13 +134,14 @@ abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp,
sp -= n;
/* FIXME - handle put_user() failures */
put_user_ual(argc, sp);
ts->info->arg_start = stringp;
while (argc-- > 0) {
/* FIXME - handle put_user() failures */
put_user_ual(stringp, argv);
argv += n;
stringp += target_strlen(stringp) + 1;
}
ts->info->arg_end = stringp;
/* FIXME - handle put_user() failures */
put_user_ual(0, argv);
while (envc-- > 0) {
......@@ -155,45 +157,45 @@ abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp,
}
int loader_exec(const char * filename, char ** argv, char ** envp,
struct target_pt_regs * regs, struct image_info *infop)
struct target_pt_regs * regs, struct image_info *infop,
struct linux_binprm *bprm)
{
struct linux_binprm bprm;
int retval;
int i;
bprm.p = TARGET_PAGE_SIZE*MAX_ARG_PAGES-sizeof(unsigned int);
bprm->p = TARGET_PAGE_SIZE*MAX_ARG_PAGES-sizeof(unsigned int);
for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */
bprm.page[i] = 0;
bprm->page[i] = 0;
retval = open(filename, O_RDONLY);
if (retval < 0)
return retval;
bprm.fd = retval;
bprm.filename = (char *)filename;
bprm.argc = count(argv);
bprm.argv = argv;
bprm.envc = count(envp);
bprm.envp = envp;
bprm->fd = retval;
bprm->filename = (char *)filename;
bprm->argc = count(argv);
bprm->argv = argv;
bprm->envc = count(envp);
bprm->envp = envp;
retval = prepare_binprm(&bprm);
retval = prepare_binprm(bprm);
infop->host_argv = argv;
if(retval>=0) {
if (bprm.buf[0] == 0x7f
&& bprm.buf[1] == 'E'
&& bprm.buf[2] == 'L'
&& bprm.buf[3] == 'F') {
if (bprm->buf[0] == 0x7f
&& bprm->buf[1] == 'E'
&& bprm->buf[2] == 'L'
&& bprm->buf[3] == 'F') {
#ifndef TARGET_HAS_ELFLOAD32
retval = load_elf_binary(&bprm,regs,infop);
retval = load_elf_binary(bprm,regs,infop);
#else
retval = load_elf_binary_multi(&bprm, regs, infop);
retval = load_elf_binary_multi(bprm, regs, infop);
#endif
#if defined(TARGET_HAS_BFLT)
} else if (bprm.buf[0] == 'b'
&& bprm.buf[1] == 'F'
&& bprm.buf[2] == 'L'
&& bprm.buf[3] == 'T') {
retval = load_flt_binary(&bprm,regs,infop);
} else if (bprm->buf[0] == 'b'
&& bprm->buf[1] == 'F'
&& bprm->buf[2] == 'L'
&& bprm->buf[3] == 'T') {
retval = load_flt_binary(bprm,regs,infop);
#endif
} else {
fprintf(stderr, "Unknown binary format\n");
......@@ -209,7 +211,7 @@ int loader_exec(const char * filename, char ** argv, char ** envp,
/* Something went wrong, return the inode and free the argument pages*/
for (i=0 ; i<MAX_ARG_PAGES ; i++) {
free(bprm.page[i]);
free(bprm->page[i]);
}
return(retval);
}
......@@ -25,6 +25,7 @@
#include <errno.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include "qemu.h"
#include "qemu-common.h"
......@@ -2319,6 +2320,27 @@ static void usage(void)
THREAD CPUState *thread_env;
void task_settid(TaskState *ts)
{
if (ts->ts_tid == 0) {
#ifdef USE_NPTL
ts->ts_tid = (pid_t)syscall(SYS_gettid);
#else
/* when no threads are used, tid becomes pid */
ts->ts_tid = getpid();
#endif
}
}
void stop_all_tasks(void)
{
/*
* We trust that when using NPTL, start_exclusive()
* handles thread stopping correctly.
*/
start_exclusive();
}
/* Assumes contents are already zeroed. */
void init_task_state(TaskState *ts)
{
......@@ -2338,6 +2360,7 @@ int main(int argc, char **argv, char **envp)
const char *cpu_model;
struct target_pt_regs regs1, *regs = &regs1;
struct image_info info1, *info = &info1;
struct linux_binprm bprm;
TaskState ts1, *ts = &ts1;
CPUState *env;
int optind;
......@@ -2467,6 +2490,8 @@ int main(int argc, char **argv, char **envp)
/* Zero out image_info */
memset(info, 0, sizeof(struct image_info));
memset(&bprm, 0, sizeof (bprm));
/* Scan interp_prefix dir for replacement files. */
init_paths(interp_prefix);
......@@ -2543,7 +2568,16 @@ int main(int argc, char **argv, char **envp)
}
target_argv[target_argc] = NULL;
if (loader_exec(filename, target_argv, target_environ, regs, info) != 0) {
memset(ts, 0, sizeof(TaskState));
init_task_state(ts);
/* build Task State */
ts->info = info;
ts->bprm = &bprm;
env->opaque = ts;
task_settid(ts);
if (loader_exec(filename, target_argv, target_environ, regs,
info, &bprm) != 0) {
printf("Error loading %s\n", filename);
_exit(1);
}
......@@ -2579,12 +2613,6 @@ int main(int argc, char **argv, char **envp)
syscall_init();
signal_init();
/* build Task State */
memset(ts, 0, sizeof(TaskState));
init_task_state(ts);
ts->info = info;
env->opaque = ts;
#if defined(TARGET_I386)
cpu_x86_set_cpl(env, 3);
......
......@@ -18,6 +18,7 @@
#include "syscall.h"
#include "target_signal.h"
#include "gdbstub.h"
#include "sys-queue.h"
#if defined(USE_NPTL)
#define THREAD __thread
......@@ -44,6 +45,9 @@ struct image_info {
abi_ulong entry;
abi_ulong code_offset;
abi_ulong data_offset;
abi_ulong saved_auxv;
abi_ulong arg_start;
abi_ulong arg_end;
char **host_argv;
int personality;
};
......@@ -87,7 +91,7 @@ struct emulated_sigtable {
/* NOTE: we force a big alignment so that the stack stored after is
aligned too */
typedef struct TaskState {
struct TaskState *next;
pid_t ts_tid; /* tid (or pid) of this task */
#ifdef TARGET_ARM
/* FPA state */
FPA11 fpa;
......@@ -114,6 +118,7 @@ typedef struct TaskState {
#endif
int used; /* non zero if used */
struct image_info *info;
struct linux_binprm *bprm;
struct emulated_sigtable sigtab[TARGET_NSIG];
struct sigqueue sigqueue_table[MAX_SIGQUEUE_SIZE]; /* siginfo queue */
......@@ -125,6 +130,8 @@ typedef struct TaskState {
extern char *exec_path;
void init_task_state(TaskState *ts);
void task_settid(TaskState *);
void stop_all_tasks(void);
extern const char *qemu_uname_release;
/* ??? See if we can avoid exposing so much of the loader internals. */
......@@ -149,13 +156,15 @@ struct linux_binprm {
char **argv;
char **envp;
char * filename; /* Name of binary */
int (*core_dump)(int, const CPUState *); /* coredump routine */
};
void do_init_thread(struct target_pt_regs *regs, struct image_info *infop);
abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp,
abi_ulong stringp, int push_ptr);
int loader_exec(const char * filename, char ** argv, char ** envp,
struct target_pt_regs * regs, struct image_info *infop);
struct target_pt_regs * regs, struct image_info *infop,
struct linux_binprm *);
int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
struct image_info * info);
......
......@@ -27,6 +27,7 @@
#include <errno.h>
#include <assert.h>
#include <sys/ucontext.h>
#include <sys/resource.h>
#include "qemu.h"
#include "qemu-common.h"
......@@ -287,6 +288,23 @@ static int fatal_signal (int sig)
}
}
/* returns 1 if given signal should dump core if not handled */
static int core_dump_signal(int sig)
{
switch (sig) {
case TARGET_SIGABRT:
case TARGET_SIGFPE:
case TARGET_SIGILL:
case TARGET_SIGQUIT:
case TARGET_SIGSEGV:
case TARGET_SIGTRAP:
case TARGET_SIGBUS:
return (1);
default:
return (0);
}
}
void signal_init(void)
{
struct sigaction act;
......@@ -352,13 +370,29 @@ static inline void free_sigqueue(CPUState *env, struct sigqueue *q)
/* abort execution with signal */
static void QEMU_NORETURN force_sig(int sig)
{
int host_sig;
TaskState *ts = (TaskState *)thread_env->opaque;
int host_sig, core_dumped = 0;
struct sigaction act;
host_sig = target_to_host_signal(sig);
fprintf(stderr, "qemu: uncaught target signal %d (%s) - exiting\n",
sig, strsignal(host_sig));
gdb_signalled(thread_env, sig);
/* dump core if supported by target binary format */
if (core_dump_signal(sig) && (ts->bprm->core_dump != NULL)) {
stop_all_tasks();
core_dumped =
((*ts->bprm->core_dump)(sig, thread_env) == 0);
}
if (core_dumped) {
/* we already dumped the core of target process, we don't want
* a coredump of qemu itself */
struct rlimit nodump;
getrlimit(RLIMIT_CORE, &nodump);
nodump.rlim_cur=0;
setrlimit(RLIMIT_CORE, &nodump);
(void) fprintf(stderr, "qemu: uncaught target signal %d (%s) - %s\n",
sig, strsignal(host_sig), "core dumped" );
}
/* The proper exit code for dieing from an uncaught signal is
* -<signal>. The kernel doesn't allow exit() or _exit() to pass
* a negative value. To get the proper exit code we need to
......
......@@ -3379,11 +3379,14 @@ static void *clone_func(void *arg)
{
new_thread_info *info = arg;
CPUState *env;
TaskState *ts;
env = info->env;
thread_env = env;
ts = (TaskState *)thread_env->opaque;
info->tid = gettid();
env->host_tid = info->tid;
task_settid(ts);
if (info->child_tidptr)
put_user_u32(info->tid, info->child_tidptr);
if (info->parent_tidptr)
......@@ -3435,6 +3438,7 @@ static int do_fork(CPUState *env, unsigned int flags, abi_ulong newsp,
flags &= ~(CLONE_VFORK | CLONE_VM);
if (flags & CLONE_VM) {
TaskState *parent_ts = (TaskState *)env->opaque;
#if defined(USE_NPTL)
new_thread_info info;
pthread_attr_t attr;
......@@ -3447,6 +3451,8 @@ static int do_fork(CPUState *env, unsigned int flags, abi_ulong newsp,
/* Init regs that differ from the parent. */
cpu_clone_regs(new_env, newsp);
new_env->opaque = ts;
ts->bprm = parent_ts->bprm;
ts->info = parent_ts->info;
#if defined(USE_NPTL)
nptl_flags = flags;
flags &= ~CLONE_NPTL_FLAGS2;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment