/* * Copyright 1999 Silicon Graphics, Inc. All rights reserved. */ /* * lkcd_x86_trace.c * * Copyright (C) 2002-2012, 2017-2018 David Anderson * Copyright (C) 2002-2012, 2017-2018 Red Hat, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Adapted as noted from the following LKCD files: * * lkcdutils-4.1/lcrash/arch/i386/lib/dis.c * lkcdutils-4.1/lcrash/arch/i386/lib/trace.c * lkcdutils-4.1/libutil/kl_queue.c */ #ifdef X86 #ifdef REDHAT #include "lkcd_x86_trace.h" #undef XEN_HYPER_MODE static int XEN_HYPER_MODE(void) { return (pc->flags & XEN_HYPER) != 0; } static void *kl_alloc_block(int, int); static void kl_free_block(void *); static void GET_BLOCK(kaddr_t, unsigned, void *); static void kl_get_kaddr(kaddr_t, void *); static char *kl_funcname(kaddr_t); static kaddr_t kl_funcaddr(kaddr_t); static syment_t *kl_lkup_symaddr(kaddr_t); static k_error_t kl_get_task_struct(kaddr_t, int, void *); static kaddr_t kl_kernelstack(kaddr_t); static kaddr_t get_call_pc(kaddr_t); static kaddr_t get_call_pc_v2(kaddr_t); static int get_jmp_instr(kaddr_t, kaddr_t, kaddr_t *, char *, char **); static int is_push(unsigned int); static int is_pop(unsigned int); static int get_framesize(kaddr_t, struct bt_info *); static int cache_framesize(int, kaddr_t funcaddr, int *, void **); struct framesize_cache; static int framesize_modify(struct framesize_cache *); struct framesize_mods; static int compiler_matches(struct framesize_mods *); static sframe_t *alloc_sframe(trace_t *, int); static void free_sframes(trace_t *); static void free_trace_rec(trace_t *); static void clean_trace_rec(trace_t *); static int setup_trace_rec(kaddr_t, kaddr_t, int, trace_t *); static int valid_ra(kaddr_t); static int valid_ra_function(kaddr_t, char *); static int eframe_incr(kaddr_t, char *); static int find_trace(kaddr_t, kaddr_t, kaddr_t, kaddr_t, trace_t *, int); static void dump_stack_frame(trace_t *, sframe_t *, FILE *); static void print_trace(trace_t *, int, FILE *); static int eframe_type(uaddr_t *); static char *funcname_display(char *, ulong, struct bt_info *, char *); static void print_eframe(FILE *, uaddr_t *); static void trace_banner(FILE *); static void print_kaddr(kaddr_t, FILE *, int); int do_text_list(kaddr_t, int, FILE *); int print_traces(struct bt_info *, int, int, FILE *); static int get_instr_info(kaddr_t, instr_rec_t *); static instr_rec_t *get_instr_stream(kaddr_t, int, int); static void free_instr_stream(instr_rec_t *); static trace_t *alloc_trace_rec(int); static void kl_enqueue(element_t**, element_t*); static element_t *kl_dequeue(element_t**); static void handle_trace_error(struct bt_info *, int, FILE *); static int verify_back_trace(struct bt_info *); static int recoverable(struct bt_info *, FILE *); static void fill_instr_cache(kaddr_t, char *); static void do_bt_reference_check(struct bt_info *, sframe_t *); static void print_stack_entry(struct bt_info *, int, ulong, ulong, char *, sframe_t *, FILE *); static struct syment *eframe_label(char *, ulong); static int dump_framesize_cache(FILE *, struct framesize_cache *); static int modify_framesize_cache_entry(FILE *, ulong, int); static int framesize_debug(struct bt_info *, FILE *); static int kernel_entry_from_user_space(sframe_t *, struct bt_info *); k_error_t klib_error = 0; static void * kl_alloc_block(int size, int flags) { return ((void *)GETBUF(size)); } static void kl_free_block(void *blk) { if (blk) FREEBUF(blk); } static void GET_BLOCK(kaddr_t addr, unsigned size, void *buffer) { KL_ERROR = 0; if (!readmem(addr, KVADDR, (void *)buffer, (ulong)size, "GET_BLOCK", RETURN_ON_ERROR|QUIET)) { console("GET_BLOCK: %lx (%d/0x%x)\n", addr, size, size); KL_ERROR = KLE_INVALID_READ; } } static void kl_get_kaddr(kaddr_t addr, void *bp) { KL_ERROR = 0; GET_BLOCK(addr, 4, bp); } static char * kl_funcname(kaddr_t pc) { struct syment *sp; char *buf, *name; struct load_module *lm; if ((sp = value_search(pc, NULL))) { if (STREQ(sp->name, "_stext") && (sp->value == (sp+1)->value)) sp++; switch (sp->type) { case 'r': if (strstr(sp->name, "_interrupt") || STREQ(sp->name, "call_do_IRQ")) return sp->name; break; case 't': case 'T': return sp->name; } if (is_kernel_text(pc)) return sp->name; } if (IS_MODULE_VADDR(pc)) { buf = GETBUF(BUFSIZE); name = &buf[BUFSIZE/2]; if (module_symbol(pc, NULL, NULL, buf, output_radix)) { sprintf(name, "(%s)", buf); return name; } else { FREEBUF(buf); return "(unknown module)"; } } if ((lm = init_module_function(pc))) return ("init_module"); return NULL; } static kaddr_t kl_funcaddr(kaddr_t pc) { struct syment *sp; struct load_module *lm; if ((sp = value_search(pc, NULL))) { switch (sp->type) { case 'r': if (strstr(sp->name, "_interrupt") || STREQ(sp->name, "call_do_IRQ")) return sp->value; break; case 't': case 'T': return sp->value; } if (is_kernel_text(pc)) return sp->value; } if ((lm = init_module_function(pc))) return lm->mod_init_module_ptr; return((kaddr_t)NULL); } static struct syment init_module_syment = { .name = "init_module", .type = 't', }; static syment_t * kl_lkup_symaddr(kaddr_t addr) { struct syment *sp; struct load_module *lm; if ((sp = value_search(addr, NULL))) return sp; if ((lm = init_module_function(addr))) { init_module_syment.value = lm->mod_init_module_ptr; return &init_module_syment; } return NULL; } static k_error_t kl_get_task_struct(kaddr_t value, int mode, void *tsp) { KL_ERROR = 0; if (value == tt->last_task_read) BCOPY(tt->task_struct, tsp, TASK_STRUCT_SZ); else GET_BLOCK(value, TASK_STRUCT_SZ, tsp); return KL_ERROR; } static kaddr_t kl_kernelstack(kaddr_t task) { kaddr_t saddr; return (saddr = (task + KSTACK_SIZE)); } static void print_kaddr(kaddr_t kaddr, FILE *ofp, int flag) { fprintf(ofp, "%lx", (ulong)kaddr); } #endif /* REDHAT */ /* * lkcdutils-4.1/lcrash/arch/i386/lib/trace.c */ #ifndef REDHAT /* * Copyright 1999 Silicon Graphics, Inc. All rights reserved. */ #include #include #include #endif /* !REDHAT */ /* * get_call_pc() */ kaddr_t get_call_pc(kaddr_t ra) { kaddr_t addr = 0; instr_rec_t *irp; if (!(irp = get_instr_stream(ra, 1, 0))) { return((kaddr_t)NULL); } if (!irp->prev) { free_instr_stream(irp); return((kaddr_t)NULL); } if ((irp->prev->opcode == 0x00e8) || (irp->prev->opcode == 0xff02)) { addr = irp->prev->addr; } free_instr_stream(irp); /* * If the old LKCD code fails, try disassembling... */ if (!addr) return get_call_pc_v2(ra); return(addr); } kaddr_t get_call_pc_v2(kaddr_t ra) { int c ATTRIBUTE_UNUSED; int line, len; kaddr_t addr, addr2; ulong offset; struct syment *sp; char *arglist[MAXARGS]; char buf[BUFSIZE]; if ((sp = value_search(ra, &offset))) { if (offset == 0) return 0; } else return 0; addr = 0; for (len = 2; len < 8; len++) { open_tmpfile2(); sprintf(buf, "x/2i 0x%x", ra - len); if (!gdb_pass_through(buf, pc->tmpfile2, GNU_RETURN_ON_ERROR)) { close_tmpfile2(); return 0; } rewind(pc->tmpfile2); line = 1; while (fgets(buf, BUFSIZE, pc->tmpfile2)) { c = parse_line(buf, arglist); if ((line == 1) && !STREQ(arglist[2], "call")) break; if (line == 2) { addr2 = (kaddr_t)htol(arglist[0], RETURN_ON_ERROR|QUIET, 0); if (addr2 == ra) { addr = ra - len; break; } } line++; } close_tmpfile2(); if (addr) { if (CRASHDEBUG(1)) { fprintf(fp, "get_call_pc_v2(ra: %x) -> %x -> ", ra, addr); if (value_to_symstr(addr, buf, 0)) fprintf(fp, "%s", buf); fprintf(fp, "\n"); } break; } } return addr; } /* * get_jmp_instr() */ int get_jmp_instr(kaddr_t addr, kaddr_t isp, kaddr_t *caddr, char *fname, char **cfname) { kaddr_t a; int offset; instr_rec_t *irp; if (!(irp = get_instr_stream(addr, 1, 0))) { return(1); } if (!irp->prev) { free_instr_stream(irp); return(1); } irp = irp->prev; if (!(irp->opcode == 0x00e8) && !(irp->opcode == 0xff02)) { free_instr_stream(irp); return(1); } /* Check for the easiest case first... */ if (irp->opcode == 0xe8) { a = irp->operand[0].op_addr; if ((*cfname = kl_funcname(a))) { *caddr = a; } } else if (irp->opcode == 0xff02) { switch (irp->modrm) { case 0x14: if (irp->sib == 0x85) { kl_get_kaddr(addr - 4, &a); if (KL_ERROR) { free_instr_stream(irp); return(1); } if (strstr(fname, "system_call")) { GET_BLOCK(isp + 28, 4, &offset); a += (offset * 4); kl_get_kaddr(a, &a); if ((*cfname = kl_funcname(a))) { *caddr = a; } } } break; case 0xc2: /* EAX */ case 0xca: /* ECX */ case 0xd2: /* EDX */ case 0xda: /* EBX */ case 0xea: /* EBP */ case 0xf2: /* ESI */ case 0xfa: /* EDI */ break; } } free_instr_stream(irp); return(0); } /* * is_push() */ int is_push(unsigned int opcode) { switch(opcode) { case 0x0006: case 0x000e: case 0x0016: case 0x001e: case 0x0050: case 0x0051: case 0x0052: case 0x0053: case 0x0054: case 0x0055: case 0x0056: case 0x0057: case 0x0068: case 0x006a: case 0x009c: case 0x0fa0: case 0x0fa8: case 0xff06: return(1); case 0x0060: return(2); } return(0); } /* * is_pop() */ int is_pop(unsigned int opcode) { switch(opcode) { case 0x0007: case 0x0017: case 0x001f: case 0x0058: case 0x0059: case 0x005a: case 0x005b: case 0x005c: case 0x005d: case 0x005e: case 0x005f: case 0x008f: case 0x009d: case 0x0fa1: case 0x0fa9: return(1); case 0x0061: return(2); } return(0); } #ifdef REDHAT #define FRAMESIZE_VALIDATE (0x1) struct framesize_cache { kaddr_t pc; int flags; int frmsize; int bp_adjust; }; #define FRAMESIZE_CACHE (200) static struct framesize_cache framesize_cache[FRAMESIZE_CACHE] = {{0}}; static struct framesize_cache framesize_cache_empty = {0}; #define FSZ_QUERY (1) #define FSZ_VALIDATE (2) #define FSZ_ENTER (3) #define FRAMESIZE_CACHE_QUERY(pc,szp) cache_framesize(FSZ_QUERY, pc, szp, NULL) #define FRAMESIZE_CACHE_ENTER(pc,szp) cache_framesize(FSZ_ENTER, pc, szp, NULL) #define FRAMESIZE_CACHE_VALIDATE(pc,fcpp) cache_framesize(FSZ_VALIDATE, pc, NULL, fcpp) static int cache_framesize(int cmd, kaddr_t funcaddr, int *fsize, void **ptr) { int i; static ulong last_cleared = 0; retry: for (i = 0; i < FRAMESIZE_CACHE; i++) { if (framesize_cache[i].pc == funcaddr) { switch (cmd) { case FSZ_VALIDATE: *ptr = &framesize_cache[i]; return TRUE; case FSZ_QUERY: *fsize = framesize_cache[i].frmsize; return TRUE; case FSZ_ENTER: *fsize = framesize_cache[i].frmsize; return TRUE; } } /* * The entry does not exist. * * If FSZ_QUERY or FSZ_VALIDATE, return their * no-such-entry indications. * * Otherwise, load up the entry with the new data, and * and modify it with known kludgery. */ if (framesize_cache[i].pc == 0) { switch (cmd) { case FSZ_QUERY: return FALSE; case FSZ_VALIDATE: *ptr = &framesize_cache_empty; return FALSE; case FSZ_ENTER: framesize_cache[i].pc = funcaddr; framesize_cache[i].frmsize = *fsize; framesize_cache[i].bp_adjust = 0; framesize_modify(&framesize_cache[i]); *fsize = framesize_cache[i].frmsize; return TRUE; } } } console("framesize_cache is full\n"); /* * No place to put it, or it doesn't exist. */ switch (cmd) { case FSZ_VALIDATE: *ptr = &framesize_cache_empty; return FALSE; case FSZ_QUERY: return FALSE; case FSZ_ENTER: BZERO(&framesize_cache[last_cleared % FRAMESIZE_CACHE], sizeof(struct framesize_cache)); last_cleared++; goto retry; } return FALSE; /* can't get here -- for compiler happiness */ } /* * More kludgery for compiler oddities. */ #define COMPILER_VERSION_MASK (1) /* deprecated -- usable up to 3.3.3 */ #define COMPILER_VERSION_EQUAL (2) #define COMPILER_VERSION_START (3) #define COMPILER_VERSION_RANGE (4) struct framesize_mods { char *funcname; char *called_function; ulong compiler_flag; ulong compiler1; ulong compiler2; int pre_adjust; int post_adjust; } framesize_mods[] = { { "do_select", "schedule_timeout", COMPILER_VERSION_START, GCC(3,3,2), 0, 0, 0 }, { "svc_recv", "schedule_timeout", COMPILER_VERSION_START, GCC(3,3,2), 0, 0, 0 }, { "__down_interruptible", "schedule", COMPILER_VERSION_START, GCC(3,3,2), 0, 0, 0 }, { "netconsole_netdump", NULL, COMPILER_VERSION_START, GCC(3,3,2), 0, 0, -28 }, { "generic_file_write", NULL, COMPILER_VERSION_EQUAL, GCC(2,96,0), 0, 0, 20 }, { "block_prepare_write", NULL, COMPILER_VERSION_EQUAL, GCC(2,96,0), 0, 0, 72 }, { "receive_chars", NULL, COMPILER_VERSION_EQUAL, GCC(2,96,0), 0, 0, 48 }, { "default_idle", NULL, COMPILER_VERSION_START, GCC(2,96,0), 0, -4, 0 }, { "hidinput_hid_event", NULL, COMPILER_VERSION_START, GCC(4,1,2), 0, 0, 28 }, { NULL, NULL, 0, 0, 0, 0, 0 }, }; static int framesize_modify(struct framesize_cache *fc) { char *funcname; struct framesize_mods *fmp; if (!(funcname = kl_funcname(fc->pc))) return FALSE; if (fc->frmsize < 0) { if (CRASHDEBUG(1)) error(INFO, "bogus framesize: %d for pc: %lx (%s)\n", fc->frmsize, fc->pc, funcname); fc->frmsize = 0; } for (fmp = &framesize_mods[0]; fmp->funcname; fmp++) { if (STREQ(funcname, fmp->funcname) && compiler_matches(fmp)) break; } if (!fmp->funcname) return FALSE; if (fmp->pre_adjust) fc->frmsize += fmp->pre_adjust; if (fmp->post_adjust) fc->bp_adjust = fmp->post_adjust; if (fmp->called_function) { if (STREQ(fmp->called_function,x86_function_called_by(fc->pc))) fc->flags |= FRAMESIZE_VALIDATE; } return TRUE; } static int compiler_matches(struct framesize_mods *fmp) { switch (fmp->compiler_flag) { case COMPILER_VERSION_MASK: if (fmp->compiler1 & (kt->flags & GCC_VERSION_DEPRECATED)) return TRUE; break; case COMPILER_VERSION_EQUAL: if (THIS_GCC_VERSION == fmp->compiler1) return TRUE; break; case COMPILER_VERSION_START: if (THIS_GCC_VERSION >= fmp->compiler1) return TRUE; break; case COMPILER_VERSION_RANGE: if ((THIS_GCC_VERSION >= fmp->compiler1) && (THIS_GCC_VERSION <= fmp->compiler2)) return TRUE; break; } return FALSE; } static int dump_framesize_cache(FILE *ofp, struct framesize_cache *fcp) { int i, count; struct syment *sp, *spm; ulong offset; int once; for (i = once = count = 0; i < FRAMESIZE_CACHE; i++) { if (framesize_cache[i].pc == 0) break; count++; if (fcp && (fcp != &framesize_cache[i])) continue; if (!once) { fprintf(ofp, "RET ADDR FSZ BPA V FUNCTION\n"); once++; } fprintf(ofp, "%8x %4d %4d %s ", framesize_cache[i].pc, framesize_cache[i].frmsize, framesize_cache[i].bp_adjust, framesize_cache[i].flags & FRAMESIZE_VALIDATE ? "V" : "-"); if ((sp = value_search(framesize_cache[i].pc, &offset)) || (spm = kl_lkup_symaddr(framesize_cache[i].pc))) { if (sp) fprintf(ofp, "(%s+", sp->name); else { fprintf(ofp, "(%s+", spm->name); offset = framesize_cache[i].pc - spm->value; } switch (pc->output_radix) { case 10: fprintf(ofp, "%ld)", offset); break; default: case 16: fprintf(ofp, "%lx)", offset); break; } } fprintf(ofp, "\n"); if (fcp) return 0; } if (!count) fprintf(ofp, "framesize cache emtpy\n"); if (kt->flags & RA_SEEK) fprintf(ofp, "RA_SEEK: ON\n"); if (kt->flags & NO_RA_SEEK) fprintf(ofp, "NO_RA_SEEK: ON\n"); return count; } static int modify_framesize_cache_entry(FILE *ofp, ulong eip, int framesize) { int i, found, all_cleared; for (i = found = all_cleared = 0; i < FRAMESIZE_CACHE; i++) { if (!eip) { switch (framesize) { case -1: framesize_cache[i].flags |= FRAMESIZE_VALIDATE; break; case -2: framesize_cache[i].flags &= ~FRAMESIZE_VALIDATE; break; default: framesize_cache[i].pc = 0; framesize_cache[i].frmsize = 0; framesize_cache[i].flags = 0; all_cleared = TRUE; break; } continue; } if (framesize_cache[i].pc == 0) break; if (framesize_cache[i].pc == eip) { found++; switch (framesize) { case -1: framesize_cache[i].flags |= FRAMESIZE_VALIDATE; break; case -2: framesize_cache[i].flags &= ~FRAMESIZE_VALIDATE; break; default: framesize_cache[i].frmsize = framesize; break; } dump_framesize_cache(ofp, &framesize_cache[i]); return TRUE; } } if (eip && !found) fprintf(ofp, "eip: %lx not found in framesize cache\n", eip); if (all_cleared) fprintf(ofp, "framesize cache cleared\n"); return FALSE; } /* * If eip, look for it and replace its frmsize with the passed-in value. * If no eip, frmsize of zero means clear the cache, non-zero displays it. */ static int framesize_debug(struct bt_info *bt, FILE *ofp) { ulong eip; int frmsize; eip = bt->hp->eip; frmsize = (int)bt->hp->esp; if (!eip) { switch (frmsize) { case 0: case -1: case -2: return modify_framesize_cache_entry(ofp, 0, frmsize); default: return dump_framesize_cache(ofp, NULL); } } return modify_framesize_cache_entry(ofp, eip, frmsize); } #endif /* REDHAT */ /* #define FRMSIZE_DBG 1 #define FRMSIZE2_DBG 1 */ /* * get_framesize() */ int #ifdef REDHAT get_framesize(kaddr_t pc, struct bt_info *bt) #else get_framesize(kaddr_t pc) #endif { int size, ret, frmsize = 0; kaddr_t addr; instr_rec_t irp; syment_t *sp; #ifdef REDHAT int check_IRQ_stack_switch = 0; syment_t *jmpsp, *trampsp; ulong offset; int frmsize_restore = 0; int last_add = 0; if (FRAMESIZE_CACHE_QUERY(pc, &frmsize)) return frmsize; frmsize = 0; #endif if (!(sp = kl_lkup_symaddr(pc))) { return(0); } #ifdef REDHAT if (STREQ(sp->name, "do_IRQ") && (tt->flags & IRQSTACKS)) check_IRQ_stack_switch++; if (STREQ(sp->name, "stext_lock") || STRNEQ(sp->name, ".text.lock.")) { jmpsp = x86_text_lock_jmp(pc, &offset); if (jmpsp) { console("get_framesize: stext_lock %lx => %s\n", pc, jmpsp->name); pc = jmpsp->value + offset; sp = jmpsp; } } if ((trampsp = x86_is_entry_tramp_address(pc, &offset))) { if (STREQ(sp->name, "system_call")) return 0; pc = trampsp->value + offset; } #endif #ifdef FRMSIZE_DBG fprintf(stderr, "get_framesize(): pc=0x%x (0x%x:%s)\n", pc, sp->s_addr, sp->s_name); #endif addr = sp->s_addr; while (addr <= pc) { bzero(&irp, sizeof(irp)); irp.aflag = 1; irp.dflag = 1; if (!(size = get_instr_info(addr, &irp))) { fprintf(stderr, "ZERO SIZE!!\n"); return(-1); } if (size != irp.size) { fprintf(stderr, "SIZE DOES NOT MATCH!!\n"); } #ifdef REDHAT /* * Account for do_IRQ() stack switch. */ if (check_IRQ_stack_switch && (irp.opcode == 0xff02) && (irp.operand[0].op_reg == 0x7)) break; /* * Account for embedded "ret" instructions screwing up * the frame size calculation. */ if (irp.opcode == 0xc3) { frmsize += frmsize_restore; frmsize_restore = 0; last_add = FALSE; } else if ((irp.opcode == 0x8300) && (irp.operand[0].op_reg == R_eSP)) { frmsize_restore += irp.operand[1].op_addr; last_add = TRUE; } else if ((irp.opcode == 0x8100) && (irp.operand[0].op_reg == R_eSP)) { frmsize_restore += irp.operand[1].op_addr; last_add = TRUE; } else if ((ret = is_pop(irp.opcode))) { if (ret == 2) frmsize_restore += (8 * 4); else frmsize_restore += 4; last_add = FALSE; } else { if (last_add) last_add = FALSE; else frmsize_restore = 0; } #endif /* REDHAT */ #ifdef REDHAT if ((irp.opcode == 0x8300) || (irp.opcode == 0x8100)) { #else if (irp.opcode == 0x8300) { #endif /* e.g., addl $0x8,%esp */ if (irp.operand[0].op_reg == R_eSP) { frmsize -= irp.operand[1].op_addr; #ifdef FRMSIZE_DBG fprintf(stderr, " addl --> 0x%x: -%d\n", addr, irp.operand[1].op_addr); #endif } } else if ((irp.opcode == 0x8305) || (irp.opcode == 0x8105)) { /* e.g., subl $0x40,%esp */ if (irp.operand[0].op_reg == R_eSP) { frmsize += irp.operand[1].op_addr; #ifdef FRMSIZE_DBG fprintf(stderr, " subl --> 0x%x: +%d\n", addr, irp.operand[1].op_addr); #endif } } else if ((ret = is_push(irp.opcode))) { if (ret == 2) { frmsize += (8 * 4); #ifdef FRMSIZE_DBG fprintf(stderr, " pusha --> 0x%x: +%d\n", addr, (8 * 4)); #endif } else { frmsize += 4; #ifdef FRMSIZE_DBG fprintf(stderr, " pushl --> 0x%x: +%d\n" , addr, 4); #endif } } else if ((ret = is_pop(irp.opcode))) { if (ret == 2) { frmsize -= (8 * 4); #ifdef FRMSIZE_DBG fprintf(stderr, " popa --> 0x%x: -%d\n", addr, (8 * 4)); #endif } else { frmsize -= 4; #ifdef FRMSIZE_DBG fprintf(stderr, " popl --> 0x%x: -%d\n", addr, 4); #endif } #ifdef FRMSIZE2_DBG } else { fprintf(stderr, " 0x%x: opcode=0x%x\n", addr, irp.opcode); #endif } addr += size; } #ifdef REDHAT /* * Account for fact that schedule may not "call" anybody, plus * the difference between gcc 3.2 and earlier compilers. */ if (STREQ(kl_funcname(pc), "schedule") && !(bt->flags & BT_CONTEXT_SWITCH)) frmsize -= THIS_GCC_VERSION == GCC(3,2,0) ? 4 : 8; FRAMESIZE_CACHE_ENTER(pc, &frmsize); #endif return(frmsize); } #ifndef REDHAT /* * print_pc() */ void print_pc(kaddr_t addr, FILE *ofp) { int offset = 0; syment_t *sp; if ((sp = kl_lkup_symaddr(addr))) { offset = addr - sp->s_addr; } /* Print out address */ fprintf(ofp, "0x%x", addr); /* Print out symbol name */ if (sp) { if (offset) { fprintf(ofp, " <%s+%d>", sp->s_name, offset); } else { fprintf(ofp, " <%s>", sp->s_name); } } } #endif /* !REDHAT */ /* * alloc_sframe() -- Allocate a stack frame record */ sframe_t * alloc_sframe(trace_t *trace, int flags) { sframe_t *f; if (flags & C_PERM) { f = (sframe_t *)kl_alloc_block(sizeof(sframe_t), K_PERM); } else { f = (sframe_t *)kl_alloc_block(sizeof(sframe_t), K_TEMP); } if (!f) { return((sframe_t *)NULL); } f->level = trace->nframes; return(f); } /* * free_sframes() -- Free all stack frames allocated to a trace record. */ void free_sframes(trace_t *t) { sframe_t *sf; t->nframes = 0; sf = t->frame; while(t->frame) { sf = (sframe_t *)kl_dequeue((element_t **)&t->frame); if (sf->srcfile) { kl_free_block((void *)sf->srcfile); } kl_free_block((void *)sf); } t->frame = (sframe_t *)NULL; } /* * alloc_trace_rec() -- Allocate stack trace header */ trace_t * alloc_trace_rec(int flags) { trace_t *t; if (flags & C_PERM) { t = (trace_t *)kl_alloc_block(sizeof(trace_t), K_PERM); } else { t = (trace_t *)kl_alloc_block(sizeof(trace_t), K_TEMP); } return(t); } /* * free_trace_rec() -- Free memory associated with stack trace header */ void free_trace_rec(trace_t *t) { int i; if (t->tsp) { kl_free_block(t->tsp); } for (i = 0; i < STACK_SEGMENTS; i++) { if (t->stack[i].ptr) { kl_free_block((void *)t->stack[i].ptr); } } free_sframes(t); kl_free_block((void *)t); } /* * clean_trace_rec() -- Clean up stack trace record without releasing * any of the allocated memory (except sframes). */ void clean_trace_rec(trace_t *t) { int i; t->flags = 0; t->task = 0; if (t->tsp) { kl_free_block(t->tsp); t->tsp = 0; } t->stackcnt = 0; for (i = 0; i < STACK_SEGMENTS; i++) { if (t->stack[i].ptr) { t->stack[i].type = 0; t->stack[i].size = 0; t->stack[i].addr = (kaddr_t)NULL; kl_free_block((void *)t->stack[i].ptr); t->stack[i].ptr = (uaddr_t *)NULL; } } free_sframes(t); } /* * setup_trace_rec() */ int setup_trace_rec(kaddr_t saddr, kaddr_t task, int flag, trace_t *trace) { int aflag = K_TEMP; #ifdef REDHAT KL_ERROR = 0; #else kl_reset_error(); #endif if (flag & C_PERM) { aflag = K_PERM; } if (task) { trace->tsp = kl_alloc_block(TASK_STRUCT_SZ, aflag); if (kl_get_task_struct(task, 2, trace->tsp)) { kl_free_block(trace->tsp); trace->tsp = NULL; return(1); } } trace->stack[0].type = S_KERNELSTACK; trace->stack[0].size = STACK_SIZE; /* Get the base address of the stack */ trace->stack[0].addr = saddr - trace->stack[0].size; trace->stack[0].ptr = kl_alloc_block(STACK_SIZE, aflag); if (KL_ERROR) { clean_trace_rec(trace); return(1); } #ifdef REDHAT BCOPY(trace->bt->stackbuf, trace->stack[0].ptr, STACK_SIZE); #else GET_BLOCK(trace->stack[0].addr, STACK_SIZE, trace->stack[0].ptr); #endif if (KL_ERROR) { clean_trace_rec(trace); return(1); } return(0); } /* * valid_ra() */ int valid_ra(kaddr_t ra) { kaddr_t pc; if ((ra < KL_PAGE_OFFSET) || !kl_funcaddr(ra)) return(0); if ((pc = get_call_pc(ra))) return(1); return(0); } /* * valid_ra_function() * * Same as above, but ensure that it calls the funcname passed in. */ int valid_ra_function(kaddr_t ra, char *funcname) { kaddr_t pc; if ((ra < KL_PAGE_OFFSET) || !kl_funcaddr(ra)) return(0); if (!(pc = get_call_pc(ra))) return(0); if (STREQ(x86_function_called_by(ra-5), funcname)) return(1); return(0); } #ifndef REDHAT #include #endif #define KERNEL_EFRAME 0 #define USER_EFRAME 1 #define KERNEL_EFRAME_SZ 13 /* no ss and esp */ #define USER_EFRAME_SZ 15 #ifdef REDHAT #undef __KERNEL_CS #undef __KERNEL_DS #undef __USER_CS #undef __USER_DS #define __KERNEL_CS 0x10 #define __KERNEL_DS 0x18 #define __USER_CS 0x23 #define __USER_DS 0x2B #endif /* * Check if the exception frame is of kernel or user type * Is checking only DS and CS values sufficient ? */ int eframe_type(uaddr_t *int_eframe) { ushort xcs, xds; xcs = (ushort)(int_eframe[INT_EFRAME_CS] & 0xffff); xds = (ushort)(int_eframe[INT_EFRAME_DS] & 0xffff); if ((xcs == __KERNEL_CS) && (xds == __KERNEL_DS)) return KERNEL_EFRAME; #ifdef REDHAT else if ((xcs == 0x60) && (xds == 0x68)) return KERNEL_EFRAME; else if ((xcs == 0x60) && (xds == 0x7b)) return KERNEL_EFRAME; else if (XEN() && (xcs == 0x61) && (xds == 0x7b)) return KERNEL_EFRAME; #endif else if ((xcs == __USER_CS) && (xds == __USER_DS)) return USER_EFRAME; #ifdef REDHAT else if ((xcs == 0x73) && (xds == 0x7b)) return USER_EFRAME; #endif return -1; } void print_eframe(FILE *ofp, uaddr_t *regs) { int type = eframe_type(regs); #ifdef REDHAT x86_dump_eframe_common(NULL, (ulong *)regs, (type == KERNEL_EFRAME)); #else fprintf(ofp, " ebx: %08lx ecx: %08lx edx: %08lx esi: %08lx\n", regs->ebx, regs->ecx, regs->edx, regs->esi); fprintf(ofp, " edi: %08lx ebp: %08lx eax: %08lx ds: %04x\n", regs->edi, regs->ebp, regs->eax, regs->xds & 0xffff); fprintf(ofp, " es: %04x eip: %08lx cs: %04x eflags: %08lx\n", regs->xes & 0xffff, regs->eip, regs->xcs & 0xffff, regs->eflags); if (type == USER_EFRAME) fprintf(ofp, " esp: %08lx ss: %04x\n", regs->esp, regs->xss); #endif } #ifdef REDHAT #define SEEK_VALID_RA() \ { \ while (!valid_ra(ra)) { \ if ((bp + 4) < bt->stacktop) { \ bp += 4; \ ra = GET_STACK_ULONG(bp + 4); \ } else \ break; \ } \ } #define SEEK_VALID_RA_FUNCTION(F) \ { \ while (!valid_ra_function(ra, (F))) { \ if ((bp + 4) < bt->stacktop) { \ bp += 4; \ ra = GET_STACK_ULONG(bp + 4); \ } else \ break; \ } \ } #endif /* * Determine how much to increment the stack pointer to find the * exception frame associated with a generic "error_code" or "nmi" * exception. * * The incoming addr is that of the call to the generic error_code * or nmi exception handler function. Until later 2.6 kernels, the next * instruction had always been an "addl $8,%esp". However, with later * 2.6 kernels, that esp adjustment is no long valid, and there will be * an immediate "jmp" instruction. Returns 4 or 12, whichever is appropriate. * Cache the value the first time, and allow for future changes or additions. */ #define NMI_ADJ (0) #define ERROR_CODE_ADJ (1) #define EFRAME_ADJUSTS (ERROR_CODE_ADJ+1) static int eframe_adjust[EFRAME_ADJUSTS] = { 0 }; static int eframe_incr(kaddr_t addr, char *funcname) { instr_rec_t irp; kaddr_t next; int size, adj, val; if (STRNEQ(funcname, "nmi")) { adj = NMI_ADJ; val = eframe_adjust[NMI_ADJ]; } else if (strstr(funcname, "error_code")) { adj = ERROR_CODE_ADJ; val = eframe_adjust[ERROR_CODE_ADJ]; } else { adj = -1; val = 0; error(INFO, "unexpected exception frame marker: %lx (%s)\n", addr, funcname); } if (val) { console("eframe_incr(%lx, %s): eframe_adjust[%d]: %d\n", addr, funcname, adj, val); return val; } console("eframe_incr(%lx, %s): TBD:\n", addr, funcname); bzero(&irp, sizeof(irp)); irp.aflag = 1; irp.dflag = 1; if (!(size = get_instr_info(addr, &irp))) { if (CRASHDEBUG(1)) error(INFO, "eframe_incr(%lx, %s): get_instr_info(%lx) failed\n", addr, funcname, addr); return((THIS_KERNEL_VERSION > LINUX(2,6,9)) ? 4 : 12); } console(" addr: %lx size: %d opcode: 0x%x insn: \"%s\"\n", addr, size, irp.opcode, irp.opcodep->name); next = addr + size; bzero(&irp, sizeof(irp)); irp.aflag = 1; irp.dflag = 1; if (!(size = get_instr_info(next, &irp))) { if (CRASHDEBUG(1)) error(INFO, "eframe_incr(%lx, %s): get_instr_info(%lx) failed\n", addr, funcname, next); return((THIS_KERNEL_VERSION > LINUX(2,6,9)) ? 4 : 12); } console(" next: %lx size: %d opcode: 0x%x insn: \"%s\"\n", next, size, irp.opcode, irp.opcodep->name); if (STREQ(irp.opcodep->name, "jmp") || STREQ(irp.opcodep->name, "nop")) val = 4; else val = 12; if (adj >= 0) eframe_adjust[adj] = val; return val; } static int xen_top_of_stack(struct bt_info *bt, char *funcname) { ulong stkptr, contents; for (stkptr = bt->stacktop-4; stkptr > bt->stackbase; stkptr--) { contents = GET_STACK_ULONG(stkptr); if (kl_funcname(contents) == funcname) return TRUE; if (valid_ra(contents)) break; } return FALSE; } static char * xen_funcname(struct bt_info *bt, ulong pc) { char *funcname = kl_funcname(pc); if (xen_top_of_stack(bt, funcname) && (pc >= symbol_value("hypercall")) && (pc < symbol_value("ret_from_intr"))) return "hypercall"; return funcname; } static int userspace_return(kaddr_t frame, struct bt_info *bt) { ulong esp0, eframe_addr; uint32_t *stkptr, *eframeptr; if (INVALID_MEMBER(task_struct_thread) || (((esp0 = MEMBER_OFFSET("thread_struct", "esp0")) < 0) && ((esp0 = MEMBER_OFFSET("thread_struct", "sp0")) < 0))) eframe_addr = bt->stacktop - SIZE(pt_regs); else eframe_addr = ULONG(tt->task_struct + OFFSET(task_struct_thread) + esp0) - SIZE(pt_regs); if (!INSTACK(eframe_addr, bt)) return FALSE; stkptr = (uint32_t *)(bt->stackbuf + ((ulong)frame - bt->stackbase)); eframeptr = (uint32_t *)(bt->stackbuf + (eframe_addr - bt->stackbase)); while (stkptr < eframeptr) { if (is_kernel_text_offset(*stkptr)) return FALSE; stkptr++; } return TRUE; } /* * find_trace() * * Given a starting pc (start_cp), starting stack pointer (start_sp), * and stack address, check to see if a valid trace is possible. A * trace is considered valid if no errors are encountered (bad PC, * bad SP, etc.) Certain errors are tolorated however. For example, * if the current stack frame is an exception frame (e.g., VEC_*), * go ahead and return success -- even if PC and SP obtained from * the exception frame are bad (a partial trace is better than no * trace).. * * Return zero if no valid trace was found. Otherwise, return the * number of frames found. If the C_ALL flag is passed in, then * return a trace even if it is a subtrace of a trace that was * previously found. * * Parameters: * * start_pc starting program counter * start_sp starting stack pointer * check_pc if non-NULL, check to see if check_pc/check_sp * check_sp are a sub-trace of trace beginning with spc/ssp * trace structure containing all trace related info (frames, * pages, page/frame counts, etc. * flags */ int find_trace( kaddr_t start_pc, kaddr_t start_sp, kaddr_t check_pc, kaddr_t check_sp, trace_t *trace, int flags) { int curstkidx = 0, frame_size, frame_type; kaddr_t sp, pc, ra, bp, sbase, saddr, func_addr; sframe_t *curframe; char *func_name; uaddr_t *sbp, *asp; #ifdef REDHAT struct syment *sp1; ulong offset; int flag; int interrupted_system_call = FALSE; struct bt_info *bt = trace->bt; uaddr_t *pt; curframe = NULL; #endif sbp = trace->stack[curstkidx].ptr; sbase = trace->stack[curstkidx].addr; saddr = sbase + trace->stack[curstkidx].size; #ifdef REDHAT bp = start_sp + get_framesize(start_pc, bt); #else bp = start_sp + get_framesize(start_pc); #endif if (KL_ERROR || (bp < sbase) || (bp >= saddr)) { return(0); } pc = start_pc; sp = start_sp; func_name = kl_funcname(pc); #ifdef REDHAT if (STREQ(func_name, "context_switch")) bt->flags |= BT_CONTEXT_SWITCH; #endif while (pc) { /* LOOP TRAP! Make sure we are not just looping on the * same frame forever. */ if ((trace->nframes > 1) && (curframe->funcname == curframe->prev->funcname) && (curframe->sp == curframe->prev->sp)) { curframe->error = 1; #ifdef REDHAT bt->flags |= BT_LOOP_TRAP; #endif return(trace->nframes); } #ifdef REDHAT /* * If we wrap back to a lower stack location, we're cooked. */ if ((trace->nframes > 1) && (curframe->sp < curframe->prev->sp)) { curframe->error = 1; bt->flags |= BT_WRAP_TRAP; return(trace->nframes); } #endif /* Allocate space for a stack frame rec */ curframe = alloc_sframe(trace, flags); if (!(func_addr = kl_funcaddr(pc))) { curframe->error = KLE_BAD_PC; UPDATE_FRAME(0, pc, 0, 0, 0, 0, 0, 0, 0, 0); return(trace->nframes); } /* Check to see if check_pc/check_sp points to a sub-trace * of spc/ssp. If it does then don't return a trace (unless * C_ALL). Make sure we free the curframe block since we * wont be linking it in to the trace rec. */ if (check_pc && ((pc == check_pc) && (sp == check_sp))) { kl_free_block((void *)curframe); if (flags & C_ALL) { return(trace->nframes); } else { return(0); } } asp = (uaddr_t*)((uaddr_t)sbp + (STACK_SIZE - (saddr - sp))); #ifdef REDHAT if (XEN_HYPER_MODE()) { func_name = xen_funcname(bt, pc); if (STREQ(func_name, "idle_loop") || STREQ(func_name, "hypercall") || STREQ(func_name, "process_softirqs") || STREQ(func_name, "tracing_off") || STREQ(func_name, "page_fault") || STREQ(func_name, "handle_exception") || xen_top_of_stack(bt, func_name)) { UPDATE_FRAME(func_name, pc, 0, sp, bp, asp, 0, 0, bp - sp, 0); return(trace->nframes); } } else if (STREQ(closest_symbol(pc), "cpu_idle")) { func_name = kl_funcname(pc); UPDATE_FRAME(func_name, pc, 0, sp, bp, asp, 0, 0, bp - sp, 0); return(trace->nframes); } ra = GET_STACK_ULONG(bp + 4); /* * HACK: The get_framesize() function can return the proper * value -- as verified by disassembling the function -- but * in rare circumstances there's more to the stack frame than * meets the eye. Until I can figure out why, extra space * can be added here for any "known" anomolies. gcc version * restrictions are also added rather than assuming anything. * See framesize_modify() for kludgery. */ if (!valid_ra(ra)) { char *funcname; struct framesize_cache *fcp; funcname = kl_funcname(pc); FRAMESIZE_CACHE_VALIDATE(pc, (void **)&fcp); bp += fcp->bp_adjust; ra = GET_STACK_ULONG(bp + 4); /* * This anomoly would be caught by the recovery * speculation, but since we know it's an issue * just catch it here first. */ if (STREQ(funcname, "schedule") && (THIS_GCC_VERSION >= GCC(3,2,3))) { SEEK_VALID_RA(); /* * else FRAMESIZE_VALIDATE has been turned on */ } else if (fcp->flags & FRAMESIZE_VALIDATE) { SEEK_VALID_RA_FUNCTION(funcname); /* * Generic speculation continues the search for * a valid RA at a higher stack address. */ } else if ((bt->flags & BT_SPECULATE) && !STREQ(funcname, "context_switch") && !STREQ(funcname, "die") && !(bt->frameptr && ((bp+4) < bt->frameptr))) SEEK_VALID_RA(); } #else kl_get_kaddr(bp + 4, &ra); #endif /* Make sure that the ra we have is a valid one. If not * then back up in the frame, word by word, until we find * one that is good. */ if (!valid_ra(ra)) { int i; i = ((bp - sp + 8) / 4); while (i) { bp -= 4; #ifdef REDHAT ra = GET_STACK_ULONG(bp + 4); #else kl_get_kaddr(bp + 4, &ra); #endif if (valid_ra(ra)) { break; } i--; } if (i == 0) { #ifdef REDHAT if (interrupted_system_call) { if ((sp1 = x86_is_entry_tramp_address (pc, &offset))) pc = sp1->value + offset; flag = EX_FRAME; } else { if (!XEN_HYPER_MODE() && !is_kernel_thread(bt->task) && (bt->stacktop == machdep->get_stacktop(bt->task))) { if (((ulong)(bp+4) + SIZE(pt_regs)) > bt->stacktop) flag = INCOMPLETE_EX_FRAME; else if ((sp1 = eframe_label(NULL, pc)) && STREQ(sp1->name, "system_call")) flag = EX_FRAME|SET_EX_FRAME_ADDR; else if (STREQ(closest_symbol(pc), "ret_from_fork")) flag = EX_FRAME|SET_EX_FRAME_ADDR; else if (userspace_return(bp, bt)) flag = EX_FRAME|SET_EX_FRAME_ADDR; else { curframe->error = KLE_BAD_RA; flag = 0; } } else { curframe->error = KLE_BAD_RA; flag = 0; } } #else curframe->error = KLE_BAD_RA; #endif UPDATE_FRAME(func_name, pc, ra, sp, bp + 4, asp, 0, 0, 0, flag); return(trace->nframes); } } UPDATE_FRAME(func_name, pc, ra, sp, bp + 4, asp, 0, 0, 0, 0); curframe->frame_size = curframe->fp - curframe->sp + 4; /* Gather starting information for the next frame */ pc = get_call_pc(ra); #ifdef USE_FRAMEPTRS kl_get_kaddr(bp, &bp); if (KL_ERROR) { curframe->error = 2; return(trace->nframes); } #else /* It's possible for get_framesize() to return a size * that is larger than the actual frame size (because * all it does is count the push, pop, addl, and subl * instructions that effect the SP). If we are real near * the top of the stack, this might cause bp to overflow. * This will be fixed above, but we need to bring bp * back into the legal range so we don't crap out * before we can get to it... */ #ifdef REDHAT frame_size = get_framesize(pc, bt); interrupted_system_call = FALSE; #else frame_size = get_framesize(pc); #endif if ((curframe->fp + frame_size) >= saddr) { bp = saddr - 4; } else { bp = curframe->fp + frame_size; } #endif func_name = kl_funcname(pc); if (func_name && !XEN_HYPER_MODE()) { if (strstr(func_name, "kernel_thread")) { ra = 0; bp = saddr - 4; asp = (uaddr_t*) ((uaddr_t)sbp + (STACK_SIZE - 12)); curframe = alloc_sframe(trace, flags); UPDATE_FRAME(func_name, pc, ra, sp, bp, asp, 0, 0, 16, 0); return(trace->nframes); } else if (strstr(func_name, "is386")) { ra = 0; bp = sp = saddr - 4; asp = curframe->asp; curframe = alloc_sframe(trace, flags); UPDATE_FRAME(func_name, pc, ra, sp, bp, asp, 0, 0, 0, 0); return(trace->nframes); } else if (STREQ(func_name, "ret_from_fork")) { ra = 0; bp = sp = saddr - 4; asp = curframe->asp; curframe = alloc_sframe(trace, flags); UPDATE_FRAME(func_name, pc, ra, sp, bp, asp, 0, 0, 0, EX_FRAME|SET_EX_FRAME_ADDR); return(trace->nframes); #ifdef REDHAT } else if (STREQ(func_name, "cpu_idle") || STREQ(func_name, "cpu_startup_entry") || STREQ(func_name, "start_secondary")) { ra = 0; bp = sp = saddr - 4; asp = curframe->asp; curframe = alloc_sframe(trace, flags); UPDATE_FRAME(func_name, pc, ra, sp, bp, asp, 0, 0, 0, 0); return(trace->nframes); } else if (strstr(func_name, "system_call") || strstr(func_name, "sysenter_past_esp") || eframe_label(func_name, pc) || strstr(func_name, "syscall_call") || strstr(func_name, "signal_return") || strstr(func_name, "reschedule") || kernel_entry_from_user_space(curframe, bt)) { #else } else if (strstr(func_name, "system_call")) { #endif /* * user exception frame, kernel stack ends * here. */ bp = saddr - 4; sp = curframe->fp + 4; #ifdef REDHAT ra = GET_STACK_ULONG(bp-16); #else kl_get_kaddr(bp-16, &ra); #endif curframe = alloc_sframe(trace, flags); asp = (uaddr_t*)((uaddr_t)sbp + (STACK_SIZE - (saddr - sp))); UPDATE_FRAME(func_name, pc, ra, sp, bp, asp, 0, 0, (bp - sp + 4), EX_FRAME); return(trace->nframes); #ifdef REDHAT } else if (strstr(func_name, "error_code") || STREQ(func_name, "nmi_stack_correct") || STREQ(func_name, "nmi")) { #else } else if (strstr(func_name, "error_code")) { #endif /* an exception frame */ sp = curframe->fp + eframe_incr(pc, func_name); bp = sp + (KERNEL_EFRAME_SZ-1)*4; asp = (uaddr_t*)((uaddr_t)sbp + (STACK_SIZE - (saddr - sp))); curframe = alloc_sframe(trace, flags); ra = asp[INT_EFRAME_EIP]; frame_type = eframe_type(asp); UPDATE_FRAME(func_name, pc, ra, sp, bp, asp, 0, 0, (bp - sp + 4), EX_FRAME); /* prepare for next kernel frame, if present */ if (frame_type == KERNEL_EFRAME) { pc = asp[INT_EFRAME_EIP]; sp = curframe->fp+4; #ifdef REDHAT bp = sp + get_framesize(pc, bt); #else bp = sp + get_framesize(pc); #endif func_name = kl_funcname(pc); continue; } else { return(trace->nframes); } } else if (is_task_active(bt->task) && (strstr(func_name, "call_do_IRQ") || strstr(func_name, "common_interrupt") || strstr(func_name, "reboot_interrupt") || strstr(func_name, "call_function_interrupt"))) { /* Interrupt frame */ sp = curframe->fp + 4; asp = (uaddr_t*)((uaddr_t)sbp + (STACK_SIZE - (saddr - sp))); frame_type = eframe_type(asp); if (frame_type == KERNEL_EFRAME) bp = curframe->fp+(KERNEL_EFRAME_SZ-1)*4; else bp = curframe->fp+(USER_EFRAME_SZ-1)*4; curframe = alloc_sframe(trace, flags); ra = asp[INT_EFRAME_EIP]; UPDATE_FRAME(func_name, pc, ra, sp, bp + 4, asp, 0, 0, curframe->fp - curframe->sp+4, EX_FRAME); /* prepare for next kernel frame, if present */ if (frame_type == KERNEL_EFRAME) { sp = curframe->fp + 4; pc = asp[INT_EFRAME_EIP]; #ifdef REDHAT bp = sp + get_framesize(pc, bt); #else bp = sp + get_framesize(pc); #endif func_name = kl_funcname(pc); #ifdef REDHAT /* interrupted system_call entry */ if (STREQ(func_name, "system_call")) interrupted_system_call = TRUE; #endif continue; } else { return trace->nframes; } } } if (func_name && XEN_HYPER_MODE()) { if (STREQ(func_name, "continue_nmi") || STREQ(func_name, "vmx_asm_vmexit_handler") || STREQ(func_name, "common_interrupt") || STREQ(func_name, "handle_nmi_mce") || STREQ(func_name, "deferred_nmi")) { /* Interrupt frame */ sp = curframe->fp + 4; asp = (uaddr_t*)((uaddr_t)sbp + (STACK_SIZE - (saddr - sp))); bp = curframe->fp + (12 * 4); curframe = alloc_sframe(trace, flags); ra = *(asp + 9); UPDATE_FRAME(func_name, pc, ra, sp, bp + 4, asp, 0, 0, curframe->fp - curframe->sp+4, 12 * 4); /* contunue next frame */ pc = ra; sp = curframe->fp + 4; bp = sp + get_framesize(pc, bt); func_name = kl_funcname(pc); if (!func_name) return trace->nframes; continue; } } /* * Check for hypervisor_callback from user-space. */ if ((bt->flags & BT_XEN_STOP_THIS_CPU) && bt->tc->mm_struct && STREQ(kl_funcname(curframe->pc), "hypervisor_callback")) { pt = curframe->asp+1; if (eframe_type(pt) == USER_EFRAME) { if (program_context.debug >= 1) /* pc above */ error(INFO, "hypervisor_callback from user space\n"); curframe->asp++; curframe->flag |= EX_FRAME; return(trace->nframes); } } /* Make sure our next frame pointer is valid (in the stack). */ if ((bp < sbase) || (bp >= saddr)) { curframe->error = 3; return(trace->nframes); } sp = curframe->fp + 4; } return(trace->nframes); } static int kernel_entry_from_user_space(sframe_t *curframe, struct bt_info *bt) { ulong stack_segment; if (is_kernel_thread(bt->tc->task)) return FALSE; stack_segment = GET_STACK_ULONG(curframe->fp + 4 + SIZE(pt_regs) - sizeof(kaddr_t)); if ((curframe->fp + 4 + SIZE(pt_regs)) == GET_STACKTOP(bt->task)) { if ((stack_segment == 0x7b) || (stack_segment == 0x2b)) return TRUE; } if ((curframe->fp + 4 + SIZE(pt_regs) + 8) == GET_STACKTOP(bt->task)) { if ((stack_segment == 0x7b) || (stack_segment == 0x2b)) return TRUE; } if (userspace_return(curframe->fp+4, bt)) return TRUE; else return FALSE; } #ifndef REDHAT /* * pc_offset() */ int pc_offset(kaddr_t pc) { kaddr_t func_addr; if ((func_addr = kl_funcaddr(pc))) { return(pc - func_addr); } return(-1); } #endif /* !REDHAT */ /* * dump_stack_frame() */ void dump_stack_frame(trace_t *trace, sframe_t *curframe, FILE *ofp) { int i, first_time = 1; kaddr_t sp; uaddr_t *asp; char buf[BUFSIZE]; sp = curframe->sp; asp = curframe->asp; for (i = 0; i < curframe->frame_size / 4; i++) { if (!(i % 4)) { if (first_time) { first_time = 0; #ifdef REDHAT fprintf(ofp, " %x: %s ", sp, format_stack_entry(trace->bt, buf, *asp++, 0)); #else fprintf(ofp, " %x: %08x ", sp, *asp++); #endif } else { #ifdef REDHAT fprintf(ofp, "\n %x: ", sp); #else fprintf(ofp, "\n %x: ", sp); #endif fprintf(ofp, "%s ", format_stack_entry(trace->bt, buf, *asp++, 0)); } sp += 16; } else { fprintf(ofp, "%s ", format_stack_entry(trace->bt, buf, *asp++, 0)); } } if (curframe->frame_size) { #ifdef REDHAT fprintf(ofp, "\n"); #else fprintf(ofp, "\n\n"); #endif } } /* * eframe_address() */ static uaddr_t * eframe_address(sframe_t *frmp, struct bt_info *bt) { ulong esp0, pt; if (!(frmp->flag & SET_EX_FRAME_ADDR) || INVALID_MEMBER(task_struct_thread) || (((esp0 = MEMBER_OFFSET("thread_struct", "esp0")) < 0) && ((esp0 = MEMBER_OFFSET("thread_struct", "sp0")) < 0))) return frmp->asp; /* * Work required in rarely-seen SET_EX_FRAME_ADDR circumstances. */ pt = ULONG(tt->task_struct + OFFSET(task_struct_thread) + esp0) - SIZE(pt_regs); if (!INSTACK(pt, bt)) return frmp->asp; return ((uint32_t *)(bt->stackbuf + (pt - bt->stackbase))); } /* * print_trace() */ void print_trace(trace_t *trace, int flags, FILE *ofp) { sframe_t *frmp; #ifdef REDHAT kaddr_t fp = 0; kaddr_t last_fp ATTRIBUTE_UNUSED; kaddr_t last_pc, next_fp, next_pc; struct bt_info *bt; bt = trace->bt; last_fp = last_pc = next_fp = next_pc = 0; #else int offset; #endif if ((frmp = trace->frame)) { do { #ifdef REDHAT if (trace->bt->flags & BT_LOOP_TRAP) { if (frmp->prev && frmp->error && (frmp->pc == frmp->prev->pc) && (frmp->fp == frmp->prev->fp)) goto print_trace_error; } if ((trace->bt->flags & BT_WRAP_TRAP) && frmp->error) goto print_trace_error; /* * We're guaranteed to run into an error when unwinding * a hard or soft IRQ stack, so just bail with success. */ if ((frmp->next != trace->frame) && frmp->next->error && (bt->flags & (BT_LOOP_TRAP|BT_WRAP_TRAP)) && (bt->flags & (BT_HARDIRQ|BT_SOFTIRQ))) return; if ((frmp->level == 0) && (bt->flags & BT_XEN_STOP_THIS_CPU)) { print_stack_entry(trace->bt, 0, trace->bt->stkptr, symbol_value("stop_this_cpu"), value_symbol(symbol_value("stop_this_cpu")), frmp, ofp); } print_stack_entry(trace->bt, (trace->bt->flags & (BT_BUMP_FRAME_LEVEL|BT_XEN_STOP_THIS_CPU)) ? frmp->level + 1 : frmp->level, fp ? (ulong)fp : trace->bt->stkptr, (ulong)frmp->pc, frmp->funcname, frmp, ofp); if (trace->bt->flags & BT_LOOP_TRAP) { last_fp = fp ? (ulong)fp : trace->bt->stkptr; last_pc = frmp->pc; } fp = frmp->fp; #else fprintf(ofp, "%2d %s", frmp->level, frmp->funcname); offset = pc_offset(frmp->pc); if (offset > 0) { fprintf(ofp, "+%d", offset); } else if (offset < 0) { fprintf(ofp, "+"); } fprintf(ofp, " [0x%x]\n", frmp->pc); #endif if (frmp->flag & EX_FRAME) { if (CRASHDEBUG(1)) fprintf(ofp, " EXCEPTION FRAME: %lx\n", (unsigned long)frmp->sp); print_eframe(ofp, eframe_address(frmp, bt)); } #ifdef REDHAT if (CRASHDEBUG(1) && (frmp->flag & INCOMPLETE_EX_FRAME)) { fprintf(ofp, " INCOMPLETE EXCEPTION FRAME:\n"); fprintf(ofp, " user stacktop: %lx frame #%d: %lx (+pt_regs: %lx)\n", bt->stacktop, frmp->level, (ulong)frmp->fp, (ulong)frmp->fp + SIZE(pt_regs)); } if (trace->bt->flags & BT_FULL) { fprintf(ofp, " [RA: %x SP: %x FP: %x " "SIZE: %d]\n", frmp->ra, frmp->sp, frmp->fp, frmp->frame_size); dump_stack_frame(trace, frmp, ofp); } #else if (flags & C_FULL) { fprintf(ofp, "\n"); fprintf(ofp, " RA=0x%x, SP=0x%x, FP=0x%x, " "SIZE=%d\n\n", frmp->ra, frmp->sp, frmp->fp, frmp->frame_size); #ifdef FRMSIZE_DBG fprintf(ofp, "\n FRAMESIZE=%d\n\n", #ifdef REDHAT get_framesize(frmp->pc, bt)); #else get_framesize(frmp->pc)); #endif #endif dump_stack_frame(trace, frmp, ofp); } #endif /* !REDHAT */ if (frmp->error) { #ifdef REDHAT print_trace_error: KL_ERROR = KLE_PRINT_TRACE_ERROR; if (CRASHDEBUG(1) || trace->bt->debug) fprintf(ofp, "TRACE ERROR: 0x%llx %llx\n", frmp->error, trace->bt->flags); if (trace->bt->flags & BT_WRAP_TRAP) return; #else fprintf(ofp, "TRACE ERROR: 0x%llx\n", frmp->error); #endif } frmp = frmp->next; } while (frmp != trace->frame); } } /* * trace_banner() */ void trace_banner(FILE *ofp) { fprintf(ofp, "====================================================" "============\n"); } /* * task_trace() */ int #ifdef REDHAT lkcd_x86_back_trace(struct bt_info *bt, int flags, FILE *ofp) #else task_trace(kaddr_t task, int flags, FILE *ofp) #endif { void *tsp; kaddr_t saddr, eip, esp; ulong contents; trace_t *trace; #ifdef REDHAT int nframes = 0; kaddr_t task = bt->task; KL_ERROR = 0; tsp = NULL; if (bt->flags & BT_FRAMESIZE_DEBUG) return(framesize_debug(bt, ofp)); if (kt->flags & RA_SEEK) bt->flags |= BT_SPECULATE; if (XENDUMP_DUMPFILE() && XEN() && is_task_active(bt->task) && STREQ(kl_funcname(bt->instptr), "stop_this_cpu")) { /* * bt->instptr of "stop_this_cpu" is not a return * address -- replace it with the actual return * address found at the bt->stkptr location. */ if (readmem((ulong)bt->stkptr, KVADDR, &eip, sizeof(ulong), "xendump eip", RETURN_ON_ERROR)) bt->instptr = eip; bt->flags |= BT_XEN_STOP_THIS_CPU; if (CRASHDEBUG(1)) error(INFO, "replacing stop_this_cpu with %s\n", kl_funcname(bt->instptr)); } if (XENDUMP_DUMPFILE() && XEN() && is_idle_thread(bt->task) && is_task_active(bt->task) && !(kt->xen_flags & XEN_SUSPEND) && STREQ(kl_funcname(bt->instptr), "schedule")) { /* * This is an invalid (stale) schedule reference * left in the task->thread. Move down the stack * until the smp_call_function_interrupt return * address is found. */ saddr = bt->stkptr; while (readmem(saddr, KVADDR, &eip, sizeof(ulong), "xendump esp", RETURN_ON_ERROR)) { if (STREQ(kl_funcname(eip), "smp_call_function_interrupt")) { bt->instptr = eip; bt->stkptr = saddr; bt->flags |= BT_XEN_STOP_THIS_CPU; if (CRASHDEBUG(1)) error(INFO, "switch schedule to smp_call_function_interrupt\n"); break; } saddr -= sizeof(void *); if (saddr <= bt->stackbase) break; } } if (XENDUMP_DUMPFILE() && XEN() && is_idle_thread(bt->task) && is_task_active(bt->task) && (kt->xen_flags & XEN_SUSPEND) && STREQ(kl_funcname(bt->instptr), "schedule")) { int framesize = 0; /* * This is an invalid (stale) schedule reference * left in the task->thread. Move down the stack * until the hypercall_page() return address is * found, and fix up its framesize as we go. */ saddr = bt->stacktop; while (readmem(saddr, KVADDR, &eip, sizeof(ulong), "xendump esp", RETURN_ON_ERROR)) { if (STREQ(kl_funcname(eip), "xen_idle")) framesize += sizeof(ulong); else if (framesize) framesize += sizeof(ulong); if (STREQ(kl_funcname(eip), "hypercall_page")) { int framesize = 24; bt->instptr = eip; bt->stkptr = saddr; if (CRASHDEBUG(1)) error(INFO, "switch schedule to hypercall_page (framesize: %d)\n", framesize); FRAMESIZE_CACHE_ENTER(eip, &framesize); break; } saddr -= sizeof(void *); if (saddr <= bt->stackbase) break; } } if (XENDUMP_DUMPFILE() && XEN() && !is_idle_thread(bt->task) && is_task_active(bt->task) && STREQ(kl_funcname(bt->instptr), "schedule")) { /* * This is an invalid (stale) schedule reference * left in the task->thread. Move down the stack * until the smp_call_function_interrupt return * address is found. */ saddr = bt->stacktop; while (readmem(saddr, KVADDR, &eip, sizeof(ulong), "xendump esp", RETURN_ON_ERROR)) { if (STREQ(kl_funcname(eip), "smp_call_function_interrupt")) { bt->instptr = eip; bt->stkptr = saddr; bt->flags |= BT_XEN_STOP_THIS_CPU; if (CRASHDEBUG(1)) error(INFO, "switch schedule to smp_call_function_interrupt\n"); break; } saddr -= sizeof(void *); if (saddr <= bt->stackbase) break; } } if (STREQ(kl_funcname(bt->instptr), "crash_kexec") || STREQ(kl_funcname(bt->instptr), "crash_nmi_callback")) { if (readmem(bt->stkptr-4, KVADDR, &contents, sizeof(ulong), "stkptr-4 contents", RETURN_ON_ERROR|QUIET) && (contents == bt->instptr)) bt->stkptr -= 4; } if (!verify_back_trace(bt) && !recoverable(bt, ofp) && !BT_REFERENCE_CHECK(bt)) error(INFO, "cannot resolve stack trace:\n"); if (BT_REFERENCE_CHECK(bt)) return(0); #endif if (!XEN_HYPER_MODE()) { if (!(tsp = kl_alloc_block(TASK_STRUCT_SZ, K_TEMP))) { return(1); } if (kl_get_task_struct(task, 2, tsp)) { kl_free_block(tsp); return(1); } } trace = (trace_t *)alloc_trace_rec(C_TEMP); if (!trace) { #ifdef REDHAT error(INFO, "Could not alloc trace rec!\n"); #else fprintf(KL_ERRORFP, "Could not alloc trace rec!\n"); #endif return(1); } else { #ifdef REDHAT saddr = kl_kernelstack(bt->stackbase); eip = bt->instptr; esp = bt->stkptr; trace->bt = bt; #else saddr = kl_kernelstack(task); if (kl_smp_dumptask(task)) { eip = kl_dumpeip(task); esp = kl_dumpesp(task); } else { if (LINUX_2_2_X(KL_LINUX_RELEASE)) { eip = KL_UINT(K_PTR(tsp, "task_struct", "tss"), "thread_struct", "eip"); esp = KL_UINT(K_PTR(tsp, "task_struct", "tss"), "thread_struct", "esp"); } else { eip = KL_UINT( K_PTR(tsp, "task_struct", "thread"), "thread_struct", "eip"); esp = KL_UINT( K_PTR(tsp, "task_struct", "thread"), "thread_struct", "esp"); } } #endif if (esp < KL_PAGE_OFFSET || eip < KL_PAGE_OFFSET) { #ifdef REDHAT error(INFO, "Task in user space -- no backtrace\n"); #else fprintf(KL_ERRORFP, "Task in user space, No backtrace\n"); #endif return 1; } setup_trace_rec(saddr, 0, 0, trace); if (KL_ERROR) { #ifdef REDHAT error(INFO, "Error setting up trace rec!\n"); #else fprintf(KL_ERRORFP, "Error setting up trace rec!\n"); #endif free_trace_rec(trace); return(1); } #ifdef REDHAT nframes = find_trace(eip, esp, 0, 0, trace, 0); #else find_trace(eip, esp, 0, 0, trace, 0); trace_banner(ofp); fprintf(ofp, "STACK TRACE FOR TASK: 0x%x", task); if (KL_TYPEINFO()) { fprintf(ofp, "(%s)\n\n", (char *)K_PTR(tsp, "task_struct", "comm")); } else { fprintf(ofp, "(%s)\n\n", (char *)K_PTR(tsp, "task_struct", "comm")); } #endif print_trace(trace, flags, ofp); } if (!XEN_HYPER_MODE()) kl_free_block(tsp); free_trace_rec(trace); #ifdef REDHAT if (KL_ERROR == KLE_PRINT_TRACE_ERROR) { handle_trace_error(bt, nframes, ofp); return(1); } #endif return(0); } #ifdef REDHAT /* * Run find_trace() and check for any errors encountered. */ static int verify_back_trace(struct bt_info *bt) { void *tsp; kaddr_t saddr, eip, esp; int errcnt; trace_t *trace; sframe_t *frmp; errcnt = 0; KL_ERROR = 0; tsp = NULL; if (!XEN_HYPER_MODE()) { if (!(tsp = kl_alloc_block(TASK_STRUCT_SZ, K_TEMP))) return FALSE; if (kl_get_task_struct(bt->task, 2, tsp)) { kl_free_block(tsp); return FALSE; } } trace = (trace_t *)alloc_trace_rec(C_TEMP); if (!trace) return FALSE; saddr = kl_kernelstack(bt->stackbase); eip = bt->instptr; esp = bt->stkptr; trace->bt = bt; if (esp < KL_PAGE_OFFSET || eip < KL_PAGE_OFFSET) return FALSE; setup_trace_rec(saddr, 0, 0, trace); if (KL_ERROR) { free_trace_rec(trace); return FALSE; } find_trace(eip, esp, 0, 0, trace, 0); if ((frmp = trace->frame)) { do { if (frmp->error) { /* * We're guaranteed to run into an error when * unwinding and IRQ stack, so bail out without * reporting the error. */ if ((bt->flags & (BT_HARDIRQ|BT_SOFTIRQ)) && (bt->flags & (BT_LOOP_TRAP|BT_WRAP_TRAP))) break; errcnt++; if (!(bt->flags & BT_SPECULATE) && !bt->frameptr) bt->frameptr = frmp->fp; } if (BT_REFERENCE_CHECK(bt)) do_bt_reference_check(bt, frmp); frmp = frmp->next; } while (frmp != trace->frame); } if (!XEN_HYPER_MODE()) kl_free_block(tsp); free_trace_rec(trace); return (errcnt ? FALSE : TRUE); } /* * Check a frame for a requested reference. */ static void do_bt_reference_check(struct bt_info *bt, sframe_t *frmp) { int type; struct syment *sp; sp = frmp->prev && STREQ(frmp->funcname, "error_code") ? x86_jmp_error_code((ulong)frmp->prev->pc) : NULL; switch (bt->ref->cmdflags & (BT_REF_SYMBOL|BT_REF_HEXVAL)) { case BT_REF_SYMBOL: if (STREQ(kl_funcname(frmp->pc), bt->ref->str) || (sp && STREQ(sp->name, bt->ref->str))) bt->ref->cmdflags |= BT_REF_FOUND; break; case BT_REF_HEXVAL: if ((bt->ref->hexval == frmp->pc) || (sp && (bt->ref->hexval == sp->value))) bt->ref->cmdflags |= BT_REF_FOUND; if (frmp->flag & EX_FRAME) { type = eframe_type(frmp->asp); x86_dump_eframe_common(bt, (ulong *)frmp->asp, (type == KERNEL_EFRAME)); } break; } } /* * This function is a repository for "known" find_trace() failures that * can be "fixed" on the fly. * * Currently the routine only deals with BT_LOOP_TRAP/BT_WRAP_TRAP errors * where get_framesize() leaves the bp in an invalid location, where * where schedule() coming from schedule_timeout() is interrupted by a * false return address in between, those where the cpu_idle() trail * cannot be followed, and where the functions called by kernel_thread() * can't find their way back to kernel_thread(). As new fixable trace * instances are discovered, add them in. * * NOTE: the schedule() BT_LOOP_TRAP may have been subsequently fixed * by the get_framesize() adjustment for schedule(), but it's worth * keeping it around if a new schedule framesize anomoly pops up in * the future. */ static int recoverable(struct bt_info *bt, FILE *ofp) { ulong esp, eip; sframe_t sframe; struct stack_hook *hp; struct bt_info btloc; ulong kernel_thread; int calls_schedule; if (!(kt->flags & NO_RA_SEEK)) { BCOPY(bt, &btloc, sizeof(struct bt_info)); btloc.flags &= ~(ulonglong)BT_ERROR_MASK; btloc.flags |= BT_SPECULATE; if (verify_back_trace(&btloc)) { bt->flags &= ~(ulonglong)BT_ERROR_MASK; bt->flags |= BT_SPECULATE; if (CRASHDEBUG(1) || bt->debug) error(INFO, "recovered back trace with RA seek\n"); return TRUE; } } if (!gather_text_list(bt) || !STREQ(kl_funcname(bt->instptr), "schedule")) return FALSE; if (!is_idle_thread(bt->task) && !(bt->flags & BT_ERROR_MASK)) return FALSE; esp = eip = 0; calls_schedule = FALSE; kernel_thread = 0; for (hp = bt->textlist; hp->esp; hp++) { if (STREQ(kl_funcname(hp->eip), "kernel_thread")) { kernel_thread = hp->eip; continue; } if (!calls_schedule && STREQ(x86_function_called_by(hp->eip-5), "schedule")) calls_schedule = TRUE; if (STREQ(kl_funcname(hp->eip), "schedule_timeout")) { esp = hp->esp; eip = hp->eip; break; } if (STREQ(kl_funcname(hp->eip), "cpu_idle") && (bt->tc->pid == 0)) { esp = hp->esp; eip = hp->eip; bt->flags |= BT_CPU_IDLE; for ( ; BT_REFERENCE_CHECK(bt) && hp->esp; hp++) { if (STREQ(kl_funcname(hp->eip), "rest_init") || STREQ(kl_funcname(hp->eip), "start_kernel")) { BZERO(&sframe, sizeof(sframe_t)); sframe.pc = hp->eip; do_bt_reference_check(bt, &sframe); } } break; } } BCOPY(bt, &btloc, sizeof(struct bt_info)); btloc.flags &= ~(ulonglong)BT_ERROR_MASK; if (esp && eip) { btloc.instptr = eip; btloc.stkptr = esp; if (verify_back_trace(&btloc)) { if (CRASHDEBUG(1) || bt->debug) error(INFO, "recovered stack trace:\n"); if (!BT_REFERENCE_CHECK(bt)) fprintf(ofp, " #0 [%08lx] %s at %lx\n", bt->stkptr, kl_funcname(bt->instptr), bt->instptr); bt->instptr = eip; bt->stkptr = esp; bt->flags &= ~(ulonglong)BT_ERROR_MASK; bt->flags |= BT_BUMP_FRAME_LEVEL; FREEBUF(bt->textlist); return TRUE; } if (bt->flags & BT_CPU_IDLE) { if (CRASHDEBUG(1) || bt->debug) error(INFO, "recovered stack trace:\n"); return TRUE; } } if (kernel_thread && calls_schedule && is_kernel_thread(bt->tc->task)) { if (CRASHDEBUG(1) || bt->debug) error(INFO, "recovered stack trace:\n"); if (BT_REFERENCE_CHECK(bt)) { BZERO(&sframe, sizeof(sframe_t)); sframe.pc = kernel_thread; do_bt_reference_check(bt, &sframe); } bt->flags |= BT_KERNEL_THREAD; return TRUE; } return FALSE; } /* * If a trace is recoverable from this point finish it here. Otherwise, * if a back trace fails and is unrecoverable, dump the text symbols along * with any possible exception frames that can be found on the stack. */ static void handle_trace_error(struct bt_info *bt, int nframes, FILE *ofp) { int cnt, level; struct stack_hook *hp; if (CRASHDEBUG(2) || (bt->debug >= 2)) { for (hp = bt->textlist; hp->esp; hp++) { char *func; if ((func = x86_function_called_by(hp->eip-5))) fprintf(ofp, "%lx %s calls %s\n", hp->eip, kl_funcname(hp->eip), func); } } if (bt->flags & BT_CPU_IDLE) { for (hp = bt->textlist, level = 2; hp->esp; hp++) { if (STREQ(kl_funcname(hp->eip), "rest_init") || STREQ(kl_funcname(hp->eip), "start_kernel")) print_stack_entry(bt, level++, hp->esp, hp->eip, kl_funcname(hp->eip), NULL, ofp); } FREEBUF(bt->textlist); return; } if (bt->flags & BT_KERNEL_THREAD) { for (hp = bt->textlist; hp->esp; hp++) { if (STREQ(kl_funcname(hp->eip), "kernel_thread")) print_stack_entry(bt, nframes-1, hp->esp, hp->eip, "kernel_thread", NULL, ofp); } FREEBUF(bt->textlist); return; } error(INFO, "text symbols on stack:\n"); bt->flags |= BT_TEXT_SYMBOLS_PRINT|BT_ERROR_MASK; back_trace(bt); if (!XEN_HYPER_MODE()) { bt->flags = BT_EFRAME_COUNT; if ((cnt = machdep->eframe_search(bt))) { error(INFO, "possible exception frame%s:\n", cnt > 1 ? "s" : ""); bt->flags &= ~(ulonglong)BT_EFRAME_COUNT; machdep->eframe_search(bt); } } } /* * Print a stack entry, and its line number if requested. */ static void print_stack_entry(struct bt_info *bt, int level, ulong esp, ulong eip, char *funcname, sframe_t *frmp, FILE *ofp) { char buf1[BUFSIZE]; char buf2[BUFSIZE]; struct syment *sp; struct load_module *lm; if (frmp && frmp->prev && STREQ(frmp->funcname, "error_code") && (sp = x86_jmp_error_code((ulong)frmp->prev->pc))) sprintf(buf1, " (via %s)", sp->name); else if (frmp && (STREQ(frmp->funcname, "stext_lock") || STRNEQ(frmp->funcname, ".text.lock")) && (sp = x86_text_lock_jmp(eip, NULL))) sprintf(buf1, " (via %s)", sp->name); else buf1[0] = NULLCHAR; if ((sp = eframe_label(funcname, eip))) funcname = sp->name; fprintf(ofp, "%s#%d [%8lx] %s%s at %lx", level < 10 ? " " : "", level, esp, funcname_display(funcname, eip, bt, buf2), strlen(buf1) ? buf1 : "", eip); if (module_symbol(eip, NULL, &lm, NULL, 0)) fprintf(ofp, " [%s]", lm->mod_name); fprintf(ofp, "\n"); if (bt->flags & BT_LINE_NUMBERS) { get_line_number(eip, buf1, FALSE); if (strlen(buf1)) fprintf(ofp, " %s\n", buf1); } } /* * The new process accounting stuff installs a label between system_call and * ret_from_sys_call, confusing the code that recognizes exception frame * symbols. This function has been put in place to catch that anomoly, as * well as serving as a template for any future labels that get placed in the * kernel entry point code. It returns the syment of the "real" kernel entry * point. */ #define EFRAME_LABELS 10 static struct eframe_labels { int init; ulong syscall_labels[EFRAME_LABELS]; struct syment *syscall; struct syment *syscall_end; ulong tracesys_labels[EFRAME_LABELS]; struct syment *tracesys; struct syment *tracesys_exit; ulong sysenter_labels[EFRAME_LABELS]; struct syment *sysenter; struct syment *sysenter_end; } eframe_labels = { 0 }; static struct syment * eframe_label(char *funcname, ulong eip) { int i; struct eframe_labels *efp; struct syment *sp; if (XEN_HYPER_MODE()) return NULL; /* ODA: need support ? */ efp = &eframe_labels; if (!efp->init) { if (!(efp->syscall = symbol_search("system_call"))) { if (CRASHDEBUG(1)) error(WARNING, "\"system_call\" symbol does not exist\n"); } if ((sp = symbol_search("ret_from_sys_call"))) efp->syscall_end = sp; else if ((sp = symbol_search("syscall_badsys"))) efp->syscall_end = sp; else { if (CRASHDEBUG(1)) error(WARNING, "neither \"ret_from_sys_call\" nor \"syscall_badsys\" symbols exist\n"); } if (efp->syscall) { efp->tracesys = symbol_search("tracesys"); efp->tracesys_exit = symbol_search("tracesys_exit"); } if ((efp->sysenter = symbol_search("sysenter_entry")) || (efp->sysenter = symbol_search("ia32_sysenter_target"))) { if ((sp = symbol_search("sysexit_ret_end_marker"))) efp->sysenter_end = sp; else if (THIS_KERNEL_VERSION >= LINUX(2,6,32)) { if ((sp = symbol_search("sysexit_audit")) || (sp = symbol_search("sysenter_exit"))) efp->sysenter_end = next_symbol(NULL, sp); else error(WARNING, "cannot determine end of %s function\n", efp->sysenter->name); } else if ((sp = symbol_search("system_call"))) efp->sysenter_end = sp; else error(WARNING, "neither \"sysexit_ret_end_marker\" nor \"system_call\" symbols exist\n"); } efp->init = TRUE; } /* * First search for the currently-known system_call labels. */ for (i = 0; (i < EFRAME_LABELS) && efp->syscall_labels[i]; i++) { if (efp->syscall_labels[i] == eip) return efp->syscall; } for (i = 0; (i < EFRAME_LABELS) && efp->tracesys_labels[i]; i++) { if (efp->tracesys_labels[i] == eip) return efp->syscall; } for (i = 0; (i < EFRAME_LABELS) && efp->sysenter_labels[i]; i++) { if (efp->sysenter_labels[i] == eip) return efp->sysenter; } /* * If the eip fits in any of the label arrays, try to store it, * but always return the real function it's referencing. */ if (efp->syscall && efp->syscall_end) { if (((eip >= efp->syscall->value) && (eip < efp->syscall_end->value))) { for (i = 0; i < EFRAME_LABELS; i++) if (!efp->syscall_labels[i]) efp->syscall_labels[i] = eip; return efp->syscall; } } if (efp->tracesys && efp->tracesys_exit) { if (((eip >= efp->tracesys->value) && (eip < efp->tracesys_exit->value))) { for (i = 0; i < EFRAME_LABELS; i++) if (!efp->tracesys_labels[i]) efp->tracesys_labels[i] = eip; return efp->syscall; } } if (efp->sysenter && efp->sysenter_end) { if (((eip >= efp->sysenter->value) && (eip < efp->sysenter_end->value))) { for (i = 0; i < EFRAME_LABELS; i++) if (!efp->sysenter_labels[i]) efp->sysenter_labels[i] = eip; return efp->sysenter; } } return NULL; } /* * If it makes sense to display a different function/label name * in a stack entry, it can be done here. Unlike eframe_label(), * this routine won't cause the passed-in function name pointer * to be changed -- this is strictly for display purposes only. */ static char * funcname_display(char *funcname, ulong eip, struct bt_info *bt, char *buf) { struct syment *sp; ulong offset; if (bt->flags & BT_SYMBOL_OFFSET) { sp = value_search(eip, &offset); if (sp && offset) return value_to_symstr(eip, buf, bt->radix); } if (STREQ(funcname, "nmi_stack_correct") && (sp = symbol_search("nmi"))) return sp->name; return funcname; } /* * Cache 2k starting from the passed-in text address. This sits on top * of the instrbuf 256-byte cache, but we don't want to extend its size * because we can run off the end of a module segment -- if this routine * does so, it's benign. Tests of "foreach bt" result in more than an * 80% cache-hit rate. */ #define TEXT_BLOCK_SIZE (2048) static void fill_instr_cache(kaddr_t pc, char *buf) { static kaddr_t last_block = 0; static char block[TEXT_BLOCK_SIZE]; ulong offset; if ((pc >= last_block) && ((pc+256) < (last_block+TEXT_BLOCK_SIZE))) { offset = pc - last_block; } else { if (readmem(pc, KVADDR, block, TEXT_BLOCK_SIZE, "fill_instr_cache", RETURN_ON_ERROR|QUIET)) { last_block = pc; offset = 0; } else { GET_BLOCK(pc, 256, block); last_block = 0; offset = 0; } } BCOPY(&block[offset], buf, 256); } #endif /* * print_traces() * * Output a list of all valid code addresses contained in a stack * along with their function name and stack location. */ int #ifdef REDHAT print_traces(struct bt_info *bt, int level, int flags, FILE *ofp) #else print_traces(kaddr_t saddr, int level, int flags, FILE *ofp) #endif { int nfrms; char *fname, *cfname; uaddr_t *wordp, *stackp; trace_t *trace; kaddr_t addr, isp, caddr, sbase; #ifdef REDHAT kaddr_t saddr = bt->stkptr; #endif stackp = (uaddr_t*)kl_alloc_block(STACK_SIZE, K_TEMP); sbase = saddr - STACK_SIZE; GET_BLOCK(sbase, STACK_SIZE, stackp); if (KL_ERROR) { kl_free_block(stackp); return(1); } if (!(trace = (trace_t *)alloc_trace_rec(K_TEMP))) { #ifdef REDHAT error(INFO, "Could not alloc trace rec!\n"); #else fprintf(KL_ERRORFP, "Could not alloc trace rec!\n"); #endif kl_free_block(stackp); return(1); } setup_trace_rec(saddr, 0, 0, trace); #ifdef REDHAT trace->bt = bt; #endif wordp = stackp; while(wordp < (stackp + (STACK_SIZE / 4))) { if ((addr = (kaddr_t)(*(uaddr_t*)wordp))) { /* check to see if this is a valid code address */ if ((fname = kl_funcname(addr))) { /* Now use the instruction to back up and * see if this RA was saved after a call. * If it was, then try to determine what * function was called. At the very least, * only print out info for true return * addresses (coming right after a call * instruction -- even if we can't tell * what function was called). */ isp = sbase + (((uaddr_t)wordp) - ((uaddr_t)stackp)); cfname = (char *)NULL; caddr = 0; if (get_jmp_instr(addr, isp, &caddr, fname, &cfname)) { wordp++; continue; } /* We have found a valid jump address. Now, * try and get a backtrace. */ nfrms = find_trace(addr, isp, 0, 0, trace, 0); if (nfrms) { if ((nfrms >= level) && (!trace->frame->prev->error || (flags & C_ALL))) { fprintf(ofp, "\nPC="); print_kaddr(addr, ofp, 0); fprintf(ofp, " SP="); print_kaddr(isp, ofp, 0); fprintf(ofp, " SADDR="); print_kaddr(saddr, ofp, 0); fprintf(ofp, "\n"); trace_banner(ofp); print_trace(trace, flags, ofp); trace_banner(ofp); } free_sframes(trace); } } wordp++; } else { wordp++; } } kl_free_block(stackp); return(0); } /* * do_list() * * Output a list of all valid code addresses contained in a stack * along with their function name and stack location. */ int #ifdef REDHAT do_text_list(kaddr_t saddr, int size, FILE *ofp) #else do_list(kaddr_t saddr, int size, FILE *ofp) #endif { char *fname, *cfname; uaddr_t *wordp, *stackp; kaddr_t addr, isp, caddr, sbase; stackp = (uaddr_t*)kl_alloc_block(size, K_TEMP); sbase = saddr - size; GET_BLOCK(sbase, size, stackp); if (KL_ERROR) { kl_free_block(stackp); return(1); } wordp = stackp; while(wordp < (stackp + (size / 4))) { if ((addr = (kaddr_t)(*(uaddr_t*)wordp))) { /* check to see if this is a valid code address */ if ((fname = kl_funcname(addr))) { /* Now use the instruction to back up and * see if this RA was saved after a call. * If it was, then try to determine what * function was called. At the very least, * only print out info for true return * addresses (coming right after a call * instruction -- even if we can't tell * what function was called). */ isp = sbase + (((uaddr_t)wordp) - ((uaddr_t)stackp)); cfname = (char *)NULL; caddr = 0; if (get_jmp_instr(addr, isp, &caddr, fname, &cfname)) { wordp++; continue; } fprintf(ofp, "0x%x -- 0x%x (%s)", isp, addr, fname); if (cfname) { fprintf(ofp, " --> 0x%x (%s)\n", caddr, cfname); } else { fprintf(ofp, "\n"); } } wordp++; } else { wordp++; } } kl_free_block(stackp); return(0); } #ifndef REDHAT /* * add_frame() */ int add_frame(trace_t *trace, kaddr_t fp, kaddr_t ra) { sframe_t *cf, *sf; /* Check to make sure that sp is from the stack in the trace * record. * * XXX -- todo */ sf = (sframe_t *)alloc_sframe(trace, C_PERM); sf->fp = fp; sf->ra = ra; if ((cf = trace->frame)) { do { if (cf->fp && (sf->fp < cf->fp)) { if (cf->next == cf) { cf->prev = sf; sf->next = cf; cf->next = sf; sf->prev = cf; trace->frame = sf; } else { cf->prev->next = sf; sf->prev = cf->prev; cf->prev = sf; sf->next = cf; } return(0); } cf = cf->next; } while (cf != trace->frame); cf = 0; } if (!cf) { kl_enqueue((element_t **)&trace->frame, (element_t *)sf); } return(1); } /* * finish_trace() */ void finish_trace(trace_t *trace) { int level = 0, curstkidx = 0; uaddr_t *sbp; kaddr_t sbase, saddr; sframe_t *sf; sbp = trace->stack[curstkidx].ptr; sbase = trace->stack[curstkidx].addr; saddr = sbase + trace->stack[curstkidx].size; if ((sf = trace->frame)) { do { if (!sf->pc) { if (sf != trace->frame) { sf->sp = sf->prev->fp + 4; sf->pc = get_call_pc(sf->prev->ra); } if (!sf->pc) { sf = sf->next; continue; } } sf->level = level++; sf->frame_size = sf->fp - sf->sp + 4; sf->funcname = kl_funcname(sf->pc); sf->asp = (uaddr_t*)((uaddr_t)sbp + (STACK_SIZE - (saddr - sf->sp))); sf = sf->next; } while (sf != trace->frame); if (level > 0) { sf = (sframe_t *)alloc_sframe(trace, C_PERM); sf->level = level; sf->sp = trace->frame->prev->fp + 4; sf->pc = get_call_pc(trace->frame->prev->ra); sf->funcname = kl_funcname(sf->pc); if (sf->funcname && strstr(sf->funcname, "kernel_thread")) { sf->ra = 0; sf->fp = saddr - 4; sf->asp = (uaddr_t*)((uaddr_t)sbp + (STACK_SIZE - 12)); } else { sf->fp = saddr - 20; kl_get_kaddr(sf->fp, &sf->ra); sf->asp = (uaddr_t*)((uaddr_t)sbp + (STACK_SIZE - (saddr - sf->sp))); } sf->frame_size = sf->fp - sf->sp + 4; kl_enqueue((element_t **)&trace->frame, (element_t *)sf); } } } /* * dumptask_trace() */ int dumptask_trace( kaddr_t curtask, dump_header_asm_t *dha, int flags, FILE *ofp) { kaddr_t eip, esp, saddr; void *tsp; trace_t *trace; int i; for (i = 0; i < dha->dha_smp_num_cpus; i++) { if (curtask == (kaddr_t)dha->dha_smp_current_task[i]) { eip = dha->dha_smp_regs[i].eip; esp = dha->dha_smp_regs[i].esp; break; } } tsp = kl_alloc_block(TASK_STRUCT_SZ, K_TEMP); if (!tsp) { return(1); } if (kl_get_task_struct(curtask, 2, tsp)) { kl_free_block(tsp); return(1); } if (!(trace = alloc_trace_rec(K_TEMP))) { fprintf(KL_ERRORFP, "Could not alloc trace rec!\n"); } else { saddr = kl_kernelstack(curtask); setup_trace_rec(saddr, 0, 0, trace); find_trace(eip, esp, 0, 0, trace, 0); trace_banner(ofp); fprintf(ofp, "STACK TRACE FOR TASK: 0x%"FMTPTR"x (%s)\n\n", curtask, (char*)K_PTR(tsp, "task_struct", "comm")); print_trace(trace, flags, ofp); trace_banner(ofp); free_trace_rec(trace); } return(0); } #endif /* !REDHAT */ /* * lkcdutils-4.1/lcrash/arch/i386/lib/dis.c */ /* * Copyright 1999 Silicon Graphics, Inc. All rights reserved. */ #ifndef REDHAT #include #include #include #endif /* !REDHAT */ static int instr_buf_init = 1; static instr_buf_t instrbuf; static unsigned char *codeptr; /* Forward declarations for local functions */ static int seg_prefix(int); static int op_e(int, int, instr_rec_t *); static opcode_rec_t op_386[] = { /* 0x00 */ { "addb", Eb, Gb }, { "addS", Ev, Gv }, { "addb", Gb, Eb }, { "addS", Gv, Ev }, { "addb", AL, Ib }, { "addS", eAX, Iv }, { "pushS", es }, { "popS", es }, /* 0x08 */ { "orb", Eb, Gb }, { "orS", Ev, Gv }, { "orb", Gb, Eb }, { "orS", Gv, Ev }, { "orb", AL, Ib }, { "orS", eAX, Iv }, { "pushS", cs }, { "(bad)", BAD }, /* 0x10 */ { "adcb", Eb, Gb }, { "adcS", Ev, Gv }, { "adcb", Gb, Eb }, { "adcS", Gv, Ev }, { "adcb", AL, Ib }, { "adcS", eAX, Iv }, { "pushS", ss }, { "popS", ss }, /* 0x18 */ { "sbbb", Eb, Gb }, { "sbbS", Ev, Gv }, { "sbbb", Gb, Eb }, { "sbbS", Gv, Ev }, { "sbbb", AL, Ib }, { "sbbS", eAX, Iv }, { "pushS", ds }, { "popS", ds }, /* 0x20 */ { "andb", Eb, Gb }, { "andS", Ev, Gv }, { "andb", Gb, Eb }, { "andS", Gv, Ev }, { "andb", AL, Ib }, { "andS", eAX, Iv }, { "(bad)", BAD }, /* SEG ES prefix */ { "daa", NONE }, /* 0x28 */ { "subb", Eb, Gb }, { "subS", Ev, Gv }, { "subb", Gb, Eb }, { "subS", Gv, Ev }, { "subb", AL, Ib }, { "subS", eAX, Iv }, { "(bad)", BAD }, /* SEG CS prefix */ { "das", NONE }, /* 0x30 */ { "xorb", Eb, Gb }, { "xorS", Ev, Gv }, { "xorb", Gb, Eb }, { "xorS", Gv, Ev }, { "xorb", AL, Ib }, { "xorS", eAX, Iv }, { "(bad)", BAD }, /* SEG SS prefix */ { "aaa", NONE }, /* 0x38 */ { "cmpb", Eb, Gb }, { "cmpS", Ev, Gv }, { "cmpb", Gb, Eb }, { "cmpS", Gv, Ev }, { "cmpb", AL, Ib }, { "cmpS", eAX, Iv }, { "(bad)", BAD }, /* SEG DS previx */ { "aas", NONE }, /* 0x40 */ { "incS", eAX }, { "incS", eCX }, { "incS", eDX }, { "incS", eBX }, { "incS", eSP }, { "incS", eBP }, { "incS", eSI }, { "incS", eDI }, /* 0x48 */ { "decS", eAX }, { "decS", eCX }, { "decS", eDX }, { "decS", eBX }, { "decS", eSP }, { "decS", eBP }, { "decS", eSI }, { "decS", eDI }, /* 0x50 */ { "pushS", eAX }, { "pushS", eCX }, { "pushS", eDX }, { "pushS", eBX }, { "pushS", eSP }, { "pushS", eBP }, { "pushS", eSI }, { "pushS", eDI }, /* 0x58 */ { "popS", eAX }, { "popS", eCX }, { "popS", eDX }, { "popS", eBX }, { "popS", eSP }, { "popS", eBP }, { "popS", eSI }, { "popS", eDI }, /* 0x60 */ { "pusha", NONE }, { "popa", NONE }, { "boundS", Gv, Ma }, { "arpl", Ew, Gw }, { "(bad)", BAD }, /* seg fs */ { "(bad)", BAD }, /* seg gs */ { "(bad)", BAD }, /* op size prefix */ { "(bad)", BAD }, /* adr size prefix */ /* 0x68 */ { "pushS", Iv }, { "imulS", Gv, Ev, Iv }, { "pushS", sIb }, /* push of byte really pushes 2 or 4 bytes */ { "imulS", Gv, Ev, Ib }, { "insb", Yb, indirDX }, { "insS", Yv, indirDX }, { "outsb", indirDX, Xb }, { "outsS", indirDX, Xv }, /* 0x70 */ { "jo", Jb }, { "jno", Jb }, { "jb", Jb }, { "jae", Jb }, { "je", Jb }, { "jne", Jb }, { "jbe", Jb }, { "ja", Jb }, /* 0x78 */ { "js", Jb }, { "jns", Jb }, { "jp", Jb }, { "jnp", Jb }, { "jl", Jb }, { "jnl", Jb }, { "jle", Jb }, { "jg", Jb }, /* 0x80 */ { GRP1b }, { GRP1S }, { "(bad)", BAD }, { GRP1Ss }, { "testb", Eb, Gb }, { "testS", Ev, Gv }, { "xchgb", Eb, Gb }, { "xchgS", Ev, Gv }, /* 0x88 */ { "movb", Eb, Gb }, { "movS", Ev, Gv }, { "movb", Gb, Eb }, { "movS", Gv, Ev }, { "movw", Ew, Sw }, { "leaS", Gv, M }, { "movw", Sw, Ew }, { "popS", Ev }, /* 0x90 */ { "nop", NONE }, { "xchgS", eCX, eAX }, { "xchgS", eDX, eAX }, { "xchgS", eBX, eAX }, { "xchgS", eSP, eAX }, { "xchgS", eBP, eAX }, { "xchgS", eSI, eAX }, { "xchgS", eDI, eAX }, /* 0x98 */ { "cWtS", NONE }, { "cStd", NONE }, { "lcall", Ap }, { "(bad)", BAD }, /* fwait */ { "pushf", NONE }, { "popf", NONE }, { "sahf", NONE }, { "lahf", NONE }, /* 0xa0 */ { "movb", AL, Ob }, { "movS", eAX, Ov }, { "movb", Ob, AL }, { "movS", Ov, eAX }, { "movsb", Yb, Xb }, { "movsS", Yv, Xv }, { "cmpsb", Yb, Xb }, { "cmpsS", Yv, Xv }, /* 0xa8 */ { "testb", AL, Ib }, { "testS", eAX, Iv }, { "stosb", Yb, AL }, { "stosS", Yv, eAX }, { "lodsb", AL, Xb }, { "lodsS", eAX, Xv }, { "scasb", AL, Yb }, { "scasS", eAX, Yv }, /* 0xb0 */ { "movb", AL, Ib }, { "movb", CL, Ib }, { "movb", DL, Ib }, { "movb", BL, Ib }, { "movb", AH, Ib }, { "movb", CH, Ib }, { "movb", DH, Ib }, { "movb", BH, Ib }, /* 0xb8 */ { "movS", eAX, Iv }, { "movS", eCX, Iv }, { "movS", eDX, Iv }, { "movS", eBX, Iv }, { "movS", eSP, Iv }, { "movS", eBP, Iv }, { "movS", eSI, Iv }, { "movS", eDI, Iv }, /* 0xc0 */ { GRP2b }, { GRP2S }, { "ret", Iw }, { "ret", NONE }, { "lesS", Gv, Mp }, { "ldsS", Gv, Mp }, { "movb", Eb, Ib }, { "movS", Ev, Iv }, /* 0xc8 */ { "enter", Iw, Ib }, { "leave", NONE }, { "lret", Iw }, { "lret", NONE }, { "int3", NONE }, { "int", Ib }, { "into", NONE }, { "iret", NONE }, /* 0xd0 */ { GRP2b_one }, { GRP2S_one }, { GRP2b_cl }, { GRP2S_cl }, { "aam", Ib }, { "aad", Ib }, { "(bad)", BAD }, { "xlat", NONE }, /* 0xd8 */ { FLOAT, NONE }, { FLOAT, NONE }, { FLOAT, NONE }, { FLOAT, NONE }, { FLOAT, NONE }, { FLOAT, NONE }, { FLOAT, NONE }, { FLOAT, NONE }, /* 0xe0 */ { "loopne", Jb }, { "loope", Jb }, { "loop", Jb }, { "jCcxz", Jb }, { "inb", AL, Ib }, { "inS", eAX, Ib }, { "outb", Ib, AL }, { "outS", Ib, eAX }, /* 0xe8 */ { "call", Av }, { "jmp", Jv }, { "ljmp", Ap }, { "jmp", Jb }, { "inb", AL, indirDX }, { "inS", eAX, indirDX }, { "outb", indirDX, AL }, { "outS", indirDX, eAX }, /* 0xf0 */ { "(bad)", BAD }, /* lock prefix */ { "(bad)", BAD }, { "(bad)", BAD }, /* repne */ { "(bad)", BAD }, /* repz */ { "hlt", NONE }, { "cmc", NONE }, { GRP3b }, { GRP3S }, /* 0xf8 */ { "clc", NONE }, { "stc", NONE }, { "cli", NONE }, { "sti", NONE }, { "cld", NONE }, { "std", NONE }, { GRP4 }, { GRP5 }, }; static opcode_rec_t op_386_twobyte[] = { /* 0x00 */ { GRP6 }, { GRP7 }, { "larS", Gv, Ew }, { "lslS", Gv, Ew }, { "(bad)", BAD }, { "(bad)", BAD }, { "clts", NONE }, { "(bad)", BAD }, /* 0x08 */ { "invd", NONE }, { "wbinvd", NONE }, { "(bad)", BAD }, { "ud2a", NONE }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, /* 0x10 */ { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, /* 0x18 */ { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, /* 0x20 */ /* these are all backward in appendix A of the intel book */ { "movl", Rd, Cd }, { "movl", Rd, Dd }, { "movl", Cd, Rd }, { "movl", Dd, Rd }, { "movl", Rd, Td }, { "(bad)", BAD }, { "movl", Td, Rd }, { "(bad)", BAD }, /* 0x28 */ { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, /* 0x30 */ { "wrmsr", NONE }, { "rdtsc", NONE }, { "rdmsr", NONE }, { "rdpmc", NONE }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, /* 0x38 */ { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, /* 0x40 */ { "cmovo", Gv,Ev }, { "cmovno", Gv,Ev }, { "cmovb", Gv,Ev }, { "cmovae", Gv,Ev }, { "cmove", Gv,Ev }, { "cmovne", Gv,Ev }, { "cmovbe", Gv,Ev }, { "cmova", Gv,Ev }, /* 0x48 */ { "cmovs", Gv,Ev }, { "cmovns", Gv,Ev }, { "cmovp", Gv,Ev }, { "cmovnp", Gv,Ev }, { "cmovl", Gv,Ev }, { "cmovge", Gv,Ev }, { "cmovle", Gv,Ev }, { "cmovg", Gv,Ev }, /* 0x50 */ { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, /* 0x58 */ { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, /* 0x60 */ { "punpcklbw", MX, EM }, { "punpcklwd", MX, EM }, { "punpckldq", MX, EM }, { "packsswb", MX, EM }, { "pcmpgtb", MX, EM }, { "pcmpgtw", MX, EM }, { "pcmpgtd", MX, EM }, { "packuswb", MX, EM }, /* 0x68 */ { "punpckhbw", MX, EM }, { "punpckhwd", MX, EM }, { "punpckhdq", MX, EM }, { "packssdw", MX, EM }, { "(bad)", BAD }, { "(bad)", BAD }, { "movd", MX, Ev }, { "movq", MX, EM }, /* 0x70 */ { "(bad)", BAD }, { GRP10 }, { GRP11 }, { GRP12 }, { "pcmpeqb", MX, EM }, { "pcmpeqw", MX, EM }, { "pcmpeqd", MX, EM }, { "emms" , NONE }, /* 0x78 */ { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "movd", Ev, MX }, { "movq", EM, MX }, /* 0x80 */ { "jo", Jv }, { "jno", Jv }, { "jb", Jv }, { "jae", Jv }, { "je", Jv }, { "jne", Jv }, { "jbe", Jv }, { "ja", Jv }, /* 0x88 */ { "js", Jv }, { "jns", Jv }, { "jp", Jv }, { "jnp", Jv }, { "jl", Jv }, { "jge", Jv }, { "jle", Jv }, { "jg", Jv }, /* 0x90 */ { "seto", Eb }, { "setno", Eb }, { "setb", Eb }, { "setae", Eb }, { "sete", Eb }, { "setne", Eb }, { "setbe", Eb }, { "seta", Eb }, /* 0x98 */ { "sets", Eb }, { "setns", Eb }, { "setp", Eb }, { "setnp", Eb }, { "setl", Eb }, { "setge", Eb }, { "setle", Eb }, { "setg", Eb }, /* 0xa0 */ { "pushS", fs }, { "popS", fs }, { "cpuid", NONE }, { "btS", Ev, Gv }, { "shldS", Ev, Gv, Ib }, { "shldS", Ev, Gv, CL }, { "(bad)", BAD }, { "(bad)", BAD }, /* 0xa8 */ { "pushS", gs }, { "popS", gs }, { "rsm", NONE }, { "btsS", Ev, Gv }, { "shrdS", Ev, Gv, Ib }, { "shrdS", Ev, Gv, CL }, { "(bad)", BAD }, { "imulS", Gv, Ev }, /* 0xb0 */ { "cmpxchgb", Eb, Gb }, { "cmpxchgS", Ev, Gv }, { "lssS", Gv, Mp }, /* 386 lists only Mp */ { "btrS", Ev, Gv }, { "lfsS", Gv, Mp }, /* 386 lists only Mp */ { "lgsS", Gv, Mp }, /* 386 lists only Mp */ { "movzbS", Gv, Eb }, { "movzwS", Gv, Ew }, /* 0xb8 */ { "ud2b", NONE }, { "(bad)", BAD }, { GRP8 }, { "btcS", Ev, Gv }, { "bsfS", Gv, Ev }, { "bsrS", Gv, Ev }, { "movsbS", Gv, Eb }, { "movswS", Gv, Ew }, /* 0xc0 */ { "xaddb", Eb, Gb }, { "xaddS", Ev, Gv }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { GRP9 }, /* 0xc8 */ { "bswap", eAX }, { "bswap", eCX }, { "bswap", eDX }, { "bswap", eBX }, { "bswap", eSP }, { "bswap", eBP }, { "bswap", eSI }, { "bswap", eDI }, /* 0xd0 */ { "(bad)", BAD }, { "psrlw", MX, EM }, { "psrld", MX, EM }, { "psrlq", MX, EM }, { "(bad)", BAD }, { "pmullw", MX, EM }, { "(bad)", BAD }, { "(bad)", BAD }, /* 0xd8 */ { "psubusb", MX, EM }, { "psubusw", MX, EM }, { "(bad)", BAD }, { "pand", MX, EM }, { "paddusb", MX, EM }, { "paddusw", MX, EM }, { "(bad)", BAD }, { "pandn", MX, EM }, /* 0xe0 */ { "(bad)", BAD }, { "psraw", MX, EM }, { "psrad", MX, EM }, { "(bad)", BAD }, { "(bad)", BAD }, { "pmulhw", MX, EM }, { "(bad)", BAD }, { "(bad)", BAD }, /* 0xe8 */ { "psubsb", MX, EM }, { "psubsw", MX, EM }, { "(bad)", BAD }, { "por", MX, EM }, { "paddsb", MX, EM }, { "paddsw", MX, EM }, { "(bad)", BAD }, { "pxor", MX, EM }, /* 0xf0 */ { "(bad)", BAD }, { "psllw", MX, EM }, { "pslld", MX, EM }, { "psllq", MX, EM }, { "(bad)", BAD }, { "pmaddwd", MX, EM }, { "(bad)", BAD }, { "(bad)", BAD }, /* 0xf8 */ { "psubb", MX, EM }, { "psubw", MX, EM }, { "psubd", MX, EM }, { "(bad)", BAD }, { "paddb", MX, EM }, { "paddw", MX, EM }, { "paddd", MX, EM }, { "(bad)", BAD }, }; static opcode_rec_t grps[][8] = { /* GRP1b */ { { "addb", Eb, Ib }, { "orb", Eb, Ib }, { "adcb", Eb, Ib }, { "sbbb", Eb, Ib }, { "andb", Eb, Ib }, { "subb", Eb, Ib }, { "xorb", Eb, Ib }, { "cmpb", Eb, Ib } }, /* GRP1S */ { { "addS", Ev, Iv }, { "orS", Ev, Iv }, { "adcS", Ev, Iv }, { "sbbS", Ev, Iv }, { "andS", Ev, Iv }, { "subS", Ev, Iv }, { "xorS", Ev, Iv }, { "cmpS", Ev, Iv } }, /* GRP1Ss */ { { "addS", Ev, sIb }, { "orS", Ev, sIb }, { "adcS", Ev, sIb }, { "sbbS", Ev, sIb }, { "andS", Ev, sIb }, { "subS", Ev, sIb }, { "xorS", Ev, sIb }, { "cmpS", Ev, sIb } }, /* GRP2b */ { { "rolb", Eb, Ib }, { "rorb", Eb, Ib }, { "rclb", Eb, Ib }, { "rcrb", Eb, Ib }, { "shlb", Eb, Ib }, { "shrb", Eb, Ib }, { "(bad)", BAD }, { "sarb", Eb, Ib }, }, /* GRP2S */ { { "rolS", Ev, Ib }, { "rorS", Ev, Ib }, { "rclS", Ev, Ib }, { "rcrS", Ev, Ib }, { "shlS", Ev, Ib }, { "shrS", Ev, Ib }, { "(bad)", BAD }, { "sarS", Ev, Ib }, }, /* GRP2b_one */ { { "rolb", Eb }, { "rorb", Eb }, { "rclb", Eb }, { "rcrb", Eb }, { "shlb", Eb }, { "shrb", Eb }, { "(bad)", BAD }, { "sarb", Eb }, }, /* GRP2S_one */ { { "rolS", Ev }, { "rorS", Ev }, { "rclS", Ev }, { "rcrS", Ev }, { "shlS", Ev }, { "shrS", Ev }, { "(bad)", BAD }, { "sarS", Ev }, }, /* GRP2b_cl */ { { "rolb", Eb, CL }, { "rorb", Eb, CL }, { "rclb", Eb, CL }, { "rcrb", Eb, CL }, { "shlb", Eb, CL }, { "shrb", Eb, CL }, { "(bad)", BAD }, { "sarb", Eb, CL }, }, /* GRP2S_cl */ { { "rolS", Ev, CL }, { "rorS", Ev, CL }, { "rclS", Ev, CL }, { "rcrS", Ev, CL }, { "shlS", Ev, CL }, { "shrS", Ev, CL }, { "(bad)", BAD }, { "sarS", Ev, CL } }, /* GRP3b */ { { "testb", Eb, Ib }, { "(bad)", Eb }, { "notb", Eb }, { "negb", Eb }, { "mulb", AL, Eb }, { "imulb", AL, Eb }, { "divb", AL, Eb }, { "idivb", AL, Eb } }, /* GRP3S */ { { "testS", Ev, Iv }, { "(bad)", BAD }, { "notS", Ev }, { "negS", Ev }, { "mulS", eAX, Ev }, { "imulS", eAX, Ev }, { "divS", eAX, Ev }, { "idivS", eAX, Ev }, }, /* GRP4 */ { { "incb", Eb }, { "decb", Eb }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, }, /* GRP5 */ { { "incS", Ev }, { "decS", Ev }, { "call", indirEv }, { "lcall", indirEv }, { "jmp", indirEv }, { "ljmp", indirEv }, { "pushS", Ev }, { "(bad)", BAD }, }, /* GRP6 */ { { "sldt", Ew }, { "str", Ew }, { "lldt", Ew }, { "ltr", Ew }, { "verr", Ew }, { "verw", Ew }, { "(bad)", BAD }, { "(bad)", BAD } }, /* GRP7 */ { { "sgdt", Ew }, { "sidt", Ew }, { "lgdt", Ew }, { "lidt", Ew }, { "smsw", Ew }, { "(bad)", BAD }, { "lmsw", Ew }, { "invlpg", Ew }, }, /* GRP8 */ { { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "btS", Ev, Ib }, { "btsS", Ev, Ib }, { "btrS", Ev, Ib }, { "btcS", Ev, Ib }, }, /* GRP9 */ { { "(bad)", BAD }, { "cmpxchg8b", Ev }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, }, /* GRP10 */ { { "(bad)", BAD }, { "(bad)", BAD }, { "psrlw", MS, Ib }, { "(bad)", BAD }, { "psraw", MS, Ib }, { "(bad)", BAD }, { "psllw", MS, Ib }, { "(bad)", BAD }, }, /* GRP11 */ { { "(bad)", BAD }, { "(bad)", BAD }, { "psrld", MS, Ib }, { "(bad)", BAD }, { "psrad", MS, Ib }, { "(bad)", BAD }, { "pslld", MS, Ib }, { "(bad)", BAD }, }, /* GRP12 */ { { "(bad)", BAD }, { "(bad)", BAD }, { "psrlq", MS, Ib }, { "(bad)", BAD }, { "(bad)", BAD }, { "(bad)", BAD }, { "psllq", MS, Ib }, { "(bad)", BAD }, } }; static opcode_rec_t float_grps[][8] = { /* d8 */ { { "fadd", ST, STi }, { "fmul", ST, STi }, { "fcom", STi }, { "fcomp", STi }, { "fsub", ST, STi }, { "fsubr", ST, STi }, { "fdiv", ST, STi }, { "fdivr", ST, STi }, }, /* d9 */ { { "fld", STi }, { "fxch", STi }, { FGRPd9_2 }, { "(bad)" }, { FGRPd9_4 }, { FGRPd9_5 }, { FGRPd9_6 }, { FGRPd9_7 }, }, /* da */ { { "fcmovb", ST, STi }, { "fcmove", ST, STi }, { "fcmovbe",ST, STi }, { "fcmovu", ST, STi }, { "(bad)" }, { FGRPda_5 }, { "(bad)" }, { "(bad)" }, }, /* db */ { { "fcmovnb",ST, STi }, { "fcmovne",ST, STi }, { "fcmovnbe",ST, STi }, { "fcmovnu",ST, STi }, { FGRPdb_4 }, { "fucomi", ST, STi }, { "fcomi", ST, STi }, { "(bad)" }, }, /* dc */ { { "fadd", STi, ST }, { "fmul", STi, ST }, { "(bad)" }, { "(bad)" }, { "fsub", STi, ST }, { "fsubr", STi, ST }, { "fdiv", STi, ST }, { "fdivr", STi, ST }, }, /* dd */ { { "ffree", STi }, { "(bad)" }, { "fst", STi }, { "fstp", STi }, { "fucom", STi }, { "fucomp", STi }, { "(bad)" }, { "(bad)" }, }, /* de */ { { "faddp", STi, ST }, { "fmulp", STi, ST }, { "(bad)" }, { FGRPde_3 }, { "fsubp", STi, ST }, { "fsubrp", STi, ST }, { "fdivp", STi, ST }, { "fdivrp", STi, ST }, }, /* df */ { { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, { FGRPdf_4 }, { "fucomip",ST, STi }, { "fcomip", ST, STi }, { "(bad)" }, }, }; static char *fgrps[][8] = { /* d9_2 0 */ { "fnop","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)", }, /* d9_4 1 */ { "fchs","fabs","(bad)","(bad)","ftst","fxam","(bad)","(bad)", }, /* d9_5 2 */ { "fld1","fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz","(bad)", }, /* d9_6 3 */ { "f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp","fincstp", }, /* d9_7 4 */ { "fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos", }, /* da_5 5 */ { "(bad)","fucompp","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)", }, /* db_4 6 */ { "feni(287 only)","fdisi(287 only)","fNclex","fNinit", "fNsetpm(287 only)","(bad)","(bad)","(bad)", }, /* de_3 7 */ { "(bad)","fcompp","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)", }, /* df_4 8 */ { "fNstsw","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)", }, }; static char *float_mem[] = { /* 0xd8 */ "fadds","fmuls","fcoms","fcomps","fsubs","fsubrs","fdivs","fdivrs", /* 0xd9 */ "flds","(bad)","fsts","fstps","fldenv","fldcw","fNstenv","fNstcw", /* 0xda */ "fiaddl","fimull","ficoml","ficompl","fisubl","fisubrl","fidivl", "fidivrl", /* 0xdb */ "fildl","(bad)","fistl","fistpl","(bad)","fldt","(bad)","fstpt", /* 0xdc */ "faddl","fmull","fcoml","fcompl","fsubl","fsubrl","fdivl","fdivrl", /* 0xdd */ "fldl","(bad)","fstl","fstpl","frstor","(bad)","fNsave","fNstsw", /* 0xde */ "fiadd","fimul","ficom","ficomp","fisub","fisubr","fidiv","fidivr", /* 0xdf */ "fild","(bad)","fist","fistp","fbld","fildll","fbstp","fistpll", }; static const unsigned char onebyte_has_modrm[256] = { /* 00 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 10 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 20 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 30 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 50 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 60 */ 0,0,1,1,0,0,0,0,0,1,0,1,0,0,0,0, /* 70 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 80 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* b0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* c0 */ 1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0, /* d0 */ 1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1, /* e0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* f0 */ 0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1 }; static const unsigned char twobyte_has_modrm[256] = { /* 00 */ 1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0, /* 0f */ /* 10 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1f */ /* 20 */ 1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0, /* 2f */ /* 30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 3f */ /* 40 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 4f */ /* 50 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 5f */ /* 60 */ 1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1, /* 6f */ /* 70 */ 0,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1, /* 7f */ /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */ /* 90 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 9f */ /* a0 */ 0,0,0,1,1,1,1,1,0,0,0,1,1,1,1,1, /* af */ /* b0 */ 1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1, /* bf */ /* c0 */ 1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0, /* cf */ /* d0 */ 0,1,1,1,0,1,0,0,1,1,0,1,1,1,0,1, /* df */ /* e0 */ 0,1,1,0,0,1,0,0,1,1,0,1,1,1,0,1, /* ef */ /* f0 */ 0,1,1,1,0,1,0,0,1,1,1,0,1,1,1,0 /* ff */ }; #ifdef NOT_USED static int reg_num[] = { 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, }; #endif #ifndef REDHAT static char *reg_name[] = { "%eax","%ecx","%edx","%ebx","%esp","%ebp","%esi","%edi", "%ax","%cx","%dx","%bx","%sp","%bp","%si","%di", "%al","%cl","%dl","%bl","%ah","%ch","%dh","%bh", "%es","%cs","%ss","%ds","%fs","%gs", "bx+si","bx+di","bp+si","bp+di", }; #endif /* !REDHAT */ static int reg_32[] = { R_eAX, R_eCX, R_eDX, R_eBX, R_eSP, R_eBP, R_eSI, R_eDI, }; static int reg_16[] = { R_AX, R_CX, R_DX, R_BX, R_SP, R_BP, R_SI, R_DI, }; static int reg_8[] = { R_AL, R_CL, R_DL, R_BL, R_AH, R_CH, R_DH, R_BH, }; static int reg_seg[] = { R_ES, R_CS, R_SS, R_DS, R_FS, R_GS, R_BAD, R_BAD, }; static int reg_index[] = { R_BX_SI, R_BX_DI, R_BP_SI, R_BP_DI, R_SI, R_DI, R_BP, R_BX, }; #ifndef REDHAT static char *optype_name[] = { "NONE","A","C","D","E","M_indirE","F","G","I","sI","J","M", "O","P","Q","R","S","T","V","W","X","Y","MMX","EM","MS","GRP", "REG", }; static char *opmods[] = { "NONE","a","b","c","d","dg","p","pi", "ps","q","s","ss","si","v","w", }; static char *reg_opname[] = { "eAX","eCX","eDX","eBX","eSP","eBP","eSI","eDI", "AX","CX","DX","BX","SP","BP","SI","DI", "AL","CL","DL","BL","AH","CH","DH","BH", "ES","CS","SS","DS","FS","GS", }; static void printaddr(kaddr_t addr, int flag, FILE *ofp) { int offset = 0; syment_t *sp; if ((sp = kl_lkup_symaddr(addr))) { offset = addr - sp->s_addr; } /* Print out address */ fprintf(ofp, "0x%x", addr); /* Print out symbol name */ if (sp) { if (offset) { fprintf(ofp, " <%s+%d>", sp->s_name, offset); } else { fprintf(ofp, " <%s>", sp->s_name); } } /* Line things up properly for current function */ if (flag) { if (offset == 0) { fprintf(ofp, ": "); } else if (offset < 10) { fprintf(ofp, ": "); } else if (offset < 100) { fprintf(ofp, ": "); } else if (offset < 1000) { fprintf(ofp, ": "); } else if (offset < 10000) { fprintf(ofp, ": "); } else { fprintf(ofp, ": "); } } } static void print_optype(int m, int t, FILE *ofp) { if (m >= M_BAD) { fprintf(ofp, "BAD"); } else if (m == M_REG) { if (t >= R_BAD) { fprintf(ofp, "REG_BAD"); } else { fprintf(ofp, "%s", reg_opname[t]); } } else { if (t == T_NONE) { fprintf(ofp, "%s", optype_name[m]); } else if (t >= T_BAD) { fprintf(ofp, "%s(bad)", optype_name[m]); } else { fprintf(ofp, "%s%s", optype_name[m], opmods[t]); } } } #endif /* !REDHAT */ static void get_modrm_info(unsigned char modr, int *mod_rm, int *reg_op) { *mod_rm = ((modr >> 6) << 3) | (modr & 7); *reg_op = (modr >> 3) & 7; } static int is_prefix(unsigned char c) { int prefix = 0; switch(c) { case 0xf3: prefix = PREFIX_REPZ; break; case 0xf2: prefix = PREFIX_REPNZ; break; case 0xf0: prefix = PREFIX_LOCK; break; case 0x2e: prefix = PREFIX_CS; break; case 0x36: prefix = PREFIX_SS; break; case 0x3e: prefix = PREFIX_DS; break; case 0x26: prefix = PREFIX_ES; break; case 0x64: prefix = PREFIX_FS; break; case 0x65: prefix = PREFIX_GS; break; case 0x66: prefix = PREFIX_DATA; break; case 0x67: prefix = PREFIX_ADR; break; case 0x9b: prefix = PREFIX_FWAIT; break; } return(prefix); } static int get_modrm_reg16(int mod_rm, int opdata, instr_rec_t *irp) { int reg, mod; mod = irp->modrm >> 6; switch (mod_rm) { case 0x6: break; default: reg = mod_rm - (mod * 8); return(reg_index[reg]); } return(R_BAD); } static int get_modrm_reg32(int mod_rm, int opdata, instr_rec_t *irp) { int reg; switch (mod_rm) { case 0x0: case 0x1: case 0x2: case 0x3: case 0x6: case 0x7: return(mod_rm); case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d: case 0x1e: case 0x1f: reg = mod_rm - 0x18; switch (opdata) { case T_b: return(reg_8[reg]); case T_w: return(reg_16[reg]); case T_v: if (irp->dflag) { return(reg_32[reg]); } else { return(reg_16[reg]); } } } return(R_BAD); } #ifndef REDHAT static void print_instrname(char *name, instr_rec_t *irp, FILE *ofp) { char *cp, *np, name_str[100]; strncpy (name_str, name, 100); np = name; cp = name_str; while (*np) { if (*np == 'C') { /* For jcxz/jecxz */ if (irp->aflag) { *cp++ = 'e'; } } else if (*np == 'N') { if ((irp->prefixes & PREFIX_FWAIT) == 0) { *cp++ = 'n'; } } else if (*np == 'S') { /* operand size flag */ if (irp->dflag) { *cp++ = 'l'; } else { *cp++ = 'w'; } } else if (*np == 'W') { /* operand size flag for cwtl, cbtw */ if (irp->dflag) { *cp++ = 'w'; } else { *cp++ = 'b'; } } else { *cp++ = *np; } np++; } while(*cp) { *cp++ = ' '; } *cp = 0; fprintf(ofp, "%s", name_str); } #endif /* !REDHAT */ static void op_a(int opnum, int opdata, instr_rec_t *irp) { int offset; kaddr_t pc; pc = instrbuf.addr + (instrbuf.ptr - instrbuf.buf); switch(opdata) { case T_p: if (irp->aflag) { irp->operand[opnum].op_addr = *(uint32_t*)codeptr; codeptr += 4; } else { irp->operand[opnum].op_addr = *(uint16_t*)codeptr; codeptr += 2; } irp->operand[opnum].op_seg = *(uint16_t*)codeptr; irp->operand[opnum].op_type = O_LPTR; codeptr += 2; break; case T_v: if (irp->aflag) { offset = *(int*)codeptr; irp->operand[opnum].op_addr = pc + offset + 5; codeptr += 4; } else { offset = *(short*)codeptr; irp->operand[opnum].op_addr = pc + offset + 3; codeptr += 2; } irp->operand[opnum].op_type = O_ADDR; break; default: break; } } static void op_c(int opnum, int opdata, instr_rec_t *irp) { int reg; reg = (irp->modrm >> 3) & 7; irp->operand[opnum].op_type = (O_REG|O_CR); irp->operand[opnum].op_reg = reg; } static void op_d(int opnum, int opdata, instr_rec_t *irp) { int reg; reg = (irp->modrm >> 3) & 7; irp->operand[opnum].op_type = (O_REG|O_DB); irp->operand[opnum].op_reg = reg; } static void op_indir_e(int opnum, int opdata, instr_rec_t *irp) { op_e(opnum, opdata, irp); irp->operand[opnum].op_type |= O_INDIR; } static void get_modrm_data16(int opnum, int opdata, instr_rec_t *irp) { int mod ATTRIBUTE_UNUSED; int reg, mod_rm, reg_op; get_modrm_info(irp->modrm, &mod_rm, ®_op); mod = irp->modrm >> 6; switch(mod_rm) { case 0: case 1: case 2: case 3: case 4: case 5: case 7: reg = get_modrm_reg16(mod_rm, opdata, irp); irp->operand[opnum].op_reg = reg; irp->operand[opnum].op_type = (O_REG|O_BASE); break; case 6: /* 16-bit displacement */ irp->operand[opnum].op_type = O_DISP; irp->operand[opnum].op_disp = *(uint16_t*)codeptr; codeptr += 2; break; case 8: /* disp8[BX+SI] */ irp->operand[opnum].op_type = (O_REG|O_DISP); irp->operand[opnum].op_reg = R_BX_SI; irp->operand[opnum].op_disp = *(signed char*)codeptr; codeptr++; break; case 9: /* disp8[BX+DI] */ irp->operand[opnum].op_type = (O_REG|O_DISP); irp->operand[opnum].op_reg = R_BX_DI; irp->operand[opnum].op_disp = *(signed char*)codeptr; codeptr++; break; case 10: /* disp8[BP+SI] */ irp->operand[opnum].op_type = (O_REG|O_DISP); irp->operand[opnum].op_reg = R_BP_SI; irp->operand[opnum].op_disp = *(signed char*)codeptr; codeptr++; break; case 11: /* disp8[BP+DI] */ irp->operand[opnum].op_type = (O_REG|O_DISP); irp->operand[opnum].op_reg = R_BP_DI; irp->operand[opnum].op_disp = *(signed char*)codeptr; codeptr++; break; case 12: /* disp8[SI] */ irp->operand[opnum].op_type = (O_REG|O_DISP); irp->operand[opnum].op_reg = R_SI; irp->operand[opnum].op_disp = *(signed char*)codeptr; codeptr++; break; case 13: /* disp8[DI] */ irp->operand[opnum].op_type = (O_REG|O_DISP); irp->operand[opnum].op_reg = R_DI; irp->operand[opnum].op_disp = *(signed char*)codeptr; codeptr++; break; case 14: /* disp8[BP] */ irp->operand[opnum].op_type = (O_REG|O_DISP); irp->operand[opnum].op_reg = R_BP; irp->operand[opnum].op_disp = *(signed char*)codeptr; codeptr++; break; case 15: /* disp8[BX] */ irp->operand[opnum].op_type = (O_REG|O_DISP); irp->operand[opnum].op_reg = R_BX; irp->operand[opnum].op_disp = *(signed char*)codeptr; codeptr++; break; case 16: /* disp16[BX+SI] */ irp->operand[opnum].op_type = (O_REG|O_DISP); irp->operand[opnum].op_reg = R_BX_SI; irp->operand[opnum].op_disp = *(short*)codeptr; codeptr += 2; break; case 17: /* disp16[BX+DI] */ irp->operand[opnum].op_type = (O_REG|O_DISP); irp->operand[opnum].op_reg = R_BX_DI; irp->operand[opnum].op_disp = *(short*)codeptr; codeptr += 2; break; case 18: /* disp16[BP+SI] */ irp->operand[opnum].op_type = (O_REG|O_DISP); irp->operand[opnum].op_reg = R_BP_SI; irp->operand[opnum].op_disp = *(short*)codeptr; codeptr += 2; break; case 19: /* disp16[BP+DI] */ irp->operand[opnum].op_type = (O_REG|O_DISP); irp->operand[opnum].op_reg = R_BP_DI; irp->operand[opnum].op_disp = *(short*)codeptr; codeptr += 2; break; case 20: /* disp16[SI] */ irp->operand[opnum].op_type = (O_REG|O_DISP); irp->operand[opnum].op_reg = R_SI; irp->operand[opnum].op_disp = *(short*)codeptr; codeptr += 2; break; case 21: /* disp16[DI] */ irp->operand[opnum].op_type = (O_REG|O_DISP); irp->operand[opnum].op_reg = R_DI; irp->operand[opnum].op_disp = *(short*)codeptr; codeptr += 2; break; case 22: /* disp16[BP] */ irp->operand[opnum].op_type = (O_REG|O_DISP); irp->operand[opnum].op_reg = R_BP; irp->operand[opnum].op_disp = *(short*)codeptr; codeptr += 2; break; case 23: /* disp16[BX] */ irp->operand[opnum].op_type = (O_REG|O_DISP); irp->operand[opnum].op_reg = R_BX; irp->operand[opnum].op_disp = *(short*)codeptr; codeptr += 2; break; } } static void get_modrm_data32(int opnum, int opdata, instr_rec_t *irp) { int mod ATTRIBUTE_UNUSED; int reg, mod_rm, reg_op; get_modrm_info(irp->modrm, &mod_rm, ®_op); mod = irp->modrm >> 6; switch(mod_rm) { case 0: case 1: case 2: case 3: case 6: case 7: reg = get_modrm_reg32(mod_rm, opdata, irp); irp->operand[opnum].op_reg = reg; irp->operand[opnum].op_type = (O_REG|O_BASE); break; case 5: /* 32-bit displacement */ irp->operand[opnum].op_type = O_DISP; irp->operand[opnum].op_disp = *(kaddr_t*)codeptr; codeptr += 4; break; case 8: /* disp8[EAX] */ irp->operand[opnum].op_type = (O_REG|O_DISP); irp->operand[opnum].op_reg = R_eAX; irp->operand[opnum].op_disp = *(signed char*)codeptr; codeptr++; break; case 9: /* disp8[ECX] */ irp->operand[opnum].op_type = (O_REG|O_DISP); irp->operand[opnum].op_reg = R_eCX; irp->operand[opnum].op_disp = *(signed char*)codeptr; codeptr++; break; case 10: /* disp8[EDX] */ irp->operand[opnum].op_type = (O_REG|O_DISP); irp->operand[opnum].op_reg = R_eDX; irp->operand[opnum].op_disp = *(signed char*)codeptr; codeptr++; break; case 11: /* disp8[EBX] */ irp->operand[opnum].op_type = (O_REG|O_DISP); irp->operand[opnum].op_reg = R_eBX; irp->operand[opnum].op_disp = *(signed char*)codeptr; codeptr++; break; case 13: /* disp8[EBP] */ irp->operand[opnum].op_type = (O_REG|O_DISP); irp->operand[opnum].op_reg = R_eBP; irp->operand[opnum].op_disp = *(signed char*)codeptr; codeptr++; break; case 14: /* disp8[ESI] */ irp->operand[opnum].op_type = (O_REG|O_DISP); irp->operand[opnum].op_reg = R_eSI; irp->operand[opnum].op_disp = *(signed char*)codeptr; codeptr++; break; case 15: /* disp8[EDI] */ irp->operand[opnum].op_type = (O_REG|O_DISP); irp->operand[opnum].op_reg = R_eDI; irp->operand[opnum].op_disp = *(signed char*)codeptr; codeptr++; break; case 16: /* disp32[EAX] */ irp->operand[opnum].op_type = (O_REG|O_DISP); irp->operand[opnum].op_reg = R_eAX; irp->operand[opnum].op_disp = *(int*)codeptr; codeptr += 4; break; case 17: /* disp32[ECX] */ irp->operand[opnum].op_type = (O_REG|O_DISP); irp->operand[opnum].op_reg = R_eCX; irp->operand[opnum].op_disp = *(int*)codeptr; codeptr += 4; break; case 18: /* disp32[EDX] */ irp->operand[opnum].op_type = (O_REG|O_DISP); irp->operand[opnum].op_reg = R_eDX; irp->operand[opnum].op_disp = *(int*)codeptr; codeptr += 4; break; case 19: /* disp32[EBX] */ irp->operand[opnum].op_type = (O_REG|O_DISP); irp->operand[opnum].op_reg = R_eBX; irp->operand[opnum].op_disp = *(int*)codeptr; codeptr += 4; break; case 4: /* [..][..] (SIB) */ case 12: /* disp8[..][..] (SIB) */ case 20: { /* disp32[..][..] (SIB) */ int rm ATTRIBUTE_UNUSED; int s, i, b, mod, havebase; s = (irp->sib >> 6) & 3; i = (irp->sib >> 3) & 7; b = irp->sib & 7; mod = irp->modrm >> 6; rm = irp->modrm & 7; havebase = 1; switch (mod) { case 0: if (b == 5) { havebase = 0; irp->operand[opnum].op_disp = *(int*)codeptr; irp->operand[opnum].op_type = O_DISP; codeptr += 4; } break; case 1: irp->operand[opnum].op_disp = *(signed char*) codeptr; codeptr++; irp->operand[opnum].op_type = O_DISP; break; case 2: irp->operand[opnum].op_disp = *(int*)codeptr; codeptr += 4; irp->operand[opnum].op_type = O_DISP; break; } if (havebase) { irp->operand[opnum].op_base = b; irp->operand[opnum].op_type |= O_BASE; } if (i != 4) { irp->operand[opnum].op_index = i; irp->operand[opnum].op_type |= O_INDEX; } if (s) { irp->operand[opnum].op_scale = s; irp->operand[opnum].op_type |= O_SCALE; } break; } case 21: /* disp32[EBP] */ irp->operand[opnum].op_type = (O_REG|O_DISP); irp->operand[opnum].op_reg = R_eBP; irp->operand[opnum].op_disp = *(int*)codeptr; codeptr += 4; break; case 22: /* disp32[ESI] */ irp->operand[opnum].op_type = (O_REG|O_DISP); irp->operand[opnum].op_reg = R_eSI; irp->operand[opnum].op_disp = *(int*)codeptr; codeptr += 4; break; case 23: /* disp32[EDI] */ irp->operand[opnum].op_type = (O_REG|O_DISP); irp->operand[opnum].op_reg = R_eDI; irp->operand[opnum].op_disp = *(int*)codeptr; codeptr += 4; break; } } static int op_e(int opnum, int opdata, instr_rec_t *irp) { int reg, mod, mod_rm, reg_op; get_modrm_info(irp->modrm, &mod_rm, ®_op); mod = irp->modrm >> 6; if (mod == 3) { /* ((mod_rm >= 24) && (mod_rm <=31)) */ if (opdata == T_NONE) { return(1); } if (irp->aflag) { reg = get_modrm_reg32(mod_rm, opdata, irp); } else { reg = get_modrm_reg16(mod_rm, opdata, irp); } irp->operand[opnum].op_type = O_REG; irp->operand[opnum].op_reg = reg; if ((reg = R_BAD)) { return(1); } else { return(0); } } if (irp->aflag) { get_modrm_data32(opnum, opdata, irp); } else { get_modrm_data16(opnum, opdata, irp); } if (seg_prefix(irp->prefixes)) { irp->operand[opnum].op_type |= O_SEG; irp->operand[opnum].op_seg = seg_prefix(irp->prefixes); } return(0); } static int op_g(int opnum, int opdata, instr_rec_t *irp) { int reg, mod_rm, reg_op; get_modrm_info(irp->modrm, &mod_rm, ®_op); irp->operand[opnum].op_type = O_REG; if ((reg_op < 0) || (reg_op >= 8)){ irp->operand[opnum].op_reg = R_BAD; return(1); } switch(opdata) { case T_b: reg = reg_8[reg_op]; break; case T_w: reg = reg_16[reg_op]; break; case T_d: reg = reg_32[reg_op]; break; case T_v: if (irp->dflag) { reg = reg_32[reg_op]; } else { reg = reg_16[reg_op]; } break; default: irp->operand[opnum].op_reg = R_BAD; return(1); } irp->operand[opnum].op_reg = reg; return(0); } static void op_i(int opnum, int opdata, instr_rec_t *irp) { irp->operand[opnum].op_type = O_IMMEDIATE; switch (opdata) { case T_b: irp->operand[opnum].op_addr = *(unsigned char*)codeptr; codeptr++; break; case T_w: irp->operand[opnum].op_addr = *(uint16_t*)codeptr; codeptr += 2; break; case T_v: if (irp->dflag) { irp->operand[opnum].op_addr = *(uint32_t*)codeptr; codeptr += 4; } else { irp->operand[opnum].op_addr = *(uint16_t*)codeptr; codeptr += 2; } break; } } static void op_s(int opnum, int opdata, instr_rec_t *irp) { int reg; reg = (irp->modrm >> 3) & 7; irp->operand[opnum].op_reg = reg_seg[reg]; irp->operand[opnum].op_type = O_REG; } static void op_si(int opnum, int opdata, instr_rec_t *irp) { int val; irp->operand[opnum].op_type = O_IMMEDIATE; switch (opdata) { case T_b: val = *(signed char*)codeptr++; irp->operand[opnum].op_addr = val; break; case T_v: if (irp->dflag) { irp->operand[opnum].op_addr = *(int*)codeptr; codeptr += 4; } else { val = *(short*)codeptr; irp->operand[opnum].op_addr = val; codeptr += 2; } break; case T_w: val = *(short*)codeptr; irp->operand[opnum].op_addr = val; codeptr += 2; break; } } static void op_j(int opnum, int opdata, instr_rec_t *irp) { kaddr_t pc; pc = instrbuf.addr + (instrbuf.ptr - instrbuf.buf); pc += (codeptr - instrbuf.ptr); switch (opdata) { case T_b: pc++; pc += *(signed char *)codeptr++; break; case T_v: if (irp->dflag) { /* 32-bit */ pc += 4; pc += *(int*)codeptr; codeptr += 4; } else { /* 16-bit */ pc += 2; pc += *(short*)codeptr; codeptr += 2; } break; } irp->operand[opnum].op_type = O_ADDR; irp->operand[opnum].op_addr = pc; } static void op_m(int opnum, int opdata, instr_rec_t *irp) { op_e(opnum, 0, irp); } static void op_o(int opnum, int opdata, instr_rec_t *irp) { if (irp->aflag) { irp->operand[opnum].op_addr = *(uint32_t*)codeptr; codeptr += 4; } else { irp->operand[opnum].op_addr = *(uint16_t*)codeptr; codeptr += 2; } irp->operand[opnum].op_type = O_OFF; } static void op_r(int opnum, int opdata, instr_rec_t *irp) { int rm; rm = irp->modrm & 7; switch (opdata) { case T_d: irp->operand[opnum].op_reg = reg_32[rm]; break; case T_w: irp->operand[opnum].op_reg = reg_16[rm]; break; } irp->operand[opnum].op_type = O_REG; } static void op_x(int opnum, int opdata, instr_rec_t *irp) { irp->operand[opnum].op_seg = R_DS; if (irp->aflag) { irp->operand[opnum].op_reg = R_eSI; } else { irp->operand[opnum].op_reg = R_SI; } irp->operand[opnum].op_type = O_SEG; } static void op_y(int opnum, int opdata, instr_rec_t *irp) { irp->operand[opnum].op_seg = R_ES; if (irp->aflag) { irp->operand[opnum].op_reg = R_eDI; } else { irp->operand[opnum].op_reg = R_DI; } irp->operand[opnum].op_type = O_SEG; } static void get_operand_info(int opnum, instr_rec_t *irp) { int opcode, opdata; opcode = opdata = 0; switch(opnum) { case 0: opcode = irp->opcodep->Op1; opdata = irp->opcodep->opdata1; break; case 1: opcode = irp->opcodep->Op2; opdata = irp->opcodep->opdata2; break; case 2: opcode = irp->opcodep->Op3; opdata = irp->opcodep->opdata3; break; } switch (opcode) { case M_A: op_a(opnum, opdata, irp); break; case M_C: op_c(opnum, opdata, irp); break; case M_D: op_d(opnum, opdata, irp); break; case M_E: op_e(opnum, opdata, irp); break; case M_indirE: op_indir_e(opnum, opdata, irp); break; case M_G: op_g(opnum, opdata, irp); break; case M_I: op_i(opnum, opdata, irp); break; case M_sI: op_si(opnum, opdata, irp); break; case M_J: op_j(opnum, opdata, irp); break; case M_M: op_m(opnum, opdata, irp); break; case M_O: op_o(opnum, opdata, irp); break; case M_R: op_r(opnum, opdata, irp); break; case M_S: op_s(opnum, opdata, irp); break; case M_X: op_x(opnum, opdata, irp); break; case M_Y: op_y(opnum, opdata, irp); break; case M_REG: case M_indirREG: irp->operand[opnum].op_type = O_REG; if (opdata >= R_AX) { irp->operand[opnum].op_reg = opdata; } else { if (irp->dflag) { irp->operand[opnum].op_reg = reg_32[opdata]; } else { irp->operand[opnum].op_reg = reg_16[opdata]; } } if (opcode == M_indirREG) { /* The O_BASE gets the right results */ irp->operand[opnum].op_type |= O_BASE; } break; } } /* Temporary opcode_rec_s struct that we keep around for the times * when we have to construct a special case instruction (e.g. some * floating point instructions). */ static opcode_rec_t tempop; static char fwait_name[] = "fwait"; int get_instr_info(kaddr_t pc, instr_rec_t *irp) { int opcode, size = 0, p, prefixes = 0; unsigned char modrm = 0; opcode_rec_t *op; if (instr_buf_init) { bzero(&instrbuf, sizeof(instrbuf)); instr_buf_init = 0; } /* Check to see instrbuf is valid and if there are enough * bytes in our instruction cache to cover the worst case * scenario for this pc. */ if (!instrbuf.addr || (pc < instrbuf.addr) || (pc > (instrbuf.addr + instrbuf.size - 15))) { instrbuf.addr = pc; instrbuf.size = 256; #ifdef REDHAT fill_instr_cache(pc, (char *)instrbuf.buf); #else GET_BLOCK(pc, 256, instrbuf.buf); #endif if (KL_ERROR) { return(0); } } /* Make sure that the instruction pointer points to the * right byte in the buffer. */ instrbuf.ptr = instrbuf.buf + (pc - instrbuf.addr); codeptr = instrbuf.ptr; irp->addr = pc; /* Check for prefixes */ while((p = is_prefix(*codeptr))) { prefixes |= p; codeptr++; if ((prefixes & PREFIX_FWAIT) && ((*codeptr < 0xd8) || (*codeptr > 0xdf))) { /* If there is an fwait prefix that is not * followed by a float instruction, we need to * create a special instruction record so that * the "fwait" gets printed out. */ bzero(&tempop, sizeof(tempop)); tempop.name = fwait_name; irp->opcodep = &tempop; size = ((unsigned)codeptr - (unsigned)instrbuf.ptr); instrbuf.ptr = codeptr; irp->size = size; return(size); } } if (prefixes & PREFIX_DATA) { irp->dflag ^= 1; } if (prefixes & PREFIX_ADR) { irp->aflag ^= 1; } /* Check for one or two byte opcode, capture the opcode and * check for a ModR/M byte. */ if (*codeptr == 0x0f) { opcode = *((unsigned short*)codeptr); codeptr++; op = &op_386_twobyte[*codeptr]; if(twobyte_has_modrm[*codeptr]) { codeptr++; modrm = *codeptr++; } else { codeptr++; } if (STREQ(op->name, "ud2a")) codeptr += kt->BUG_bytes; } else { opcode = *codeptr; op = &op_386[*codeptr]; if(onebyte_has_modrm[*codeptr]) { codeptr++; modrm = *codeptr++; } else { codeptr++; } } /* See if the get_op bits from the modrm are needed to determine * the actual instruction. */ if (op->Op1 == M_GRP) { op = &grps[op->opdata1][(modrm & 0x38) >> 3]; /* Put something unique in opcode */ opcode = ((opcode << 8)|((modrm & 0x38) >> 3)); } else if (op->Op1 == M_FLOAT) { int mod, rm, reg; mod = modrm >> 6; rm = modrm & 7; reg = (modrm >> 3) & 7; bzero(&tempop, sizeof(tempop)); if (mod != 3) { tempop.name = float_mem[(opcode - 0xd8) * 8 + reg]; tempop.Op1 = M_E; tempop.opdata1 = T_v; op = &tempop; } else { op = &float_grps[opcode - 0xd8][reg]; if (op->Op1 == M_FGRP) { tempop.name = fgrps[op->opdata1][rm]; /* instruction fnstsw is only one with * strange arg */ if ((opcode == 0xdf) && (*codeptr == 0xe0)) { irp->operand[1].op_type = O_REG; irp->operand[1].op_reg = R_eAX; } op = &tempop; } } } irp->opcodep = op; irp->opcode = opcode; irp->modrm = modrm; irp->prefixes = prefixes; /* Check to see if this is a bad instruction (per a table entry) */ if (op->opdata1 == T_BAD) { /* Back off the modrm if we grabbed one and return * from here. */ if (modrm) { codeptr--; size = ((unsigned)codeptr - (unsigned)instrbuf.ptr); instrbuf.ptr = codeptr; irp->size = size; return(size); } } /* Check to see if there is an SIB byte. */ if (((modrm & 0xc0) != 0xc0) && ((modrm & 7) == 4)) { /* There is an SIB byte */ irp->sib = *codeptr++; irp->have_sib = 1; } /* Gather information on operands */ if (op->Op1 && (op->Op1 != M_BAD)) { get_operand_info(0, irp); } if (op->Op2 && (op->Op2 != M_BAD)) { get_operand_info(1, irp); } if (op->Op3 && (op->Op3 != M_BAD)) { get_operand_info(2, irp); } /* Determine total instruction size and adjust instrbuf ptr */ size = ((unsigned)codeptr - (unsigned)instrbuf.ptr); instrbuf.ptr = codeptr; irp->size = size; return(size); } static int seg_prefix(int prefixes) { if (prefixes & PREFIX_CS) { return(R_CS); } else if (prefixes & PREFIX_DS) { return(R_DS); } else if (prefixes & PREFIX_SS) { return(R_SS); } else if (prefixes & PREFIX_ES) { return(R_ES); } else if (prefixes & PREFIX_FS) { return(R_FS); } else if (prefixes & PREFIX_GS) { return(R_GS); } return(0); } #ifdef NOT_USED static void print_seg_prefix(instr_rec_t *irp, FILE *ofp) { if (irp->prefixes & PREFIX_CS) { fprintf(ofp, "%%cs:"); } if (irp->prefixes & PREFIX_DS) { fprintf(ofp, "%%ds:"); } if (irp->prefixes & PREFIX_SS) { fprintf(ofp, "%%ss:"); } if (irp->prefixes & PREFIX_ES) { fprintf(ofp, "%%es:"); } if (irp->prefixes & PREFIX_FS) { fprintf(ofp, "%%fs:"); } if (irp->prefixes & PREFIX_GS) { fprintf(ofp, "%%gs:"); } } #endif #ifndef REDHAT static int print_prefixes(instr_rec_t *irp, FILE *ofp) { int cnt = 0; if (irp->prefixes & PREFIX_REPZ) { fprintf(ofp, "repz "); cnt++; } if (irp->prefixes & PREFIX_REPNZ) { fprintf(ofp, "repnz "); cnt++; } if (irp->prefixes & PREFIX_LOCK) { fprintf(ofp, "lock "); cnt++; } if (irp->prefixes & PREFIX_ADR) { if (irp->aflag) { fprintf(ofp, "addr32 "); } else { fprintf(ofp, "addr16 "); } cnt++; } return(cnt); } static void print_sib_value(int opnum, instr_rec_t *irp, FILE *ofp) { if (irp->operand[opnum].op_type & O_REG) { if (irp->operand[opnum].op_type & O_BASE) { fprintf(ofp, "(%s)", reg_name[irp->operand[opnum].op_reg]); } else { fprintf(ofp, "%s", reg_name[irp->operand[opnum].op_reg]); } return; } else if (irp->operand[opnum].op_type & O_IMMEDIATE) { fprintf(ofp, "$0x%x", irp->operand[opnum].op_addr); return; } fprintf(ofp, "("); if (irp->operand[opnum].op_type & O_BASE) { fprintf(ofp, "%s,", reg_name[irp->operand[opnum].op_base]); } else { fprintf(ofp, ","); } if (irp->operand[opnum].op_type & O_INDEX) { fprintf(ofp, "%s,", reg_name[irp->operand[opnum].op_index]); } fprintf(ofp, "%d)", (1 << irp->operand[opnum].op_scale)); } static void print_opvalue(int opnum, instr_rec_t *irp, FILE *ofp) { if (irp->operand[opnum].op_type & O_REG) { if (irp->operand[opnum].op_type & (O_BASE|O_DISP)) { fprintf(ofp, "(%s)", reg_name[irp->operand[opnum].op_reg]); } else { fprintf(ofp, "%s", reg_name[irp->operand[opnum].op_reg]); } } else if (irp->operand[opnum].op_type & O_IMMEDIATE) { fprintf(ofp, "$0x%x", irp->operand[opnum].op_addr); } else if (irp->operand[opnum].op_type & O_ADDR) { /* jump or call address */ printaddr(irp->operand[opnum].op_addr, 0, ofp); } else if (irp->operand[opnum].op_type & O_OFF) { fprintf(ofp, "0x%x", irp->operand[opnum].op_addr); } } int print_instr(kaddr_t pc, FILE *ofp, int flag) { int p = 0, i, j, size, print_comma = 0; instr_rec_t irp; opcode_rec_t *op; bzero(&irp, sizeof(irp)); /* XXX -- For now, make aflag and dflag equal to one. Should get * this from some sort of configuration struct (set via * initialization) */ irp.aflag = 1; irp.dflag = 1; size = get_instr_info(pc, &irp); op = irp.opcodep; if (!op) { fprintf(ofp, "BAD INSTR (pc=0x%x)\n", pc); return(0); } printaddr(pc, 1, ofp); if (flag) { fprintf(ofp, "0x%04x ", irp.opcode); } if (irp.prefixes) { p = print_prefixes(&irp, ofp); } print_instrname(op->name, &irp, ofp); /* HACK! but necessary to match i386-dis.c output for fwait. */ if (!strcmp(op->name, "fwait")) { fprintf(ofp, "\n"); return(irp.size); } if (p || (strlen(op->name) >= 7)) { fprintf(ofp, " "); } else { for (i = 0; i < (7 - strlen(op->name)); i++) { fprintf(ofp, " "); } } for (j = 0; j < 3; j++) { if (irp.opcode == 0xc8) { i = j; } else { i = 2 - j; } if(irp.operand[i].op_type) { if (print_comma) { fprintf(ofp, ","); } if (irp.operand[i].op_type & O_LPTR) { fprintf(ofp, "0x%x,0x%x", irp.operand[i].op_seg, irp.operand[i].op_addr); print_comma++; continue; } if (irp.operand[i].op_type & O_CR) { fprintf(ofp, "%%cr%d", irp.operand[i].op_reg); print_comma++; continue; } if (irp.operand[i].op_type & O_DB) { fprintf(ofp, "%%db%d", irp.operand[i].op_reg); print_comma++; continue; } if (irp.operand[i].op_type & O_SEG) { fprintf(ofp, "%s:(%s)", reg_name[irp.operand[i].op_seg], reg_name[irp.operand[i].op_reg]); print_comma++; continue; } if (irp.operand[i].op_type & O_INDIR) { fprintf(ofp, "*"); } if (irp.operand[i].op_type & O_DISP) { fprintf(ofp, "0x%x", irp.operand[i].op_disp); } if (irp.have_sib) { print_sib_value(i, &irp, ofp); } else { print_opvalue(i, &irp, ofp); } print_comma++; } } if (flag) { fprintf(ofp, " (%d %s)\n", irp.size, (irp.size > 1) ? "bytes" : "byte"); } else { fprintf(ofp, "\n"); } return(irp.size); } void list_instructions(FILE *ofp) { int i, j, print_comma = 0; fprintf(ofp, "ONE BYTE INSTRUCTIONS:\n\n"); for(i = 0; i < 256; i++) { fprintf(ofp, "0x%04x %s", i, op_386[i].name); for (j = 0; j < (10 - strlen(op_386[i].name)); j++) { fprintf(ofp, " "); } if (op_386[i].Op1) { print_optype(op_386[i].Op1, op_386[i].opdata1, ofp); print_comma++; } if (op_386[i].Op2) { if (print_comma) { fprintf(ofp, ","); } print_optype(op_386[i].Op2, op_386[i].opdata2, ofp); print_comma++; } if (op_386[i].Op3) { if (print_comma) { fprintf(ofp, ","); } print_optype(op_386[i].Op3, op_386[i].opdata3, ofp); } fprintf(ofp, "\n"); } fprintf(ofp, "\nTWO BYTE INSTRUCTIONS:\n\n"); for(i = 0; i < 256; i++) { fprintf(ofp, "0x0f%02x %s", i, op_386_twobyte[i].name); for (j = 0; j < (10 - strlen(op_386_twobyte[i].name)); j++) { fprintf(ofp, " "); } if (op_386_twobyte[i].Op1) { print_optype(op_386_twobyte[i].Op1, op_386_twobyte[i].opdata1, ofp); print_comma++; } if (op_386_twobyte[i].Op2) { if (print_comma) { fprintf(ofp, ","); } print_optype(op_386_twobyte[i].Op2, op_386_twobyte[i].opdata2, ofp); print_comma++; } if (op_386_twobyte[i].Op3) { if (print_comma) { fprintf(ofp, ","); } print_optype(op_386_twobyte[i].Op3, op_386_twobyte[i].opdata3, ofp); } fprintf(ofp, "\n"); } } #endif /* !REDHAT */ void free_instr_stream(instr_rec_t *irp) { instr_rec_t *ptr; if(irp) { while (irp->prev) { irp = irp->prev; } while (irp) { ptr = irp; irp = irp->next; kl_free_block(ptr); } } } instr_rec_t * get_instr_stream(kaddr_t pc, int bcount, int acount) { int size, count = 0; kaddr_t addr, start_addr, end_addr; syment_t *sp1, *sp2; #ifdef REDHAT syment_t *sp, *sp_next, *sp_next_next; ulong offset; #endif instr_rec_t *fst = (instr_rec_t *)NULL, *lst, *ptr, *cur; #ifdef REDHAT cur = NULL; if ((sp = x86_is_entry_tramp_address(pc, &offset))) pc = sp->value + offset; #endif if (!(sp1 = kl_lkup_symaddr(pc))) { return((instr_rec_t *)NULL); } start_addr = sp1->s_addr; if (pc <= (sp1->s_addr + (bcount * 15))) { if ((sp2 = kl_lkup_symaddr(sp1->s_addr - 4))) { start_addr = sp2->s_addr; } } #ifdef REDHAT sp_next = next_symbol(NULL, sp1); if (!sp_next) return((instr_rec_t *)NULL); sp_next_next = next_symbol(NULL, sp_next); if (pc > (sp_next->s_addr - (acount * 15))) { if (sp_next_next) { end_addr = sp_next_next->s_addr; } else { end_addr = sp_next->s_addr; } } else { end_addr = sp_next->s_addr; } #else if (pc > (sp1->s_next->s_addr - (acount * 15))) { if (sp1->s_next->s_next) { end_addr = sp1->s_next->s_next->s_addr; } else { end_addr = sp1->s_next->s_addr; } } else { end_addr = sp1->s_next->s_addr; } #endif addr = start_addr; while (addr <= pc) { if (addr >= end_addr) { /* We've gone too far (beyond the end of this * function) The pc most likely was not valid * (it pointed into the middle of an instruction). */ free_instr_stream(cur); return((instr_rec_t *)NULL); } if (count <= bcount) { /* Allocate another record */ cur = (instr_rec_t *) kl_alloc_block(sizeof(instr_rec_t), K_TEMP); count++; cur->aflag = cur->dflag = 1; if ((ptr = fst)) { while (ptr->next) { ptr = ptr->next; } ptr->next = cur; cur->prev = ptr; } else { fst = cur; } } else { /* Pull the last record to the front of the list */ ptr = fst; if (ptr->next) { fst = ptr->next; fst->prev = (instr_rec_t *)NULL; cur->next = ptr; } bzero(ptr, sizeof(*ptr)); ptr->aflag = ptr->dflag = 1; if (ptr != fst) { ptr->prev = cur; } cur = ptr; } size = get_instr_info(addr, cur); if (size == 0) { free_instr_stream(cur); return((instr_rec_t *)NULL); } addr += size; } if (acount) { lst = cur; for (count = 0; count < acount; count++) { ptr = (instr_rec_t *) kl_alloc_block(sizeof(instr_rec_t), K_TEMP); ptr->aflag = ptr->dflag = 1; size = get_instr_info(addr, ptr); if (size == 0) { kl_free_block(ptr); return(cur); } lst->next = ptr; ptr->prev = lst; lst = ptr; addr += size; } } return(cur); } #ifndef REDHAT /* * print_instr_stream() */ kaddr_t print_instr_stream(kaddr_t value, int bcount, int acount, int flags, FILE *ofp) { kaddr_t v = value; instr_rec_t *cur_irp, *irp; if ((cur_irp = get_instr_stream(v, bcount, acount))) { irp = cur_irp; /* Walk back to the start of the stream and then * print out all instructions in the stream. */ while (irp->prev) { irp = irp->prev; } while (irp) { if (flags & C_FULL) { print_instr(irp->addr, ofp, 1); } else { print_instr(irp->addr, ofp, 0); } if (irp->addr >= value) { v += irp->size; } irp = irp->next; } free_instr_stream(cur_irp); } return(v); } /* * dump_instr() -- architecture specific instruction dump routine */ void dump_instr(kaddr_t addr, uint64_t count, int flags, FILE *ofp) { fprintf(ofp, "This operation not supported for i386 architecture.\n"); } #endif /* !REDHAT */ /* * lkcdutils-4.1/libutil/kl_queue.c */ /* * Copyright 2002 Silicon Graphics, Inc. All rights reserved. */ #ifndef REDHAT #include #endif /* * kl_enqueue() -- Add a new element to the tail of doubly linked list. */ void kl_enqueue(element_t **list, element_t *new) { element_t *head; /* * If there aren't any elements on the list, then make new element the * head of the list and make it point to itself (next and prev). */ if (!(head = *list)) { new->next = new; new->prev = new; *list = new; } else { head->prev->next = new; new->prev = head->prev; new->next = head; head->prev = new; } } /* * kl_dequeue() -- Remove an element from the head of doubly linked list. */ element_t * kl_dequeue(element_t **list) { element_t *head; /* If there's nothing queued up, just return */ if (!*list) { return((element_t *)NULL); } head = *list; /* If there is only one element on list, just remove it */ if (head->next == head) { *list = (element_t *)NULL; } else { head->next->prev = head->prev; head->prev->next = head->next; *list = head->next; } head->next = 0; return(head); } #ifndef REDHAT /* * kl_findqueue() */ int kl_findqueue(element_t **list, element_t *item) { element_t *e; /* If there's nothing queued up, just return */ if (!*list) { return(0); } e = *list; /* Check to see if there is only one element on the list. */ if (e->next == e) { if (e != item) { return(0); } } else { /* Now walk linked list looking for item */ while(1) { if (e == item) { break; } else if (e->next == *list) { return(0); } e = e->next; } } return(1); } /* * kl_findlist_queue() */ int kl_findlist_queue(list_of_ptrs_t **list, list_of_ptrs_t *item, int (*compare)(void *,void *)) { list_of_ptrs_t *e; /* If there's nothing queued up, just return */ if (!*list) { return(0); } e = *list; /* Check to see if there is only one element on the list. */ if (((element_t *)e)->next == (element_t *)e) { if (compare(e,item)) { return(0); } } else { /* Now walk linked list looking for item */ while(1) { if (!compare(e,item)) { break; } else if (((element_t *)e)->next == (element_t *)*list) { return(0); } e = (list_of_ptrs_t *)((element_t *)e)->next; } } return(1); } /* * kl_remqueue() -- Remove specified element from doubly linked list. */ void kl_remqueue(element_t **list, element_t *item) { /* Check to see if item is first on the list */ if (*list == item) { if (item->next == item) { *list = (element_t *)NULL; return; } else { *list = item->next; } } /* Remove item from list */ item->next->prev = item->prev; item->prev->next = item->next; } #endif /* !REDHAT */ #endif /* X86 */