From 6f00ddced6e44b620cbc8329f0b8c415e24883ce Mon Sep 17 00:00:00 2001 From: NAKAMURA Gou Date: Mon, 14 Mar 2016 21:41:05 +0900 Subject: [PATCH] move eclair from ihk repository --- configure.ac | 42 ++ executer/user/Makefile.in | 10 +- executer/user/eclair.c | 966 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 1016 insertions(+), 2 deletions(-) create mode 100644 executer/user/eclair.c diff --git a/configure.ac b/configure.ac index 085b9b43..43647be6 100644 --- a/configure.ac +++ b/configure.ac @@ -36,6 +36,12 @@ AC_ARG_ENABLE([dcfa], [AS_HELP_STRING( [--enable-dcfa],[Enable DCFA modules])],[],[enable_dcfa=no]) +AC_ARG_ENABLE([memdump], + AC_HELP_STRING([--enable-memdump], + [enable dumping memory and analyzing a dump]), + [ENABLE_MEMDUMP=$enableval], + [ENABLE_MEMDUMP=default]) + case "X$WITH_KERNELSRC" in Xyes | Xno | X) WITH_KERNELSRC='/lib/modules/`uname -r`/build' @@ -209,6 +215,41 @@ MCCTRL_FIND_KSYM([sys_mount]) MCCTRL_FIND_KSYM([sys_unshare]) MCCTRL_FIND_KSYM([zap_page_range]) +case $ENABLE_MEMDUMP in + yes|no|auto) + ;; + default) + if test "x$WITH_TARGET" = "xsmp-x86" ; then + ENABLE_MEMDUMP=auto + else + ENABLE_MEMDUMP=no + fi + ;; + *) + AC_MSG_ERROR([unknown memdump argument: $ENABLE_MEMDUMP]) + ;; +esac + +if test "x$ENABLE_MEMDUMP" != "xno" ; then + enableval=yes + AC_CHECK_LIB([bfd],[bfd_init],[],[enableval=no]) + AC_CHECK_HEADER([bfd.h],[],[enableval=no]) + + if test "x$ENABLE_MEMDUMP" = "xyes" -a "x$enableval" = "xno" ; then + AC_MSG_ERROR([memdump feature needs bfd.h and libbfd a.k.a bunutils-devel]) + fi + ENABLE_MEMDUMP=$enableval +fi + +if test "x$ENABLE_MEMDUMP" = "xyes" ; then + AC_MSG_NOTICE([memdump feature is enabled]) + AC_DEFINE([ENABLE_MEMDUMP],[1],[whether memdump feature is enabled]) + uncomment_if_ENABLE_MEMDUMP='' +else + AC_MSG_NOTICE([memdump feature is disabled]) + uncomment_if_ENABLE_MEMDUMP='#' +fi + AC_SUBST(CC) AC_SUBST(XCC) AC_SUBST(ARCH) @@ -226,6 +267,7 @@ AC_SUBST(DCFA_VERSION) AC_SUBST(IHK_RELEASE_DATE) AC_SUBST(MCKERNEL_RELEASE_DATE) AC_SUBST(DCFA_RESEASE_DATE) +AC_SUBST(uncomment_if_ENABLE_MEMDUMP) AC_CONFIG_HEADERS([executer/config.h]) AC_CONFIG_FILES([ diff --git a/executer/user/Makefile.in b/executer/user/Makefile.in index 0b3083d3..277ca85a 100644 --- a/executer/user/Makefile.in +++ b/executer/user/Makefile.in @@ -1,14 +1,19 @@ CC=@CC@ BINDIR=@BINDIR@ KDIR ?= @KDIR@ -CFLAGS=-Wall -O -fPIE -pie +CFLAGS=-Wall -O VPATH=@abs_srcdir@ TARGET=mcexec +@uncomment_if_ENABLE_MEMDUMP@TARGET+=eclair +LIBS=@LIBS@ all: $(TARGET) mcexec: mcexec.c - $(CC) -I${KDIR} $(CFLAGS) $(EXTRA_CFLAGS) -lrt -pthread -o $@ $^ $(EXTRA_OBJS) + $(CC) -I${KDIR} $(CFLAGS) $(EXTRA_CFLAGS) -fPIE -pie -lrt -pthread -o $@ $^ $(EXTRA_OBJS) + +eclair: eclair.c + $(CC) $(CFLAGS) -o $@ $^ $(LIBS) clean: $(RM) $(TARGET) *.o @@ -18,4 +23,5 @@ clean: install: mkdir -p -m 755 $(BINDIR) install -m 755 mcexec $(BINDIR) + @uncomment_if_ENABLE_MEMDUMP@install -m 755 eclair $(BINDIR) diff --git a/executer/user/eclair.c b/executer/user/eclair.c new file mode 100644 index 00000000..7399325b --- /dev/null +++ b/executer/user/eclair.c @@ -0,0 +1,966 @@ +/** + * \file eclair.c + * License details are found in the file LICENSE. + * \brief + * IHK os memory dump analyzer for McKernel + * \author Gou Nakamura \par + * Copyright (C) 2015 RIKEN AICS + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CPU_TID_BASE 1000000 + +struct options { + uint8_t cpu; + uint8_t help; + char *kernel_path; + char *dump_path; + char *log_path; +}; /* struct options */ + +struct thread_info { + struct thread_info *next; + int status; +#define PS_RUNNING 0x01 +#define PS_INTERRUPTIBLE 0x02 +#define PS_UNINTERRUPTIBLE 0x04 +#define PS_STOPPED 0x20 +#define PS_TRACED 0x40 +#define CS_IDLE 0x010000 +#define CS_RUNNING 0x020000 +#define CS_RESERVED 0x030000 + int pid; + int tid; + int cpu; + int lcpu; + int padding; + uintptr_t process; + uintptr_t clv; + uintptr_t x86_clv; +}; /* struct thread_info */ + +static struct options opt; +static volatile int f_done = 0; +static bfd *symbfd = NULL; +static bfd *dumpbfd = NULL; +static asection *dumpscn = NULL; +static int num_processors = -1; +static asymbol **symtab = NULL; +static ssize_t nsyms; +static uintptr_t kernel_base; +static struct thread_info *tihead = NULL; +static struct thread_info **titailp = &tihead; +static struct thread_info *curr_thread = NULL; +static uintptr_t ihk_mc_switch_context = -1; + +static uintptr_t lookup_symbol(char *name) { + int i; + + for (i = 0; i < nsyms; ++i) { + if (!strcmp(symtab[i]->name, name)) { + return (symtab[i]->section->vma + symtab[i]->value); + } + } +#define NOSYMBOL ((uintptr_t)-1) + return NOSYMBOL; +} /* lookup_symbol() */ + +static uintptr_t virt_to_phys(uintptr_t va) { +#define MAP_KERNEL 0xFFFFFFFF80000000 + if (va >= MAP_KERNEL) { + return (va - MAP_KERNEL + kernel_base); + } +#define MAP_ST 0xFFFF800000000000 + if (va >= MAP_ST) { + return (va - MAP_ST); + } + if (0) printf("virt_to_phys(%lx): -1\n", va); +#define NOPHYS ((uintptr_t)-1) + return NOPHYS; +} /* virt_to_phys() */ + +static int read_physmem(uintptr_t pa, void *buf, size_t size) { + off_t off; + bfd_boolean ok; + + if (pa < dumpscn->vma) { + printf("read_physmem(%lx,%p,%lx):too small pa. vma %lx\n", pa, buf, size, dumpscn->vma); + return 1; + } + off = pa - dumpscn->vma; + if (off >= dumpscn->size) { + printf("read_physmem(%lx,%p,%lx):too large pa. vma %lx size %lx\n", pa, buf, size, dumpscn->vma, dumpscn->size); + return 1; + } + if ((dumpscn->size - off) < size) { + printf("read_physmem(%lx,%p,%lx):too large size. vma %lx size %lx\n", pa, buf, size, dumpscn->vma, dumpscn->size); + return 1; + } + ok = bfd_get_section_contents(dumpbfd, dumpscn, buf, off, size); + if (!ok) { + bfd_perror("read_physmem:bfd_get_section_contents"); + return 1; + } + return 0; +} /* read_physmem() */ + +static int read_mem(uintptr_t va, void *buf, size_t size) { + uintptr_t pa; + int error; + + pa = virt_to_phys(va); + if (pa == NOPHYS) { + if (0) { + /* NOPHYS is usual for 'bt' command */ + perror("read_mem:virt_to_phys"); + } + return 1; + } + error = read_physmem(pa, buf, size); + if (error) { + perror("read_mem:read_physmem"); + return 1; + } + + return 0; +} /* read_mem() */ + +static int read_64(uintptr_t va, void *buf) { + return read_mem(va, buf, sizeof(uint64_t)); +} /* read_64() */ + +static int read_32(uintptr_t va, void *buf) { + return read_mem(va, buf, sizeof(uint32_t)); +} /* read_32() */ + +static int read_symbol_64(char *name, void *buf) { + uintptr_t va; + int error; + + va = lookup_symbol(name); + if (va == NOSYMBOL) { + printf("read_symbol_64(%s):lookup_symbol failed\n", name); + return 1; + } + + error = read_64(va, buf); + if (error) { + printf("read_symbol_64(%s):read_64(%#lx) failed", name, va); + return 1; + } + + return 0; +} /* read_symbol_64() */ + +enum { + /* cpu_local_var */ + CPU_LOCAL_VAR_SIZE = 0, + CURRENT_OFFSET, + RUNQ_OFFSET, + CPU_STATUS_OFFSET, + + /* process */ + CTX_OFFSET, + SCHED_LIST_OFFSET, + PROC_OFFSET, + + /* fork_tree_node */ + STATUS_OFFSET, + PID_OFFSET, + TID_OFFSET, + + END_MARK, +}; /* enum */ +static uintptr_t debug_constants[END_MARK+1]; +#define K(name) (debug_constants[name]) + +static int setup_constants(void) { + int error; + uintptr_t va; + + va = lookup_symbol("debug_constants"); + if (va == NOSYMBOL) { + perror("debug_constants"); + return 1; + } + + error = read_mem(va, debug_constants, sizeof(debug_constants)); + if (error) { + perror("debug_constants"); + return 1; + } + + if (0) { + printf("CPU_LOCAL_VAR_SIZE: %ld\n", K(CPU_LOCAL_VAR_SIZE)); + printf("CURRENT_OFFSET: %ld\n", K(CURRENT_OFFSET)); + printf("RUNQ_OFFSET: %ld\n", K(RUNQ_OFFSET)); + printf("CPU_STATUS_OFFSET: %ld\n", K(CPU_STATUS_OFFSET)); + printf("CTX_OFFSET: %ld\n", K(CTX_OFFSET)); + printf("SCHED_LIST_OFFSET: %ld\n", K(SCHED_LIST_OFFSET)); + printf("PROC_OFFSET: %ld\n", K(PROC_OFFSET)); + printf("STATUS_OFFSET: %ld\n", K(STATUS_OFFSET)); + printf("PID_OFFSET: %ld\n", K(PID_OFFSET)); + printf("TID_OFFSET: %ld\n", K(TID_OFFSET)); + printf("END_MARK: %ld\n", K(END_MARK)); + } + + return 0; +} /* setup_constants() */ + +static int setup_threads(void) { + int error; + uintptr_t clv; + int cpu; + uintptr_t current; + uintptr_t locals; + size_t locals_span; + + error = read_symbol_64("num_processors", &num_processors); + if (error) { + perror("num_processors"); + return 1; + } + + error = read_symbol_64("locals", &locals); + if (error) { + perror("locals"); + return 1; + } + + error = read_symbol_64("x86_cpu_local_variables_span", &locals_span); + if (error) { + locals_span = 4096; + } + if (0) printf("locals 0x%lx span 0x%lx\n", locals, locals_span); + + error = read_symbol_64("clv", &clv); + if (error) { + perror("clv"); + return 1; + } + + ihk_mc_switch_context = lookup_symbol("ihk_mc_switch_context"); + if (0) printf("ihk_mc_switch_context: %lx\n", ihk_mc_switch_context); + + for (cpu = 0; cpu < num_processors; ++cpu) { + uintptr_t v; + uintptr_t head; + uintptr_t entry; + + v = clv + (cpu * K(CPU_LOCAL_VAR_SIZE)); + + error = read_64(v+K(CURRENT_OFFSET), ¤t); + if (error) { + perror("current"); + return 1; + } + + head = v + K(RUNQ_OFFSET); + error = read_64(head, &entry); + if (error) { + perror("runq head"); + return 1; + } + + while (entry != head) { + uintptr_t thread; + uintptr_t proc; + int pid; + int tid; + struct thread_info *ti; + int status; + + ti = malloc(sizeof(*ti)); + if (!ti) { + perror("malloc"); + return 1; + } + + thread = entry - K(SCHED_LIST_OFFSET); + + error = read_64(thread+K(PROC_OFFSET), &proc); + if (error) { + perror("proc"); + return 1; + } + + error = read_32(thread+K(STATUS_OFFSET), &status); + if (error) { + perror("status"); + return 1; + } + + error = read_32(proc+K(PID_OFFSET), &pid); + if (error) { + perror("pid"); + return 1; + } + + error = read_32(thread+K(TID_OFFSET), &tid); + if (error) { + perror("tid"); + return 1; + } + + ti->next = NULL; + ti->status = status; + ti->pid = pid; + ti->tid = tid; + ti->cpu = (thread == current)? cpu: -1; + ti->lcpu = cpu; + ti->process = thread; + ti->clv = v; + ti->x86_clv = locals + locals_span*cpu; + + *titailp = ti; + titailp = &ti->next; + + error = read_64(entry, &entry); + if (error) { + perror("process2"); + return 1; + } + } + } + + if (!tihead) { + printf("thread not found. cpu mode forcibly\n"); + opt.cpu = 1; + } + + if (opt.cpu) { + for (cpu = 0; cpu < num_processors; ++cpu) { + uintptr_t v; + struct thread_info *ti; + int status; + uintptr_t current; + + v = clv + K(CPU_LOCAL_VAR_SIZE)*cpu; + + error = read_32(v+K(CPU_STATUS_OFFSET), &status); + if (error) { + perror("cpu.status"); + return 1; + } + + if (!status) { + continue; + } + + error = read_64(v+K(CURRENT_OFFSET), ¤t); + if (error) { + perror("current"); + return 1; + } + + ti = malloc(sizeof(*ti)); + if (!ti) { + perror("malloc"); + return 1; + } + + ti->next = NULL; + ti->status = status << 16; + ti->pid = CPU_TID_BASE + cpu; + ti->tid = CPU_TID_BASE + cpu; + ti->cpu = cpu; + ti->process = current; + ti->clv = v; + ti->x86_clv = locals + locals_span*cpu; + + *titailp = ti; + titailp = &ti->next; + } + } + + if (!tihead) { + printf("thread not found\n"); + return 1; + } + curr_thread = tihead; + + return 0; +} /* setup_threads() */ + +static int setup_symbols(char *fname) { + ssize_t needs; + bfd_boolean ok; + + symbfd = bfd_openr(fname, "elf64-x86-64"); + if (!symbfd) { + bfd_perror("bfd_openr"); + return 1; + } + + ok = bfd_check_format(symbfd, bfd_object); + if (!ok) { + bfd_perror("bfd_check_format"); + return 1; + } + + needs = bfd_get_symtab_upper_bound(symbfd); + if (needs < 0) { + bfd_perror("bfd_get_symtab_upper_bound"); + return 1; + } + + if (!needs) { + printf("no symbols\n"); + return 1; + } + + symtab = malloc(needs); + if (!symtab) { + perror("malloc"); + return 1; + } + + nsyms = bfd_canonicalize_symtab(symbfd, symtab); + if (nsyms < 0) { + bfd_perror("bfd_canonicalize_symtab"); + return 1; + } + + return 0; +} /* setup_symbols() */ + +static int setup_dump(char *fname) { + bfd_boolean ok; + + dumpbfd = bfd_fopen(opt.dump_path, "elf64-x86-64", "r", -1); + if (!dumpbfd) { + bfd_perror("bfd_fopen"); + return 1; + } + + ok = bfd_check_format(dumpbfd, bfd_object); + if (!ok) { + bfd_perror("bfd_check_format"); + return 1; + } + + dumpscn = bfd_get_section_by_name(dumpbfd, "physmem"); + if (!dumpscn) { + bfd_perror("bfd_get_section_by_name"); + return 1; + } + + kernel_base = dumpscn->vma + 0x200000; + + return 0; +} /* setup_dump() */ + +static ssize_t print_hex(char *buf, char *str) { + char *p; + char *q; + + q = buf; + for (p = str; *p != '\0'; ++p) { + q += sprintf(q, "%02x", *p); + } + *q = '\0'; + + return (q - buf); +} /* print_hex() */ + +static ssize_t print_bin(char *buf, void *data, size_t size) { + uint8_t *p; + char *q; + int i; + + p = data; + q = buf; + for (i = 0; i < size; ++i) { + q += sprintf(q, "%02x", *p); + ++p; + } + *q = '\0'; + + return (q - buf); +} /* print_bin() */ + +static void command(char *cmd, char *res) { + char *p; + char *rbp; + + p = cmd; + rbp = res; + + do { + if (!strncmp(p, "qSupported", 10)) { + rbp += sprintf(rbp, "PacketSize=1024"); + rbp += sprintf(rbp, ";qXfer:features:read+"); + } + else if (!strncmp(p, "Hg", 2)) { + int n; + int tid; + struct thread_info *ti; + + p += 2; + n = sscanf(p, "%x", &tid); + if (n != 1) { + printf("cannot parse 'Hg' cmd: \"%s\"\n", p); + break; + } + if (tid) { + for (ti = tihead; ti; ti = ti->next) { + if (ti->tid == tid) { + break; + } + } + if (!ti) { + printf("invalid tid %#x\n", tid); + break; + } + curr_thread = ti; + } + rbp += sprintf(rbp, "OK"); + } + else if (!strcmp(p, "Hc-1")) { + rbp += sprintf(rbp, "OK"); + } + else if (!strcmp(p, "?")) { + rbp += sprintf(rbp, "S02"); + } + else if (!strcmp(p, "qC")) { + rbp += sprintf(rbp, "QC%x", curr_thread->tid); + } + else if (!strcmp(p, "qAttached")) { + rbp += sprintf(rbp, "1"); + } + else if (!strncmp(p, "qXfer:features:read:target.xml:", 31)) { + char *str = + "" + "i386:x86-64" + ""; + rbp += sprintf(rbp, "l"); + if (0) + rbp += print_hex(rbp, str); + rbp += sprintf(rbp, "%s", str); + } + else if (!strcmp(p, "D")) { + rbp += sprintf(rbp, "OK"); + f_done = 1; + } + else if (!strcmp(p, "g")) { + if (curr_thread->cpu < 0) { + struct x86_kregs { + uintptr_t rsp, rbp, rbx, rsi; + uintptr_t rdi, r12, r13, r14; + uintptr_t r15, rflags, rsp0; + }; + + int error; + struct x86_kregs kregs; + + error = read_mem(curr_thread->process+K(CTX_OFFSET), + &kregs, sizeof(kregs)); + if (error) { + perror("read_mem"); + break; + } + + rbp += sprintf(rbp, "xxxxxxxxxxxxxxxx"); /* rax */ + rbp += print_bin(rbp, &kregs.rbx, sizeof(uint64_t)); + rbp += sprintf(rbp, "xxxxxxxxxxxxxxxx"); /* rcx */ + rbp += sprintf(rbp, "xxxxxxxxxxxxxxxx"); /* rdx */ + rbp += print_bin(rbp, &kregs.rsi, sizeof(uint64_t)); + rbp += print_bin(rbp, &kregs.rdi, sizeof(uint64_t)); + rbp += print_bin(rbp, &kregs.rbp, sizeof(uint64_t)); + rbp += print_bin(rbp, &kregs.rsp, sizeof(uint64_t)); + rbp += sprintf(rbp, "xxxxxxxxxxxxxxxx"); /* r8 */ + rbp += sprintf(rbp, "xxxxxxxxxxxxxxxx"); /* r9 */ + + rbp += sprintf(rbp, "xxxxxxxxxxxxxxxx"); /* r10 */ + rbp += sprintf(rbp, "xxxxxxxxxxxxxxxx"); /* r11 */ + rbp += print_bin(rbp, &kregs.r12, sizeof(uint64_t)); + rbp += print_bin(rbp, &kregs.r13, sizeof(uint64_t)); + rbp += print_bin(rbp, &kregs.r14, sizeof(uint64_t)); + rbp += print_bin(rbp, &kregs.r15, sizeof(uint64_t)); + rbp += print_bin(rbp, &ihk_mc_switch_context, + sizeof(uint64_t)); /* rip */ + rbp += print_bin(rbp, &kregs.rflags, sizeof(uint32_t)); + rbp += sprintf(rbp, "xxxxxxxx"); /* cs */ + rbp += sprintf(rbp, "xxxxxxxx"); /* ss */ + rbp += sprintf(rbp, "xxxxxxxx"); /* ds */ + rbp += sprintf(rbp, "xxxxxxxx"); /* es */ + rbp += sprintf(rbp, "xxxxxxxx"); /* fs */ + rbp += sprintf(rbp, "xxxxxxxx"); /* gs */ + } + else { + int error; + uintptr_t regs[21]; + uint8_t *pu8; + int i; + + error = read_mem(curr_thread->x86_clv+240, + ®s, sizeof(regs)); + if (error) { + perror("read_mem"); + break; + } + + pu8 = (void *)®s; + for (i = 0; i < sizeof(regs)-4; ++i) { + rbp += sprintf(rbp, "%02x", pu8[i]); + } + } + } + else if (!strcmp(p, "mffffffff80018a82,1")) { + rbp += sprintf(rbp, "b8"); + } + else if (!strcmp(p, "mffffffff80018a82,9")) { + rbp += sprintf(rbp, "b8f2ffffff41564155"); + } + else if (!strncmp(p, "m", 1)) { + int n; + uintptr_t start; + size_t size; + uintptr_t addr; + int error; + uint8_t u8; + + ++p; + n = sscanf(p, "%lx,%lx", &start, &size); + if (n != 2) { + break; + } + + for (addr = start; addr < (start + size); ++addr) { + error = read_mem(addr, &u8, sizeof(u8)); + if (error) { + u8 = 0xE5; + } + rbp += sprintf(rbp, "%02x", u8); + } + } + else if (!strcmp(p, "qTStatus")) { + rbp += sprintf(rbp, "T0;tnotrun:0"); + } + else if (!strncmp(p, "qXfer:memory-map:read::", 23)) { + char *str = + "" + "" + ""; + rbp += sprintf(rbp, "l"); + if (0) + rbp += print_hex(rbp, str); + rbp += sprintf(rbp, "%s", str); + } + else if (!strncmp(p, "T", 1)) { + int n; + int tid; + struct thread_info *ti; + + p += 1; + n = sscanf(p, "%x", &tid); + if (n != 1) { + printf("cannot parse 'T' cmd: \"%s\"\n", p); + break; + } + for (ti = tihead; ti; ti = ti->next) { + if (ti->tid == tid) { + break; + } + } + if (!ti) { + printf("invalid tid %#x\n", tid); + break; + } + rbp += sprintf(rbp, "OK"); + } + else if (!strcmp(p, "qfThreadInfo")) { + struct thread_info *ti; + + for (ti = tihead; ti; ti = ti->next) { + if (ti == tihead) { + rbp += sprintf(rbp, "m%x", ti->tid); + } + else { + rbp += sprintf(rbp, ",%x", ti->tid); + } + } + } + else if (!strcmp(p, "qsThreadInfo")) { + rbp += sprintf(rbp, "l"); + } + else if (!strncmp(p, "qThreadExtraInfo,", 17)) { + int n; + int tid; + struct thread_info *ti; + char buf[64]; + char *q; + + p += 17; + n = sscanf(p, "%x", &tid); + if (n != 1) { + printf("cannot parse 'qThreadExtraInfo' cmd: \"%s\"\n", p); + break; + } + for (ti = tihead; ti; ti = ti->next) { + if (ti->tid == tid) { + break; + } + } + if (!ti) { + printf("invalid tid %#x\n", tid); + break; + } + q = buf; + if (ti->status & PS_RUNNING) { + q += sprintf(q, "running on cpu%d", ti->cpu); + } + else if (ti->status & (PS_INTERRUPTIBLE | PS_UNINTERRUPTIBLE)) { + q += sprintf(q, "waiting on cpu%d", ti->lcpu); + } + else if (ti->status & PS_STOPPED) { + q += sprintf(q, "stopped on cpu%d", ti->lcpu); + } + else if (ti->status & PS_TRACED) { + q += sprintf(q, "traced on cpu%d", ti->lcpu); + } + else if (ti->status == CS_IDLE) { + q += sprintf(q, "cpu%d idle", ti->cpu); + } + else if (ti->status == CS_RUNNING) { + q += sprintf(q, "cpu%d running", ti->cpu); + } + else if (ti->status == CS_RESERVED) { + q += sprintf(q, "cpu%d reserved", ti->cpu); + } + else { + q += sprintf(q, "status=%#x", ti->status); + } + if (ti->tid != ti->pid) { + q += sprintf(q, ",pid=%d", ti->pid); + } + rbp += print_hex(rbp, buf); + } + } while (0); + + *rbp = '\0'; + return; +} /* command() */ + +static void options(int argc, char *argv[]) { + memset(&opt, 0, sizeof(opt)); + opt.kernel_path = "./kernel.img"; + opt.dump_path = "./mcdump"; + + for (;;) { + int c; + + c = getopt(argc, argv, "cd:hk:"); + if (c < 0) { + break; + } + switch (c) { + case 'h': + case '?': + opt.help = 1; + break; + case 'c': + opt.cpu = 1; + break; + case 'k': + opt.kernel_path = optarg; + break; + case 'd': + opt.dump_path = optarg; + break; + } + } + if (optind < argc) { + opt.help = 1; + } + + return; +} /* options() */ + +static int sock = -1; +static FILE *ifp = NULL; +static FILE *ofp = NULL; + +static int start_gdb(void) { + struct sockaddr_in sin; + socklen_t slen; + int error; + pid_t pid; + int ss; + + sock = socket(PF_INET, SOCK_STREAM, 0); + if (sock < 0) { + perror("socket"); + return 1; + } + + error = listen(sock, SOMAXCONN); + if (error) { + perror("listen"); + return 1; + } + + slen = sizeof(sin); + error = getsockname(sock, (struct sockaddr *)&sin, &slen); + if (error) { + perror("getsockname"); + return 1; + } + + pid = fork(); + if (pid == (pid_t)-1) { + perror("fork"); + return 1; + } + + if (!pid) { + char buf[32]; + + sprintf(buf, "target remote :%d", ntohs(sin.sin_port)); + execlp("gdb", "eclair", "-q", "-ex", "set prompt (eclair) ", + "-ex", buf, opt.kernel_path, NULL); + perror("execlp"); + return 3; + } + + ss = accept(sock, NULL, NULL); + if (ss < 0) { + perror("accept"); + return 1; + } + + ifp = fdopen(ss, "r"); + if (!ifp) { + perror("fdopen(r)"); + return 1; + } + + ofp = fdopen(ss, "r+"); + if (!ofp) { + perror("fdopen(r+)"); + return 1; + } + + return 0; +} /* start_gdb() */ + +static void print_usage(void) { + fprintf(stderr, "usage: eclair [-ch] [-d ] [-k ]\n"); + return; +} /* print_usage() */ + +int main(int argc, char *argv[]) { + int c; + int error; + int mode; + uint8_t sum; + uint8_t check; + static char lbuf[1024]; + static char rbuf[1024]; + static char cbuf[3]; + char *lbp; + char *p; + + printf("eclair 0.20151110\n"); + options(argc, argv); + if (opt.help) { + print_usage(); + return 2; + } + + error = setup_symbols(opt.kernel_path); + if (error) { + perror("setup_symbols"); + print_usage(); + return 1; + } + + error = setup_dump(opt.dump_path); + if (error) { + perror("setup_dump"); + print_usage(); + return 1; + } + + error = setup_constants(); + if (error) { + perror("setup_constants"); + return 1; + } + + error = setup_threads(); + if (error) { + perror("setup_threads"); + return 1; + } + + error = start_gdb(); + if (error) { + perror("start_gdb"); + return 1; + } + + mode = 0; + sum = 0; + lbp = NULL; + while (!f_done) { + c = fgetc(ifp); + if (c < 0) { + break; + } + + if (mode == 0) { + if (c == '$') { + mode = 1; + sum = 0; + lbp = lbuf; + continue; + } + } + if (mode == 1) { + if (c == '#') { + mode = 2; + *lbp = '\0'; + continue; + } + sum += c; + *lbp++ = c; + } + if (mode == 2) { + cbuf[0] = c; + mode = 3; + continue; + } + if (mode == 3) { + cbuf[1] = c; + cbuf[2] = '\0'; + check = strtol(cbuf, NULL, 16); + if (check != sum) { + mode = 0; + fputc('-', ofp); + continue; + } + mode = 0; + fputc('+', ofp); + command(lbuf, rbuf); + sum = 0; + for (p = rbuf; *p != '\0'; ++p) { + sum += *p; + } + fprintf(ofp, "$%s#%02x", rbuf, sum); + fflush(ofp); + continue; + } + } + + return 0; +} /* main() */