/*
 * Copyright 1999 Silicon Graphics, Inc. All rights reserved.
 */

/* 
 *  lkcd_x86_trace.c
 *
 *  Copyright (C) 2002-2012, 2017 David Anderson
 *  Copyright (C) 2002-2012, 2017 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 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 <lcrash.h>
#include <asm/lc_dis.h>
#include <strings.h>
#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);
	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 <asm/segment.h>
#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"))
		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)
{
	if (is_kernel_thread(bt->tc->task))
		return FALSE;

	if (((curframe->fp + 4 + SIZE(pt_regs)) == GET_STACKTOP(bt->task)) &&
	    !is_kernel_thread(bt->tc->task))
		return TRUE;
	else 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, "+<ERROR>");
			}
			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;
	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 (!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 <lcrash.h>
#include <asm/lc_dis.h>
#include <strings.h>
#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, &reg_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, &reg_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, &reg_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, &reg_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 <kl_lib.h>
#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 */
