mirror of https://github.com/crash-utility/crash
6216 lines
138 KiB
C
6216 lines
138 KiB
C
/*
|
|
* Copyright 1999 Silicon Graphics, Inc. All rights reserved.
|
|
*/
|
|
|
|
/*
|
|
* lkcd_x86_trace.c
|
|
*
|
|
* Copyright (C) 2002-2012, 2017-2018 David Anderson
|
|
* Copyright (C) 2002-2012, 2017-2018 Red Hat, Inc. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* Adapted as noted from the following LKCD files:
|
|
*
|
|
* lkcdutils-4.1/lcrash/arch/i386/lib/dis.c
|
|
* lkcdutils-4.1/lcrash/arch/i386/lib/trace.c
|
|
* lkcdutils-4.1/libutil/kl_queue.c
|
|
*/
|
|
|
|
|
|
#ifdef X86
|
|
#ifdef REDHAT
|
|
|
|
#include "lkcd_x86_trace.h"
|
|
|
|
#undef XEN_HYPER_MODE
|
|
static int XEN_HYPER_MODE(void) { return (pc->flags & XEN_HYPER) != 0; }
|
|
|
|
static void *kl_alloc_block(int, int);
|
|
static void kl_free_block(void *);
|
|
static void GET_BLOCK(kaddr_t, unsigned, void *);
|
|
static void kl_get_kaddr(kaddr_t, void *);
|
|
static char *kl_funcname(kaddr_t);
|
|
static kaddr_t kl_funcaddr(kaddr_t);
|
|
static syment_t *kl_lkup_symaddr(kaddr_t);
|
|
static k_error_t kl_get_task_struct(kaddr_t, int, void *);
|
|
static kaddr_t kl_kernelstack(kaddr_t);
|
|
static kaddr_t get_call_pc(kaddr_t);
|
|
static kaddr_t get_call_pc_v2(kaddr_t);
|
|
static int get_jmp_instr(kaddr_t, kaddr_t, kaddr_t *, char *, char **);
|
|
static int is_push(unsigned int);
|
|
static int is_pop(unsigned int);
|
|
static int get_framesize(kaddr_t, struct bt_info *);
|
|
static int cache_framesize(int, kaddr_t funcaddr, int *, void **);
|
|
struct framesize_cache;
|
|
static int framesize_modify(struct framesize_cache *);
|
|
struct framesize_mods;
|
|
static int compiler_matches(struct framesize_mods *);
|
|
static sframe_t *alloc_sframe(trace_t *, int);
|
|
static void free_sframes(trace_t *);
|
|
static void free_trace_rec(trace_t *);
|
|
static void clean_trace_rec(trace_t *);
|
|
static int setup_trace_rec(kaddr_t, kaddr_t, int, trace_t *);
|
|
static int valid_ra(kaddr_t);
|
|
static int valid_ra_function(kaddr_t, char *);
|
|
static int eframe_incr(kaddr_t, char *);
|
|
static int find_trace(kaddr_t, kaddr_t, kaddr_t, kaddr_t, trace_t *, int);
|
|
static void dump_stack_frame(trace_t *, sframe_t *, FILE *);
|
|
static void print_trace(trace_t *, int, FILE *);
|
|
static int eframe_type(uaddr_t *);
|
|
static char *funcname_display(char *, ulong, struct bt_info *, char *);
|
|
static void print_eframe(FILE *, uaddr_t *);
|
|
static void trace_banner(FILE *);
|
|
static void print_kaddr(kaddr_t, FILE *, int);
|
|
int do_text_list(kaddr_t, int, FILE *);
|
|
int print_traces(struct bt_info *, int, int, FILE *);
|
|
static int get_instr_info(kaddr_t, instr_rec_t *);
|
|
static instr_rec_t *get_instr_stream(kaddr_t, int, int);
|
|
static void free_instr_stream(instr_rec_t *);
|
|
static trace_t *alloc_trace_rec(int);
|
|
static void kl_enqueue(element_t**, element_t*);
|
|
static element_t *kl_dequeue(element_t**);
|
|
static void handle_trace_error(struct bt_info *, int, FILE *);
|
|
static int verify_back_trace(struct bt_info *);
|
|
static int recoverable(struct bt_info *, FILE *);
|
|
static void fill_instr_cache(kaddr_t, char *);
|
|
static void do_bt_reference_check(struct bt_info *, sframe_t *);
|
|
static void print_stack_entry(struct bt_info *, int, ulong, ulong, char *,
|
|
sframe_t *, FILE *);
|
|
static struct syment *eframe_label(char *, ulong);
|
|
static int dump_framesize_cache(FILE *, struct framesize_cache *);
|
|
static int modify_framesize_cache_entry(FILE *, ulong, int);
|
|
static int framesize_debug(struct bt_info *, FILE *);
|
|
static int kernel_entry_from_user_space(sframe_t *, struct bt_info *);
|
|
|
|
k_error_t klib_error = 0;
|
|
|
|
static void *
|
|
kl_alloc_block(int size, int flags)
|
|
{
|
|
return ((void *)GETBUF(size));
|
|
}
|
|
|
|
static void
|
|
kl_free_block(void *blk)
|
|
{
|
|
if (blk)
|
|
FREEBUF(blk);
|
|
}
|
|
|
|
static void
|
|
GET_BLOCK(kaddr_t addr, unsigned size, void *buffer)
|
|
{
|
|
KL_ERROR = 0;
|
|
if (!readmem(addr, KVADDR, (void *)buffer, (ulong)size,
|
|
"GET_BLOCK", RETURN_ON_ERROR|QUIET)) {
|
|
console("GET_BLOCK: %lx (%d/0x%x)\n", addr, size, size);
|
|
KL_ERROR = KLE_INVALID_READ;
|
|
}
|
|
}
|
|
|
|
static void
|
|
kl_get_kaddr(kaddr_t addr, void *bp)
|
|
{
|
|
KL_ERROR = 0;
|
|
GET_BLOCK(addr, 4, bp);
|
|
}
|
|
|
|
static char *
|
|
kl_funcname(kaddr_t pc)
|
|
{
|
|
struct syment *sp;
|
|
char *buf, *name;
|
|
struct load_module *lm;
|
|
|
|
if ((sp = value_search(pc, NULL))) {
|
|
if (STREQ(sp->name, "_stext") &&
|
|
(sp->value == (sp+1)->value))
|
|
sp++;
|
|
switch (sp->type)
|
|
{
|
|
case 'r':
|
|
if (strstr(sp->name, "_interrupt") ||
|
|
STREQ(sp->name, "call_do_IRQ"))
|
|
return sp->name;
|
|
break;
|
|
case 't':
|
|
case 'T':
|
|
return sp->name;
|
|
}
|
|
if (is_kernel_text(pc))
|
|
return sp->name;
|
|
}
|
|
|
|
if (IS_MODULE_VADDR(pc)) {
|
|
buf = GETBUF(BUFSIZE);
|
|
name = &buf[BUFSIZE/2];
|
|
if (module_symbol(pc, NULL, NULL, buf, output_radix)) {
|
|
sprintf(name, "(%s)", buf);
|
|
return name;
|
|
} else {
|
|
FREEBUF(buf);
|
|
return "(unknown module)";
|
|
}
|
|
}
|
|
|
|
if ((lm = init_module_function(pc)))
|
|
return ("init_module");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static kaddr_t
|
|
kl_funcaddr(kaddr_t pc)
|
|
{
|
|
struct syment *sp;
|
|
struct load_module *lm;
|
|
|
|
if ((sp = value_search(pc, NULL))) {
|
|
switch (sp->type)
|
|
{
|
|
case 'r':
|
|
if (strstr(sp->name, "_interrupt") ||
|
|
STREQ(sp->name, "call_do_IRQ"))
|
|
return sp->value;
|
|
break;
|
|
case 't':
|
|
case 'T':
|
|
return sp->value;
|
|
}
|
|
if (is_kernel_text(pc))
|
|
return sp->value;
|
|
}
|
|
|
|
if ((lm = init_module_function(pc)))
|
|
return lm->mod_init_module_ptr;
|
|
|
|
return((kaddr_t)NULL);
|
|
}
|
|
|
|
static struct syment init_module_syment = {
|
|
.name = "init_module",
|
|
.type = 't',
|
|
};
|
|
|
|
static syment_t *
|
|
kl_lkup_symaddr(kaddr_t addr)
|
|
{
|
|
struct syment *sp;
|
|
struct load_module *lm;
|
|
|
|
if ((sp = value_search(addr, NULL)))
|
|
return sp;
|
|
|
|
if ((lm = init_module_function(addr))) {
|
|
init_module_syment.value = lm->mod_init_module_ptr;
|
|
return &init_module_syment;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static k_error_t
|
|
kl_get_task_struct(kaddr_t value, int mode, void *tsp)
|
|
{
|
|
KL_ERROR = 0;
|
|
|
|
if (value == tt->last_task_read)
|
|
BCOPY(tt->task_struct, tsp, TASK_STRUCT_SZ);
|
|
else
|
|
GET_BLOCK(value, TASK_STRUCT_SZ, tsp);
|
|
|
|
return KL_ERROR;
|
|
}
|
|
|
|
static kaddr_t
|
|
kl_kernelstack(kaddr_t task)
|
|
{
|
|
kaddr_t saddr;
|
|
|
|
return (saddr = (task + KSTACK_SIZE));
|
|
}
|
|
|
|
static void
|
|
print_kaddr(kaddr_t kaddr, FILE *ofp, int flag)
|
|
{
|
|
fprintf(ofp, "%lx", (ulong)kaddr);
|
|
}
|
|
#endif /* REDHAT */
|
|
|
|
/*
|
|
* lkcdutils-4.1/lcrash/arch/i386/lib/trace.c
|
|
*/
|
|
|
|
#ifndef REDHAT
|
|
/*
|
|
* Copyright 1999 Silicon Graphics, Inc. All rights reserved.
|
|
*/
|
|
#include <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);
|
|
|
|
/*
|
|
* If the old LKCD code fails, try disassembling...
|
|
*/
|
|
if (!addr)
|
|
return get_call_pc_v2(ra);
|
|
|
|
return(addr);
|
|
}
|
|
|
|
kaddr_t
|
|
get_call_pc_v2(kaddr_t ra)
|
|
{
|
|
int c ATTRIBUTE_UNUSED;
|
|
int line, len;
|
|
kaddr_t addr, addr2;
|
|
ulong offset;
|
|
struct syment *sp;
|
|
char *arglist[MAXARGS];
|
|
char buf[BUFSIZE];
|
|
|
|
if ((sp = value_search(ra, &offset))) {
|
|
if (offset == 0)
|
|
return 0;
|
|
} else
|
|
return 0;
|
|
|
|
addr = 0;
|
|
|
|
for (len = 2; len < 8; len++) {
|
|
open_tmpfile2();
|
|
sprintf(buf, "x/2i 0x%x", ra - len);
|
|
if (!gdb_pass_through(buf, pc->tmpfile2, GNU_RETURN_ON_ERROR)) {
|
|
close_tmpfile2();
|
|
return 0;
|
|
}
|
|
|
|
rewind(pc->tmpfile2);
|
|
line = 1;
|
|
while (fgets(buf, BUFSIZE, pc->tmpfile2)) {
|
|
c = parse_line(buf, arglist);
|
|
if ((line == 1) && !STREQ(arglist[2], "call"))
|
|
break;
|
|
if (line == 2) {
|
|
addr2 = (kaddr_t)htol(arglist[0], RETURN_ON_ERROR|QUIET, 0);
|
|
if (addr2 == ra) {
|
|
addr = ra - len;
|
|
break;
|
|
}
|
|
}
|
|
line++;
|
|
}
|
|
|
|
close_tmpfile2();
|
|
|
|
if (addr) {
|
|
if (CRASHDEBUG(1)) {
|
|
fprintf(fp, "get_call_pc_v2(ra: %x) -> %x -> ", ra, addr);
|
|
if (value_to_symstr(addr, buf, 0))
|
|
fprintf(fp, "%s", buf);
|
|
fprintf(fp, "\n");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return addr;
|
|
}
|
|
|
|
/*
|
|
* get_jmp_instr()
|
|
*/
|
|
int
|
|
get_jmp_instr(kaddr_t addr, kaddr_t isp, kaddr_t *caddr, char *fname,
|
|
char **cfname)
|
|
{
|
|
kaddr_t a;
|
|
int offset;
|
|
instr_rec_t *irp;
|
|
|
|
if (!(irp = get_instr_stream(addr, 1, 0))) {
|
|
return(1);
|
|
}
|
|
if (!irp->prev) {
|
|
free_instr_stream(irp);
|
|
return(1);
|
|
}
|
|
irp = irp->prev;
|
|
if (!(irp->opcode == 0x00e8) && !(irp->opcode == 0xff02)) {
|
|
free_instr_stream(irp);
|
|
return(1);
|
|
}
|
|
|
|
/* Check for the easiest case first...
|
|
*/
|
|
if (irp->opcode == 0xe8) {
|
|
a = irp->operand[0].op_addr;
|
|
if ((*cfname = kl_funcname(a))) {
|
|
*caddr = a;
|
|
}
|
|
} else if (irp->opcode == 0xff02) {
|
|
switch (irp->modrm) {
|
|
case 0x14:
|
|
if (irp->sib == 0x85) {
|
|
kl_get_kaddr(addr - 4, &a);
|
|
if (KL_ERROR) {
|
|
free_instr_stream(irp);
|
|
return(1);
|
|
}
|
|
if (strstr(fname, "system_call")) {
|
|
GET_BLOCK(isp + 28, 4, &offset);
|
|
a += (offset * 4);
|
|
kl_get_kaddr(a, &a);
|
|
if ((*cfname =
|
|
kl_funcname(a))) {
|
|
*caddr = a;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 0xc2: /* EAX */
|
|
case 0xca: /* ECX */
|
|
case 0xd2: /* EDX */
|
|
case 0xda: /* EBX */
|
|
case 0xea: /* EBP */
|
|
case 0xf2: /* ESI */
|
|
case 0xfa: /* EDI */
|
|
break;
|
|
}
|
|
}
|
|
free_instr_stream(irp);
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* is_push()
|
|
*/
|
|
int
|
|
is_push(unsigned int opcode)
|
|
{
|
|
switch(opcode) {
|
|
case 0x0006:
|
|
case 0x000e:
|
|
case 0x0016:
|
|
case 0x001e:
|
|
case 0x0050:
|
|
case 0x0051:
|
|
case 0x0052:
|
|
case 0x0053:
|
|
case 0x0054:
|
|
case 0x0055:
|
|
case 0x0056:
|
|
case 0x0057:
|
|
case 0x0068:
|
|
case 0x006a:
|
|
case 0x009c:
|
|
case 0x0fa0:
|
|
case 0x0fa8:
|
|
case 0xff06:
|
|
return(1);
|
|
case 0x0060:
|
|
return(2);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* is_pop()
|
|
*/
|
|
int
|
|
is_pop(unsigned int opcode)
|
|
{
|
|
switch(opcode) {
|
|
case 0x0007:
|
|
case 0x0017:
|
|
case 0x001f:
|
|
case 0x0058:
|
|
case 0x0059:
|
|
case 0x005a:
|
|
case 0x005b:
|
|
case 0x005c:
|
|
case 0x005d:
|
|
case 0x005e:
|
|
case 0x005f:
|
|
case 0x008f:
|
|
case 0x009d:
|
|
case 0x0fa1:
|
|
case 0x0fa9:
|
|
return(1);
|
|
case 0x0061:
|
|
return(2);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
#ifdef REDHAT
|
|
|
|
#define FRAMESIZE_VALIDATE (0x1)
|
|
|
|
struct framesize_cache {
|
|
kaddr_t pc;
|
|
int flags;
|
|
int frmsize;
|
|
int bp_adjust;
|
|
};
|
|
#define FRAMESIZE_CACHE (200)
|
|
|
|
static struct framesize_cache framesize_cache[FRAMESIZE_CACHE] = {{0}};
|
|
static struct framesize_cache framesize_cache_empty = {0};
|
|
|
|
#define FSZ_QUERY (1)
|
|
#define FSZ_VALIDATE (2)
|
|
#define FSZ_ENTER (3)
|
|
|
|
#define FRAMESIZE_CACHE_QUERY(pc,szp) cache_framesize(FSZ_QUERY, pc, szp, NULL)
|
|
#define FRAMESIZE_CACHE_ENTER(pc,szp) cache_framesize(FSZ_ENTER, pc, szp, NULL)
|
|
#define FRAMESIZE_CACHE_VALIDATE(pc,fcpp) cache_framesize(FSZ_VALIDATE, pc, NULL, fcpp)
|
|
|
|
static int
|
|
cache_framesize(int cmd, kaddr_t funcaddr, int *fsize, void **ptr)
|
|
{
|
|
int i;
|
|
static ulong last_cleared = 0;
|
|
|
|
retry:
|
|
for (i = 0; i < FRAMESIZE_CACHE; i++) {
|
|
if (framesize_cache[i].pc == funcaddr) {
|
|
switch (cmd)
|
|
{
|
|
case FSZ_VALIDATE:
|
|
*ptr = &framesize_cache[i];
|
|
return TRUE;
|
|
|
|
case FSZ_QUERY:
|
|
*fsize = framesize_cache[i].frmsize;
|
|
return TRUE;
|
|
|
|
case FSZ_ENTER:
|
|
*fsize = framesize_cache[i].frmsize;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* The entry does not exist.
|
|
*
|
|
* If FSZ_QUERY or FSZ_VALIDATE, return their
|
|
* no-such-entry indications.
|
|
*
|
|
* Otherwise, load up the entry with the new data, and
|
|
* and modify it with known kludgery.
|
|
*/
|
|
if (framesize_cache[i].pc == 0) {
|
|
switch (cmd)
|
|
{
|
|
case FSZ_QUERY:
|
|
return FALSE;
|
|
|
|
case FSZ_VALIDATE:
|
|
*ptr = &framesize_cache_empty;
|
|
return FALSE;
|
|
|
|
case FSZ_ENTER:
|
|
framesize_cache[i].pc = funcaddr;
|
|
framesize_cache[i].frmsize = *fsize;
|
|
framesize_cache[i].bp_adjust = 0;
|
|
framesize_modify(&framesize_cache[i]);
|
|
*fsize = framesize_cache[i].frmsize;
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
console("framesize_cache is full\n");
|
|
|
|
/*
|
|
* No place to put it, or it doesn't exist.
|
|
*/
|
|
switch (cmd)
|
|
{
|
|
case FSZ_VALIDATE:
|
|
*ptr = &framesize_cache_empty;
|
|
return FALSE;
|
|
|
|
case FSZ_QUERY:
|
|
return FALSE;
|
|
|
|
case FSZ_ENTER:
|
|
BZERO(&framesize_cache[last_cleared % FRAMESIZE_CACHE],
|
|
sizeof(struct framesize_cache));
|
|
last_cleared++;
|
|
goto retry;
|
|
}
|
|
|
|
return FALSE; /* can't get here -- for compiler happiness */
|
|
}
|
|
|
|
/*
|
|
* More kludgery for compiler oddities.
|
|
*/
|
|
#define COMPILER_VERSION_MASK (1) /* deprecated -- usable up to 3.3.3 */
|
|
#define COMPILER_VERSION_EQUAL (2)
|
|
#define COMPILER_VERSION_START (3)
|
|
#define COMPILER_VERSION_RANGE (4)
|
|
|
|
struct framesize_mods {
|
|
char *funcname;
|
|
char *called_function;
|
|
ulong compiler_flag;
|
|
ulong compiler1;
|
|
ulong compiler2;
|
|
int pre_adjust;
|
|
int post_adjust;
|
|
} framesize_mods[] = {
|
|
{ "do_select", "schedule_timeout",
|
|
COMPILER_VERSION_START, GCC(3,3,2), 0, 0, 0 },
|
|
{ "svc_recv", "schedule_timeout",
|
|
COMPILER_VERSION_START, GCC(3,3,2), 0, 0, 0 },
|
|
{ "__down_interruptible", "schedule",
|
|
COMPILER_VERSION_START, GCC(3,3,2), 0, 0, 0 },
|
|
{ "netconsole_netdump", NULL,
|
|
COMPILER_VERSION_START, GCC(3,3,2), 0, 0, -28 },
|
|
{ "generic_file_write", NULL,
|
|
COMPILER_VERSION_EQUAL, GCC(2,96,0), 0, 0, 20 },
|
|
{ "block_prepare_write", NULL,
|
|
COMPILER_VERSION_EQUAL, GCC(2,96,0), 0, 0, 72 },
|
|
{ "receive_chars", NULL,
|
|
COMPILER_VERSION_EQUAL, GCC(2,96,0), 0, 0, 48 },
|
|
{ "default_idle", NULL,
|
|
COMPILER_VERSION_START, GCC(2,96,0), 0, -4, 0 },
|
|
{ "hidinput_hid_event", NULL,
|
|
COMPILER_VERSION_START, GCC(4,1,2), 0, 0, 28 },
|
|
{ NULL, NULL, 0, 0, 0, 0, 0 },
|
|
};
|
|
|
|
static int
|
|
framesize_modify(struct framesize_cache *fc)
|
|
{
|
|
char *funcname;
|
|
struct framesize_mods *fmp;
|
|
|
|
if (!(funcname = kl_funcname(fc->pc)))
|
|
return FALSE;
|
|
|
|
if (fc->frmsize < 0) {
|
|
if (CRASHDEBUG(1))
|
|
error(INFO,
|
|
"bogus framesize: %d for pc: %lx (%s)\n",
|
|
fc->frmsize, fc->pc, funcname);
|
|
fc->frmsize = 0;
|
|
}
|
|
|
|
for (fmp = &framesize_mods[0]; fmp->funcname; fmp++) {
|
|
if (STREQ(funcname, fmp->funcname) &&
|
|
compiler_matches(fmp))
|
|
break;
|
|
}
|
|
|
|
if (!fmp->funcname)
|
|
return FALSE;
|
|
|
|
if (fmp->pre_adjust)
|
|
fc->frmsize += fmp->pre_adjust;
|
|
|
|
if (fmp->post_adjust)
|
|
fc->bp_adjust = fmp->post_adjust;
|
|
|
|
if (fmp->called_function) {
|
|
if (STREQ(fmp->called_function,x86_function_called_by(fc->pc)))
|
|
fc->flags |= FRAMESIZE_VALIDATE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static int
|
|
compiler_matches(struct framesize_mods *fmp)
|
|
{
|
|
switch (fmp->compiler_flag)
|
|
{
|
|
case COMPILER_VERSION_MASK:
|
|
if (fmp->compiler1 & (kt->flags & GCC_VERSION_DEPRECATED))
|
|
return TRUE;
|
|
break;
|
|
|
|
case COMPILER_VERSION_EQUAL:
|
|
if (THIS_GCC_VERSION == fmp->compiler1)
|
|
return TRUE;
|
|
break;
|
|
|
|
case COMPILER_VERSION_START:
|
|
if (THIS_GCC_VERSION >= fmp->compiler1)
|
|
return TRUE;
|
|
break;
|
|
|
|
case COMPILER_VERSION_RANGE:
|
|
if ((THIS_GCC_VERSION >= fmp->compiler1) &&
|
|
(THIS_GCC_VERSION <= fmp->compiler2))
|
|
return TRUE;
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static int
|
|
dump_framesize_cache(FILE *ofp, struct framesize_cache *fcp)
|
|
{
|
|
int i, count;
|
|
struct syment *sp, *spm;
|
|
ulong offset;
|
|
int once;
|
|
|
|
for (i = once = count = 0; i < FRAMESIZE_CACHE; i++) {
|
|
if (framesize_cache[i].pc == 0)
|
|
break;
|
|
|
|
count++;
|
|
|
|
if (fcp && (fcp != &framesize_cache[i]))
|
|
continue;
|
|
|
|
if (!once) {
|
|
fprintf(ofp,
|
|
"RET ADDR FSZ BPA V FUNCTION\n");
|
|
once++;
|
|
}
|
|
|
|
fprintf(ofp, "%8x %4d %4d %s ",
|
|
framesize_cache[i].pc,
|
|
framesize_cache[i].frmsize,
|
|
framesize_cache[i].bp_adjust,
|
|
framesize_cache[i].flags & FRAMESIZE_VALIDATE ?
|
|
"V" : "-");
|
|
if ((sp = value_search(framesize_cache[i].pc, &offset)) ||
|
|
(spm = kl_lkup_symaddr(framesize_cache[i].pc))) {
|
|
if (sp)
|
|
fprintf(ofp, "(%s+", sp->name);
|
|
else {
|
|
fprintf(ofp, "(%s+", spm->name);
|
|
offset = framesize_cache[i].pc - spm->value;
|
|
}
|
|
switch (pc->output_radix)
|
|
{
|
|
case 10:
|
|
fprintf(ofp, "%ld)", offset);
|
|
break;
|
|
default:
|
|
case 16:
|
|
fprintf(ofp, "%lx)", offset);
|
|
break;
|
|
}
|
|
}
|
|
fprintf(ofp, "\n");
|
|
if (fcp)
|
|
return 0;
|
|
}
|
|
|
|
if (!count)
|
|
fprintf(ofp, "framesize cache emtpy\n");
|
|
|
|
if (kt->flags & RA_SEEK)
|
|
fprintf(ofp, "RA_SEEK: ON\n");
|
|
if (kt->flags & NO_RA_SEEK)
|
|
fprintf(ofp, "NO_RA_SEEK: ON\n");
|
|
|
|
return count;
|
|
}
|
|
|
|
static int
|
|
modify_framesize_cache_entry(FILE *ofp, ulong eip, int framesize)
|
|
{
|
|
int i, found, all_cleared;
|
|
|
|
for (i = found = all_cleared = 0; i < FRAMESIZE_CACHE; i++) {
|
|
if (!eip) {
|
|
switch (framesize)
|
|
{
|
|
case -1:
|
|
framesize_cache[i].flags |= FRAMESIZE_VALIDATE;
|
|
break;
|
|
case -2:
|
|
framesize_cache[i].flags &= ~FRAMESIZE_VALIDATE;
|
|
break;
|
|
default:
|
|
framesize_cache[i].pc = 0;
|
|
framesize_cache[i].frmsize = 0;
|
|
framesize_cache[i].flags = 0;
|
|
all_cleared = TRUE;
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (framesize_cache[i].pc == 0)
|
|
break;
|
|
|
|
if (framesize_cache[i].pc == eip) {
|
|
found++;
|
|
|
|
switch (framesize)
|
|
{
|
|
case -1:
|
|
framesize_cache[i].flags |= FRAMESIZE_VALIDATE;
|
|
break;
|
|
case -2:
|
|
framesize_cache[i].flags &= ~FRAMESIZE_VALIDATE;
|
|
break;
|
|
default:
|
|
framesize_cache[i].frmsize = framesize;
|
|
break;
|
|
}
|
|
|
|
dump_framesize_cache(ofp, &framesize_cache[i]);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (eip && !found)
|
|
fprintf(ofp, "eip: %lx not found in framesize cache\n", eip);
|
|
|
|
if (all_cleared)
|
|
fprintf(ofp, "framesize cache cleared\n");
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* If eip, look for it and replace its frmsize with the passed-in value.
|
|
* If no eip, frmsize of zero means clear the cache, non-zero displays it.
|
|
*/
|
|
static int
|
|
framesize_debug(struct bt_info *bt, FILE *ofp)
|
|
{
|
|
ulong eip;
|
|
int frmsize;
|
|
|
|
eip = bt->hp->eip;
|
|
frmsize = (int)bt->hp->esp;
|
|
|
|
if (!eip) {
|
|
switch (frmsize)
|
|
{
|
|
case 0:
|
|
case -1:
|
|
case -2:
|
|
return modify_framesize_cache_entry(ofp, 0, frmsize);
|
|
default:
|
|
return dump_framesize_cache(ofp, NULL);
|
|
}
|
|
}
|
|
|
|
return modify_framesize_cache_entry(ofp, eip, frmsize);
|
|
}
|
|
|
|
#endif /* REDHAT */
|
|
|
|
/*
|
|
#define FRMSIZE_DBG 1
|
|
#define FRMSIZE2_DBG 1
|
|
*/
|
|
|
|
/*
|
|
* get_framesize()
|
|
*/
|
|
int
|
|
#ifdef REDHAT
|
|
get_framesize(kaddr_t pc, struct bt_info *bt)
|
|
#else
|
|
get_framesize(kaddr_t pc)
|
|
#endif
|
|
{
|
|
int size, ret, frmsize = 0;
|
|
kaddr_t addr;
|
|
instr_rec_t irp;
|
|
syment_t *sp;
|
|
#ifdef REDHAT
|
|
int check_IRQ_stack_switch = 0;
|
|
syment_t *jmpsp, *trampsp;
|
|
ulong offset;
|
|
int frmsize_restore = 0;
|
|
int last_add = 0;
|
|
|
|
if (FRAMESIZE_CACHE_QUERY(pc, &frmsize))
|
|
return frmsize;
|
|
|
|
frmsize = 0;
|
|
#endif
|
|
|
|
if (!(sp = kl_lkup_symaddr(pc))) {
|
|
return(0);
|
|
}
|
|
#ifdef REDHAT
|
|
if (STREQ(sp->name, "do_IRQ") && (tt->flags & IRQSTACKS))
|
|
check_IRQ_stack_switch++;
|
|
|
|
if (STREQ(sp->name, "stext_lock") || STRNEQ(sp->name, ".text.lock.")) {
|
|
jmpsp = x86_text_lock_jmp(pc, &offset);
|
|
if (jmpsp) {
|
|
console("get_framesize: stext_lock %lx => %s\n",
|
|
pc, jmpsp->name);
|
|
pc = jmpsp->value + offset;
|
|
sp = jmpsp;
|
|
}
|
|
}
|
|
|
|
if ((trampsp = x86_is_entry_tramp_address(pc, &offset))) {
|
|
if (STREQ(sp->name, "system_call"))
|
|
return 0;
|
|
pc = trampsp->value + offset;
|
|
}
|
|
#endif
|
|
#ifdef FRMSIZE_DBG
|
|
fprintf(stderr, "get_framesize(): pc=0x%x (0x%x:%s)\n",
|
|
pc, sp->s_addr, sp->s_name);
|
|
#endif
|
|
addr = sp->s_addr;
|
|
while (addr <= pc) {
|
|
bzero(&irp, sizeof(irp));
|
|
irp.aflag = 1;
|
|
irp.dflag = 1;
|
|
if (!(size = get_instr_info(addr, &irp))) {
|
|
fprintf(stderr, "ZERO SIZE!!\n");
|
|
return(-1);
|
|
}
|
|
if (size != irp.size) {
|
|
fprintf(stderr, "SIZE DOES NOT MATCH!!\n");
|
|
}
|
|
#ifdef REDHAT
|
|
/*
|
|
* Account for do_IRQ() stack switch.
|
|
*/
|
|
if (check_IRQ_stack_switch && (irp.opcode == 0xff02) &&
|
|
(irp.operand[0].op_reg == 0x7))
|
|
break;
|
|
/*
|
|
* Account for embedded "ret" instructions screwing up
|
|
* the frame size calculation.
|
|
*/
|
|
if (irp.opcode == 0xc3) {
|
|
frmsize += frmsize_restore;
|
|
frmsize_restore = 0;
|
|
last_add = FALSE;
|
|
} else if ((irp.opcode == 0x8300) &&
|
|
(irp.operand[0].op_reg == R_eSP)) {
|
|
frmsize_restore += irp.operand[1].op_addr;
|
|
last_add = TRUE;
|
|
} else if ((irp.opcode == 0x8100) &&
|
|
(irp.operand[0].op_reg == R_eSP)) {
|
|
frmsize_restore += irp.operand[1].op_addr;
|
|
last_add = TRUE;
|
|
} else if ((ret = is_pop(irp.opcode))) {
|
|
if (ret == 2)
|
|
frmsize_restore += (8 * 4);
|
|
else
|
|
frmsize_restore += 4;
|
|
last_add = FALSE;
|
|
} else {
|
|
if (last_add)
|
|
last_add = FALSE;
|
|
else
|
|
frmsize_restore = 0;
|
|
}
|
|
#endif /* REDHAT */
|
|
#ifdef REDHAT
|
|
if ((irp.opcode == 0x8300) || (irp.opcode == 0x8100)) {
|
|
#else
|
|
if (irp.opcode == 0x8300) {
|
|
#endif
|
|
/* e.g., addl $0x8,%esp */
|
|
if (irp.operand[0].op_reg == R_eSP) {
|
|
frmsize -= irp.operand[1].op_addr;
|
|
#ifdef FRMSIZE_DBG
|
|
fprintf(stderr, " addl --> 0x%x: -%d\n",
|
|
addr, irp.operand[1].op_addr);
|
|
#endif
|
|
}
|
|
} else if ((irp.opcode == 0x8305) || (irp.opcode == 0x8105)) {
|
|
/* e.g., subl $0x40,%esp */
|
|
if (irp.operand[0].op_reg == R_eSP) {
|
|
frmsize += irp.operand[1].op_addr;
|
|
#ifdef FRMSIZE_DBG
|
|
fprintf(stderr, " subl --> 0x%x: +%d\n",
|
|
addr, irp.operand[1].op_addr);
|
|
#endif
|
|
}
|
|
} else if ((ret = is_push(irp.opcode))) {
|
|
if (ret == 2) {
|
|
frmsize += (8 * 4);
|
|
#ifdef FRMSIZE_DBG
|
|
fprintf(stderr, " pusha --> 0x%x: +%d\n",
|
|
addr, (8 * 4));
|
|
#endif
|
|
} else {
|
|
frmsize += 4;
|
|
#ifdef FRMSIZE_DBG
|
|
fprintf(stderr, " pushl --> 0x%x: +%d\n" ,
|
|
addr, 4);
|
|
#endif
|
|
}
|
|
} else if ((ret = is_pop(irp.opcode))) {
|
|
if (ret == 2) {
|
|
frmsize -= (8 * 4);
|
|
#ifdef FRMSIZE_DBG
|
|
fprintf(stderr, " popa --> 0x%x: -%d\n",
|
|
addr, (8 * 4));
|
|
#endif
|
|
} else {
|
|
frmsize -= 4;
|
|
#ifdef FRMSIZE_DBG
|
|
fprintf(stderr, " popl --> 0x%x: -%d\n",
|
|
addr, 4);
|
|
#endif
|
|
}
|
|
#ifdef FRMSIZE2_DBG
|
|
} else {
|
|
fprintf(stderr, " 0x%x: opcode=0x%x\n",
|
|
addr, irp.opcode);
|
|
#endif
|
|
}
|
|
addr += size;
|
|
}
|
|
#ifdef REDHAT
|
|
/*
|
|
* Account for fact that schedule may not "call" anybody, plus
|
|
* the difference between gcc 3.2 and earlier compilers.
|
|
*/
|
|
if (STREQ(kl_funcname(pc), "schedule") &&
|
|
!(bt->flags & BT_CONTEXT_SWITCH))
|
|
frmsize -= THIS_GCC_VERSION == GCC(3,2,0) ? 4 : 8;
|
|
|
|
FRAMESIZE_CACHE_ENTER(pc, &frmsize);
|
|
#endif
|
|
return(frmsize);
|
|
}
|
|
|
|
#ifndef REDHAT
|
|
/*
|
|
* print_pc()
|
|
*/
|
|
void
|
|
print_pc(kaddr_t addr, FILE *ofp)
|
|
{
|
|
int offset = 0;
|
|
syment_t *sp;
|
|
|
|
if ((sp = kl_lkup_symaddr(addr))) {
|
|
offset = addr - sp->s_addr;
|
|
}
|
|
|
|
/* Print out address
|
|
*/
|
|
fprintf(ofp, "0x%x", addr);
|
|
|
|
/* Print out symbol name
|
|
*/
|
|
if (sp) {
|
|
if (offset) {
|
|
fprintf(ofp, " <%s+%d>", sp->s_name, offset);
|
|
} else {
|
|
fprintf(ofp, " <%s>", sp->s_name);
|
|
}
|
|
}
|
|
}
|
|
#endif /* !REDHAT */
|
|
|
|
/*
|
|
* alloc_sframe() -- Allocate a stack frame record
|
|
*/
|
|
sframe_t *
|
|
alloc_sframe(trace_t *trace, int flags)
|
|
{
|
|
sframe_t *f;
|
|
|
|
if (flags & C_PERM) {
|
|
f = (sframe_t *)kl_alloc_block(sizeof(sframe_t), K_PERM);
|
|
} else {
|
|
f = (sframe_t *)kl_alloc_block(sizeof(sframe_t), K_TEMP);
|
|
}
|
|
if (!f) {
|
|
return((sframe_t *)NULL);
|
|
}
|
|
f->level = trace->nframes;
|
|
return(f);
|
|
}
|
|
|
|
/*
|
|
* free_sframes() -- Free all stack frames allocated to a trace record.
|
|
*/
|
|
void
|
|
free_sframes(trace_t *t)
|
|
{
|
|
sframe_t *sf;
|
|
|
|
t->nframes = 0;
|
|
sf = t->frame;
|
|
while(t->frame) {
|
|
sf = (sframe_t *)kl_dequeue((element_t **)&t->frame);
|
|
if (sf->srcfile) {
|
|
kl_free_block((void *)sf->srcfile);
|
|
}
|
|
kl_free_block((void *)sf);
|
|
}
|
|
t->frame = (sframe_t *)NULL;
|
|
}
|
|
|
|
/*
|
|
* alloc_trace_rec() -- Allocate stack trace header
|
|
*/
|
|
trace_t *
|
|
alloc_trace_rec(int flags)
|
|
{
|
|
trace_t *t;
|
|
|
|
if (flags & C_PERM) {
|
|
t = (trace_t *)kl_alloc_block(sizeof(trace_t), K_PERM);
|
|
} else {
|
|
t = (trace_t *)kl_alloc_block(sizeof(trace_t), K_TEMP);
|
|
}
|
|
return(t);
|
|
}
|
|
|
|
/*
|
|
* free_trace_rec() -- Free memory associated with stack trace header
|
|
*/
|
|
void
|
|
free_trace_rec(trace_t *t)
|
|
{
|
|
int i;
|
|
|
|
if (t->tsp) {
|
|
kl_free_block(t->tsp);
|
|
}
|
|
for (i = 0; i < STACK_SEGMENTS; i++) {
|
|
if (t->stack[i].ptr) {
|
|
kl_free_block((void *)t->stack[i].ptr);
|
|
}
|
|
}
|
|
free_sframes(t);
|
|
kl_free_block((void *)t);
|
|
}
|
|
|
|
/*
|
|
* clean_trace_rec() -- Clean up stack trace record without releasing
|
|
* any of the allocated memory (except sframes).
|
|
*/
|
|
void
|
|
clean_trace_rec(trace_t *t)
|
|
{
|
|
int i;
|
|
|
|
t->flags = 0;
|
|
t->task = 0;
|
|
if (t->tsp) {
|
|
kl_free_block(t->tsp);
|
|
t->tsp = 0;
|
|
}
|
|
t->stackcnt = 0;
|
|
for (i = 0; i < STACK_SEGMENTS; i++) {
|
|
if (t->stack[i].ptr) {
|
|
t->stack[i].type = 0;
|
|
t->stack[i].size = 0;
|
|
t->stack[i].addr = (kaddr_t)NULL;
|
|
kl_free_block((void *)t->stack[i].ptr);
|
|
t->stack[i].ptr = (uaddr_t *)NULL;
|
|
}
|
|
}
|
|
free_sframes(t);
|
|
}
|
|
|
|
/*
|
|
* setup_trace_rec()
|
|
*/
|
|
int
|
|
setup_trace_rec(kaddr_t saddr, kaddr_t task, int flag, trace_t *trace)
|
|
{
|
|
int aflag = K_TEMP;
|
|
|
|
#ifdef REDHAT
|
|
KL_ERROR = 0;
|
|
#else
|
|
kl_reset_error();
|
|
#endif
|
|
|
|
if (flag & C_PERM) {
|
|
aflag = K_PERM;
|
|
}
|
|
if (task) {
|
|
trace->tsp = kl_alloc_block(TASK_STRUCT_SZ, aflag);
|
|
if (kl_get_task_struct(task, 2, trace->tsp)) {
|
|
kl_free_block(trace->tsp);
|
|
trace->tsp = NULL;
|
|
return(1);
|
|
}
|
|
}
|
|
trace->stack[0].type = S_KERNELSTACK;
|
|
trace->stack[0].size = STACK_SIZE;
|
|
|
|
/* Get the base address of the stack
|
|
*/
|
|
trace->stack[0].addr = saddr - trace->stack[0].size;
|
|
trace->stack[0].ptr = kl_alloc_block(STACK_SIZE, aflag);
|
|
if (KL_ERROR) {
|
|
clean_trace_rec(trace);
|
|
return(1);
|
|
}
|
|
#ifdef REDHAT
|
|
BCOPY(trace->bt->stackbuf, trace->stack[0].ptr, STACK_SIZE);
|
|
#else
|
|
GET_BLOCK(trace->stack[0].addr, STACK_SIZE, trace->stack[0].ptr);
|
|
#endif
|
|
if (KL_ERROR) {
|
|
clean_trace_rec(trace);
|
|
return(1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* valid_ra()
|
|
*/
|
|
int
|
|
valid_ra(kaddr_t ra)
|
|
{
|
|
kaddr_t pc;
|
|
|
|
if ((ra < KL_PAGE_OFFSET) || !kl_funcaddr(ra))
|
|
return(0);
|
|
|
|
if ((pc = get_call_pc(ra)))
|
|
return(1);
|
|
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* valid_ra_function()
|
|
*
|
|
* Same as above, but ensure that it calls the funcname passed in.
|
|
*/
|
|
int
|
|
valid_ra_function(kaddr_t ra, char *funcname)
|
|
{
|
|
kaddr_t pc;
|
|
|
|
if ((ra < KL_PAGE_OFFSET) || !kl_funcaddr(ra))
|
|
return(0);
|
|
|
|
if (!(pc = get_call_pc(ra)))
|
|
return(0);
|
|
|
|
if (STREQ(x86_function_called_by(ra-5), funcname))
|
|
return(1);
|
|
|
|
return(0);
|
|
}
|
|
|
|
#ifndef REDHAT
|
|
#include <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") || STREQ(irp.opcodep->name, "nop"))
|
|
val = 4;
|
|
else
|
|
val = 12;
|
|
|
|
if (adj >= 0)
|
|
eframe_adjust[adj] = val;
|
|
|
|
return val;
|
|
}
|
|
|
|
static int
|
|
xen_top_of_stack(struct bt_info *bt, char *funcname)
|
|
{
|
|
ulong stkptr, contents;
|
|
|
|
for (stkptr = bt->stacktop-4; stkptr > bt->stackbase; stkptr--) {
|
|
contents = GET_STACK_ULONG(stkptr);
|
|
if (kl_funcname(contents) == funcname)
|
|
return TRUE;
|
|
if (valid_ra(contents))
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static char *
|
|
xen_funcname(struct bt_info *bt, ulong pc)
|
|
{
|
|
char *funcname = kl_funcname(pc);
|
|
|
|
if (xen_top_of_stack(bt, funcname) &&
|
|
(pc >= symbol_value("hypercall")) &&
|
|
(pc < symbol_value("ret_from_intr")))
|
|
return "hypercall";
|
|
|
|
return funcname;
|
|
}
|
|
|
|
static int
|
|
userspace_return(kaddr_t frame, struct bt_info *bt)
|
|
{
|
|
ulong esp0, eframe_addr;
|
|
uint32_t *stkptr, *eframeptr;
|
|
|
|
if (INVALID_MEMBER(task_struct_thread) ||
|
|
(((esp0 = MEMBER_OFFSET("thread_struct", "esp0")) < 0) &&
|
|
((esp0 = MEMBER_OFFSET("thread_struct", "sp0")) < 0)))
|
|
eframe_addr = bt->stacktop - SIZE(pt_regs);
|
|
else
|
|
eframe_addr = ULONG(tt->task_struct +
|
|
OFFSET(task_struct_thread) + esp0) - SIZE(pt_regs);
|
|
|
|
if (!INSTACK(eframe_addr, bt))
|
|
return FALSE;
|
|
|
|
stkptr = (uint32_t *)(bt->stackbuf + ((ulong)frame - bt->stackbase));
|
|
eframeptr = (uint32_t *)(bt->stackbuf + (eframe_addr - bt->stackbase));
|
|
|
|
while (stkptr < eframeptr) {
|
|
if (is_kernel_text_offset(*stkptr))
|
|
return FALSE;
|
|
stkptr++;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* find_trace()
|
|
*
|
|
* Given a starting pc (start_cp), starting stack pointer (start_sp),
|
|
* and stack address, check to see if a valid trace is possible. A
|
|
* trace is considered valid if no errors are encountered (bad PC,
|
|
* bad SP, etc.) Certain errors are tolorated however. For example,
|
|
* if the current stack frame is an exception frame (e.g., VEC_*),
|
|
* go ahead and return success -- even if PC and SP obtained from
|
|
* the exception frame are bad (a partial trace is better than no
|
|
* trace)..
|
|
*
|
|
* Return zero if no valid trace was found. Otherwise, return the
|
|
* number of frames found. If the C_ALL flag is passed in, then
|
|
* return a trace even if it is a subtrace of a trace that was
|
|
* previously found.
|
|
*
|
|
* Parameters:
|
|
*
|
|
* start_pc starting program counter
|
|
* start_sp starting stack pointer
|
|
* check_pc if non-NULL, check to see if check_pc/check_sp
|
|
* check_sp are a sub-trace of trace beginning with spc/ssp
|
|
* trace structure containing all trace related info (frames,
|
|
* pages, page/frame counts, etc.
|
|
* flags
|
|
*/
|
|
int
|
|
find_trace(
|
|
kaddr_t start_pc,
|
|
kaddr_t start_sp,
|
|
kaddr_t check_pc,
|
|
kaddr_t check_sp,
|
|
trace_t *trace,
|
|
int flags)
|
|
{
|
|
int curstkidx = 0, frame_size, frame_type;
|
|
kaddr_t sp, pc, ra, bp, sbase, saddr, func_addr;
|
|
sframe_t *curframe;
|
|
char *func_name;
|
|
uaddr_t *sbp, *asp;
|
|
#ifdef REDHAT
|
|
struct syment *sp1;
|
|
ulong offset;
|
|
int flag;
|
|
int interrupted_system_call = FALSE;
|
|
struct bt_info *bt = trace->bt;
|
|
uaddr_t *pt;
|
|
|
|
curframe = NULL;
|
|
#endif
|
|
sbp = trace->stack[curstkidx].ptr;
|
|
sbase = trace->stack[curstkidx].addr;
|
|
saddr = sbase + trace->stack[curstkidx].size;
|
|
#ifdef REDHAT
|
|
bp = start_sp + get_framesize(start_pc, bt);
|
|
#else
|
|
bp = start_sp + get_framesize(start_pc);
|
|
#endif
|
|
if (KL_ERROR || (bp < sbase) || (bp >= saddr)) {
|
|
return(0);
|
|
}
|
|
pc = start_pc;
|
|
sp = start_sp;
|
|
func_name = kl_funcname(pc);
|
|
#ifdef REDHAT
|
|
if (STREQ(func_name, "context_switch"))
|
|
bt->flags |= BT_CONTEXT_SWITCH;
|
|
#endif
|
|
|
|
while (pc) {
|
|
|
|
/* LOOP TRAP! Make sure we are not just looping on the
|
|
* same frame forever.
|
|
*/
|
|
if ((trace->nframes > 1) &&
|
|
(curframe->funcname == curframe->prev->funcname) &&
|
|
(curframe->sp == curframe->prev->sp)) {
|
|
curframe->error = 1;
|
|
#ifdef REDHAT
|
|
bt->flags |= BT_LOOP_TRAP;
|
|
#endif
|
|
return(trace->nframes);
|
|
}
|
|
#ifdef REDHAT
|
|
/*
|
|
* If we wrap back to a lower stack location, we're cooked.
|
|
*/
|
|
if ((trace->nframes > 1) &&
|
|
(curframe->sp < curframe->prev->sp)) {
|
|
curframe->error = 1;
|
|
bt->flags |= BT_WRAP_TRAP;
|
|
return(trace->nframes);
|
|
}
|
|
#endif
|
|
|
|
/* Allocate space for a stack frame rec
|
|
*/
|
|
curframe = alloc_sframe(trace, flags);
|
|
if (!(func_addr = kl_funcaddr(pc))) {
|
|
curframe->error = KLE_BAD_PC;
|
|
UPDATE_FRAME(0, pc, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
return(trace->nframes);
|
|
}
|
|
|
|
/* Check to see if check_pc/check_sp points to a sub-trace
|
|
* of spc/ssp. If it does then don't return a trace (unless
|
|
* C_ALL). Make sure we free the curframe block since we
|
|
* wont be linking it in to the trace rec.
|
|
*/
|
|
if (check_pc && ((pc == check_pc) && (sp == check_sp))) {
|
|
kl_free_block((void *)curframe);
|
|
if (flags & C_ALL) {
|
|
return(trace->nframes);
|
|
} else {
|
|
return(0);
|
|
}
|
|
}
|
|
asp = (uaddr_t*)((uaddr_t)sbp + (STACK_SIZE - (saddr - sp)));
|
|
|
|
#ifdef REDHAT
|
|
if (XEN_HYPER_MODE()) {
|
|
func_name = xen_funcname(bt, pc);
|
|
if (STREQ(func_name, "idle_loop") || STREQ(func_name, "hypercall")
|
|
|| STREQ(func_name, "process_softirqs")
|
|
|| STREQ(func_name, "tracing_off")
|
|
|| STREQ(func_name, "page_fault")
|
|
|| STREQ(func_name, "handle_exception")
|
|
|| xen_top_of_stack(bt, func_name)) {
|
|
UPDATE_FRAME(func_name, pc, 0, sp, bp, asp, 0, 0, bp - sp, 0);
|
|
return(trace->nframes);
|
|
}
|
|
} else if (STREQ(closest_symbol(pc), "cpu_idle")) {
|
|
func_name = kl_funcname(pc);
|
|
UPDATE_FRAME(func_name, pc, 0, sp, bp, asp, 0, 0, bp - sp, 0);
|
|
return(trace->nframes);
|
|
}
|
|
|
|
ra = GET_STACK_ULONG(bp + 4);
|
|
/*
|
|
* HACK: The get_framesize() function can return the proper
|
|
* value -- as verified by disassembling the function -- but
|
|
* in rare circumstances there's more to the stack frame than
|
|
* meets the eye. Until I can figure out why, extra space
|
|
* can be added here for any "known" anomolies. gcc version
|
|
* restrictions are also added rather than assuming anything.
|
|
* See framesize_modify() for kludgery.
|
|
*/
|
|
if (!valid_ra(ra)) {
|
|
char *funcname;
|
|
struct framesize_cache *fcp;
|
|
|
|
funcname = kl_funcname(pc);
|
|
|
|
FRAMESIZE_CACHE_VALIDATE(pc, (void **)&fcp);
|
|
bp += fcp->bp_adjust;
|
|
|
|
ra = GET_STACK_ULONG(bp + 4);
|
|
|
|
/*
|
|
* This anomoly would be caught by the recovery
|
|
* speculation, but since we know it's an issue
|
|
* just catch it here first.
|
|
*/
|
|
if (STREQ(funcname, "schedule") &&
|
|
(THIS_GCC_VERSION >= GCC(3,2,3))) {
|
|
SEEK_VALID_RA();
|
|
/*
|
|
* else FRAMESIZE_VALIDATE has been turned on
|
|
*/
|
|
} else if (fcp->flags & FRAMESIZE_VALIDATE) {
|
|
SEEK_VALID_RA_FUNCTION(funcname);
|
|
/*
|
|
* Generic speculation continues the search for
|
|
* a valid RA at a higher stack address.
|
|
*/
|
|
} else if ((bt->flags & BT_SPECULATE) &&
|
|
!STREQ(funcname, "context_switch") &&
|
|
!STREQ(funcname, "die") &&
|
|
!(bt->frameptr && ((bp+4) < bt->frameptr)))
|
|
SEEK_VALID_RA();
|
|
}
|
|
#else
|
|
kl_get_kaddr(bp + 4, &ra);
|
|
#endif
|
|
|
|
|
|
/* Make sure that the ra we have is a valid one. If not
|
|
* then back up in the frame, word by word, until we find
|
|
* one that is good.
|
|
*/
|
|
if (!valid_ra(ra)) {
|
|
int i;
|
|
|
|
i = ((bp - sp + 8) / 4);
|
|
while (i) {
|
|
bp -= 4;
|
|
#ifdef REDHAT
|
|
ra = GET_STACK_ULONG(bp + 4);
|
|
#else
|
|
kl_get_kaddr(bp + 4, &ra);
|
|
#endif
|
|
if (valid_ra(ra)) {
|
|
break;
|
|
}
|
|
i--;
|
|
}
|
|
if (i == 0) {
|
|
#ifdef REDHAT
|
|
if (interrupted_system_call) {
|
|
if ((sp1 = x86_is_entry_tramp_address
|
|
(pc, &offset)))
|
|
pc = sp1->value + offset;
|
|
flag = EX_FRAME;
|
|
} else {
|
|
if (!XEN_HYPER_MODE() &&
|
|
!is_kernel_thread(bt->task) &&
|
|
(bt->stacktop == machdep->get_stacktop(bt->task))) {
|
|
if (((ulong)(bp+4) + SIZE(pt_regs)) > bt->stacktop)
|
|
flag = INCOMPLETE_EX_FRAME;
|
|
else if ((sp1 = eframe_label(NULL, pc)) &&
|
|
STREQ(sp1->name, "system_call"))
|
|
flag = EX_FRAME|SET_EX_FRAME_ADDR;
|
|
else if (STREQ(closest_symbol(pc), "ret_from_fork"))
|
|
flag = EX_FRAME|SET_EX_FRAME_ADDR;
|
|
else if (userspace_return(bp, bt))
|
|
flag = EX_FRAME|SET_EX_FRAME_ADDR;
|
|
else {
|
|
curframe->error = KLE_BAD_RA;
|
|
flag = 0;
|
|
}
|
|
} else {
|
|
curframe->error = KLE_BAD_RA;
|
|
flag = 0;
|
|
}
|
|
}
|
|
#else
|
|
curframe->error = KLE_BAD_RA;
|
|
#endif
|
|
UPDATE_FRAME(func_name, pc, ra, sp,
|
|
bp + 4, asp, 0, 0, 0, flag);
|
|
|
|
return(trace->nframes);
|
|
}
|
|
}
|
|
|
|
UPDATE_FRAME(func_name, pc, ra, sp, bp + 4, asp, 0, 0, 0, 0);
|
|
curframe->frame_size = curframe->fp - curframe->sp + 4;
|
|
|
|
/* Gather starting information for the next frame
|
|
*/
|
|
pc = get_call_pc(ra);
|
|
#ifdef USE_FRAMEPTRS
|
|
kl_get_kaddr(bp, &bp);
|
|
if (KL_ERROR) {
|
|
curframe->error = 2;
|
|
return(trace->nframes);
|
|
}
|
|
#else
|
|
/* It's possible for get_framesize() to return a size
|
|
* that is larger than the actual frame size (because
|
|
* all it does is count the push, pop, addl, and subl
|
|
* instructions that effect the SP). If we are real near
|
|
* the top of the stack, this might cause bp to overflow.
|
|
* This will be fixed above, but we need to bring bp
|
|
* back into the legal range so we don't crap out
|
|
* before we can get to it...
|
|
*/
|
|
#ifdef REDHAT
|
|
frame_size = get_framesize(pc, bt);
|
|
interrupted_system_call = FALSE;
|
|
#else
|
|
frame_size = get_framesize(pc);
|
|
#endif
|
|
if ((curframe->fp + frame_size) >= saddr) {
|
|
bp = saddr - 4;
|
|
} else {
|
|
bp = curframe->fp + frame_size;
|
|
}
|
|
#endif
|
|
func_name = kl_funcname(pc);
|
|
if (func_name && !XEN_HYPER_MODE()) {
|
|
if (strstr(func_name, "kernel_thread")) {
|
|
ra = 0;
|
|
bp = saddr - 4;
|
|
asp = (uaddr_t*)
|
|
((uaddr_t)sbp + (STACK_SIZE - 12));
|
|
curframe = alloc_sframe(trace, flags);
|
|
UPDATE_FRAME(func_name, pc,
|
|
ra, sp, bp, asp, 0, 0, 16, 0);
|
|
return(trace->nframes);
|
|
} else if (strstr(func_name, "is386")) {
|
|
ra = 0;
|
|
bp = sp = saddr - 4;
|
|
asp = curframe->asp;
|
|
curframe = alloc_sframe(trace, flags);
|
|
UPDATE_FRAME(func_name, pc,
|
|
ra, sp, bp, asp, 0, 0, 0, 0);
|
|
return(trace->nframes);
|
|
} else if (STREQ(func_name, "ret_from_fork")) {
|
|
ra = 0;
|
|
bp = sp = saddr - 4;
|
|
asp = curframe->asp;
|
|
curframe = alloc_sframe(trace, flags);
|
|
UPDATE_FRAME(func_name, pc,
|
|
ra, sp, bp, asp, 0, 0, 0, EX_FRAME|SET_EX_FRAME_ADDR);
|
|
return(trace->nframes);
|
|
#ifdef REDHAT
|
|
} else if (STREQ(func_name, "cpu_idle") ||
|
|
STREQ(func_name, "cpu_startup_entry") ||
|
|
STREQ(func_name, "start_secondary")) {
|
|
ra = 0;
|
|
bp = sp = saddr - 4;
|
|
asp = curframe->asp;
|
|
curframe = alloc_sframe(trace, flags);
|
|
UPDATE_FRAME(func_name, pc,
|
|
ra, sp, bp, asp, 0, 0, 0, 0);
|
|
return(trace->nframes);
|
|
|
|
} else if (strstr(func_name, "system_call") ||
|
|
strstr(func_name, "sysenter_past_esp") ||
|
|
eframe_label(func_name, pc) ||
|
|
strstr(func_name, "syscall_call") ||
|
|
strstr(func_name, "signal_return") ||
|
|
strstr(func_name, "reschedule") ||
|
|
kernel_entry_from_user_space(curframe, bt)) {
|
|
#else
|
|
} else if (strstr(func_name, "system_call")) {
|
|
#endif
|
|
/*
|
|
* user exception frame, kernel stack ends
|
|
* here.
|
|
*/
|
|
bp = saddr - 4;
|
|
sp = curframe->fp + 4;
|
|
#ifdef REDHAT
|
|
ra = GET_STACK_ULONG(bp-16);
|
|
#else
|
|
kl_get_kaddr(bp-16, &ra);
|
|
#endif
|
|
curframe = alloc_sframe(trace, flags);
|
|
asp = (uaddr_t*)((uaddr_t)sbp +
|
|
(STACK_SIZE - (saddr - sp)));
|
|
UPDATE_FRAME(func_name, pc, ra, sp, bp,
|
|
asp, 0, 0, (bp - sp + 4), EX_FRAME);
|
|
return(trace->nframes);
|
|
#ifdef REDHAT
|
|
} else if (strstr(func_name, "error_code")
|
|
|| STREQ(func_name, "nmi_stack_correct")
|
|
|| STREQ(func_name, "nmi")) {
|
|
#else
|
|
} else if (strstr(func_name, "error_code")) {
|
|
#endif
|
|
/* an exception frame */
|
|
sp = curframe->fp + eframe_incr(pc, func_name);
|
|
|
|
bp = sp + (KERNEL_EFRAME_SZ-1)*4;
|
|
asp = (uaddr_t*)((uaddr_t)sbp + (STACK_SIZE -
|
|
(saddr - sp)));
|
|
curframe = alloc_sframe(trace, flags);
|
|
ra = asp[INT_EFRAME_EIP];
|
|
frame_type = eframe_type(asp);
|
|
UPDATE_FRAME(func_name, pc, ra, sp, bp, asp,
|
|
0, 0, (bp - sp + 4), EX_FRAME);
|
|
|
|
/* prepare for next kernel frame, if present */
|
|
if (frame_type == KERNEL_EFRAME) {
|
|
pc = asp[INT_EFRAME_EIP];
|
|
sp = curframe->fp+4;
|
|
#ifdef REDHAT
|
|
bp = sp + get_framesize(pc, bt);
|
|
#else
|
|
bp = sp + get_framesize(pc);
|
|
#endif
|
|
func_name = kl_funcname(pc);
|
|
continue;
|
|
} else {
|
|
return(trace->nframes);
|
|
}
|
|
} else if (is_task_active(bt->task) &&
|
|
(strstr(func_name, "call_do_IRQ") ||
|
|
strstr(func_name, "common_interrupt") ||
|
|
strstr(func_name, "reboot_interrupt") ||
|
|
strstr(func_name, "call_function_interrupt"))) {
|
|
/* Interrupt frame */
|
|
sp = curframe->fp + 4;
|
|
asp = (uaddr_t*)((uaddr_t)sbp + (STACK_SIZE -
|
|
(saddr - sp)));
|
|
frame_type = eframe_type(asp);
|
|
if (frame_type == KERNEL_EFRAME)
|
|
bp = curframe->fp+(KERNEL_EFRAME_SZ-1)*4;
|
|
else
|
|
bp = curframe->fp+(USER_EFRAME_SZ-1)*4;
|
|
curframe = alloc_sframe(trace, flags);
|
|
ra = asp[INT_EFRAME_EIP];
|
|
UPDATE_FRAME(func_name, pc, ra, sp, bp + 4, asp,
|
|
0, 0, curframe->fp - curframe->sp+4, EX_FRAME);
|
|
|
|
/* prepare for next kernel frame, if present */
|
|
if (frame_type == KERNEL_EFRAME) {
|
|
sp = curframe->fp + 4;
|
|
pc = asp[INT_EFRAME_EIP];
|
|
#ifdef REDHAT
|
|
bp = sp + get_framesize(pc, bt);
|
|
#else
|
|
bp = sp + get_framesize(pc);
|
|
#endif
|
|
func_name = kl_funcname(pc);
|
|
#ifdef REDHAT
|
|
/* interrupted system_call entry */
|
|
if (STREQ(func_name, "system_call"))
|
|
interrupted_system_call = TRUE;
|
|
#endif
|
|
continue;
|
|
} else {
|
|
return trace->nframes;
|
|
}
|
|
}
|
|
}
|
|
if (func_name && XEN_HYPER_MODE()) {
|
|
if (STREQ(func_name, "continue_nmi") ||
|
|
STREQ(func_name, "vmx_asm_vmexit_handler") ||
|
|
STREQ(func_name, "common_interrupt") ||
|
|
STREQ(func_name, "handle_nmi_mce") ||
|
|
STREQ(func_name, "deferred_nmi")) {
|
|
/* Interrupt frame */
|
|
sp = curframe->fp + 4;
|
|
asp = (uaddr_t*)((uaddr_t)sbp + (STACK_SIZE -
|
|
(saddr - sp)));
|
|
bp = curframe->fp + (12 * 4);
|
|
curframe = alloc_sframe(trace, flags);
|
|
ra = *(asp + 9);
|
|
UPDATE_FRAME(func_name, pc, ra, sp, bp + 4, asp,
|
|
0, 0, curframe->fp - curframe->sp+4, 12 * 4);
|
|
|
|
/* contunue next frame */
|
|
pc = ra;
|
|
sp = curframe->fp + 4;
|
|
bp = sp + get_framesize(pc, bt);
|
|
func_name = kl_funcname(pc);
|
|
if (!func_name)
|
|
return trace->nframes;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check for hypervisor_callback from user-space.
|
|
*/
|
|
if ((bt->flags & BT_XEN_STOP_THIS_CPU) && bt->tc->mm_struct &&
|
|
STREQ(kl_funcname(curframe->pc), "hypervisor_callback")) {
|
|
pt = curframe->asp+1;
|
|
if (eframe_type(pt) == USER_EFRAME) {
|
|
if (program_context.debug >= 1) /* pc above */
|
|
error(INFO,
|
|
"hypervisor_callback from user space\n");
|
|
curframe->asp++;
|
|
curframe->flag |= EX_FRAME;
|
|
return(trace->nframes);
|
|
}
|
|
}
|
|
|
|
/* Make sure our next frame pointer is valid (in the stack).
|
|
*/
|
|
if ((bp < sbase) || (bp >= saddr)) {
|
|
curframe->error = 3;
|
|
return(trace->nframes);
|
|
}
|
|
sp = curframe->fp + 4;
|
|
}
|
|
return(trace->nframes);
|
|
}
|
|
|
|
static int
|
|
kernel_entry_from_user_space(sframe_t *curframe, struct bt_info *bt)
|
|
{
|
|
ulong stack_segment;
|
|
|
|
if (is_kernel_thread(bt->tc->task))
|
|
return FALSE;
|
|
|
|
stack_segment = GET_STACK_ULONG(curframe->fp + 4 + SIZE(pt_regs) - sizeof(kaddr_t));
|
|
|
|
if ((curframe->fp + 4 + SIZE(pt_regs)) == GET_STACKTOP(bt->task)) {
|
|
if ((stack_segment == 0x7b) || (stack_segment == 0x2b))
|
|
return TRUE;
|
|
}
|
|
|
|
if ((curframe->fp + 4 + SIZE(pt_regs) + 8) == GET_STACKTOP(bt->task)) {
|
|
if ((stack_segment == 0x7b) || (stack_segment == 0x2b))
|
|
return TRUE;
|
|
}
|
|
|
|
if (userspace_return(curframe->fp+4, bt))
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
#ifndef REDHAT
|
|
/*
|
|
* pc_offset()
|
|
*/
|
|
int
|
|
pc_offset(kaddr_t pc)
|
|
{
|
|
kaddr_t func_addr;
|
|
|
|
if ((func_addr = kl_funcaddr(pc))) {
|
|
return(pc - func_addr);
|
|
}
|
|
return(-1);
|
|
}
|
|
#endif /* !REDHAT */
|
|
|
|
/*
|
|
* dump_stack_frame()
|
|
*/
|
|
void
|
|
dump_stack_frame(trace_t *trace, sframe_t *curframe, FILE *ofp)
|
|
{
|
|
int i, first_time = 1;
|
|
kaddr_t sp;
|
|
uaddr_t *asp;
|
|
char buf[BUFSIZE];
|
|
|
|
sp = curframe->sp;
|
|
asp = curframe->asp;
|
|
|
|
for (i = 0; i < curframe->frame_size / 4; i++) {
|
|
if (!(i % 4)) {
|
|
if (first_time) {
|
|
first_time = 0;
|
|
#ifdef REDHAT
|
|
fprintf(ofp, " %x: %s ", sp,
|
|
format_stack_entry(trace->bt, buf, *asp++, 0));
|
|
#else
|
|
fprintf(ofp, " %x: %08x ", sp, *asp++);
|
|
#endif
|
|
} else {
|
|
#ifdef REDHAT
|
|
fprintf(ofp, "\n %x: ", sp);
|
|
#else
|
|
fprintf(ofp, "\n %x: ", sp);
|
|
#endif
|
|
fprintf(ofp, "%s ",
|
|
format_stack_entry(trace->bt, buf, *asp++, 0));
|
|
}
|
|
sp += 16;
|
|
} else {
|
|
fprintf(ofp, "%s ",
|
|
format_stack_entry(trace->bt, buf, *asp++, 0));
|
|
}
|
|
}
|
|
if (curframe->frame_size) {
|
|
#ifdef REDHAT
|
|
fprintf(ofp, "\n");
|
|
#else
|
|
fprintf(ofp, "\n\n");
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/*
|
|
* eframe_address()
|
|
*/
|
|
static uaddr_t *
|
|
eframe_address(sframe_t *frmp, struct bt_info *bt)
|
|
{
|
|
ulong esp0, pt;
|
|
|
|
if (!(frmp->flag & SET_EX_FRAME_ADDR) ||
|
|
INVALID_MEMBER(task_struct_thread) ||
|
|
(((esp0 = MEMBER_OFFSET("thread_struct", "esp0")) < 0) &&
|
|
((esp0 = MEMBER_OFFSET("thread_struct", "sp0")) < 0)))
|
|
return frmp->asp;
|
|
/*
|
|
* Work required in rarely-seen SET_EX_FRAME_ADDR circumstances.
|
|
*/
|
|
pt = ULONG(tt->task_struct + OFFSET(task_struct_thread) + esp0)
|
|
- SIZE(pt_regs);
|
|
|
|
if (!INSTACK(pt, bt))
|
|
return frmp->asp;
|
|
|
|
return ((uint32_t *)(bt->stackbuf + (pt - bt->stackbase)));
|
|
}
|
|
|
|
|
|
/*
|
|
* print_trace()
|
|
*/
|
|
void
|
|
print_trace(trace_t *trace, int flags, FILE *ofp)
|
|
{
|
|
sframe_t *frmp;
|
|
#ifdef REDHAT
|
|
kaddr_t fp = 0;
|
|
kaddr_t last_fp ATTRIBUTE_UNUSED;
|
|
kaddr_t last_pc, next_fp, next_pc;
|
|
struct bt_info *bt;
|
|
|
|
bt = trace->bt;
|
|
last_fp = last_pc = next_fp = next_pc = 0;
|
|
#else
|
|
int offset;
|
|
#endif
|
|
|
|
if ((frmp = trace->frame)) {
|
|
do {
|
|
#ifdef REDHAT
|
|
if (trace->bt->flags & BT_LOOP_TRAP) {
|
|
if (frmp->prev && frmp->error &&
|
|
(frmp->pc == frmp->prev->pc) &&
|
|
(frmp->fp == frmp->prev->fp))
|
|
goto print_trace_error;
|
|
}
|
|
|
|
if ((trace->bt->flags & BT_WRAP_TRAP) && frmp->error)
|
|
goto print_trace_error;
|
|
|
|
/*
|
|
* We're guaranteed to run into an error when unwinding
|
|
* a hard or soft IRQ stack, so just bail with success.
|
|
*/
|
|
if ((frmp->next != trace->frame) && frmp->next->error &&
|
|
(bt->flags & (BT_LOOP_TRAP|BT_WRAP_TRAP)) &&
|
|
(bt->flags & (BT_HARDIRQ|BT_SOFTIRQ)))
|
|
return;
|
|
|
|
if ((frmp->level == 0) && (bt->flags & BT_XEN_STOP_THIS_CPU)) {
|
|
print_stack_entry(trace->bt, 0, trace->bt->stkptr,
|
|
symbol_value("stop_this_cpu"),
|
|
value_symbol(symbol_value("stop_this_cpu")),
|
|
frmp, ofp);
|
|
}
|
|
|
|
print_stack_entry(trace->bt, (trace->bt->flags &
|
|
(BT_BUMP_FRAME_LEVEL|BT_XEN_STOP_THIS_CPU)) ?
|
|
frmp->level + 1 : frmp->level,
|
|
fp ? (ulong)fp : trace->bt->stkptr,
|
|
(ulong)frmp->pc, frmp->funcname, frmp, ofp);
|
|
|
|
if (trace->bt->flags & BT_LOOP_TRAP) {
|
|
last_fp = fp ? (ulong)fp : trace->bt->stkptr;
|
|
last_pc = frmp->pc;
|
|
}
|
|
|
|
fp = frmp->fp;
|
|
#else
|
|
fprintf(ofp, "%2d %s", frmp->level, frmp->funcname);
|
|
offset = pc_offset(frmp->pc);
|
|
if (offset > 0) {
|
|
fprintf(ofp, "+%d", offset);
|
|
} else if (offset < 0) {
|
|
fprintf(ofp, "+<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;
|
|
ulong contents;
|
|
trace_t *trace;
|
|
|
|
#ifdef REDHAT
|
|
int nframes = 0;
|
|
kaddr_t task = bt->task;
|
|
KL_ERROR = 0;
|
|
tsp = NULL;
|
|
|
|
if (bt->flags & BT_FRAMESIZE_DEBUG)
|
|
return(framesize_debug(bt, ofp));
|
|
|
|
if (kt->flags & RA_SEEK)
|
|
bt->flags |= BT_SPECULATE;
|
|
|
|
if (XENDUMP_DUMPFILE() && XEN() && is_task_active(bt->task) &&
|
|
STREQ(kl_funcname(bt->instptr), "stop_this_cpu")) {
|
|
/*
|
|
* bt->instptr of "stop_this_cpu" is not a return
|
|
* address -- replace it with the actual return
|
|
* address found at the bt->stkptr location.
|
|
*/
|
|
if (readmem((ulong)bt->stkptr, KVADDR, &eip,
|
|
sizeof(ulong), "xendump eip", RETURN_ON_ERROR))
|
|
bt->instptr = eip;
|
|
bt->flags |= BT_XEN_STOP_THIS_CPU;
|
|
if (CRASHDEBUG(1))
|
|
error(INFO, "replacing stop_this_cpu with %s\n",
|
|
kl_funcname(bt->instptr));
|
|
}
|
|
|
|
if (XENDUMP_DUMPFILE() && XEN() && is_idle_thread(bt->task) &&
|
|
is_task_active(bt->task) &&
|
|
!(kt->xen_flags & XEN_SUSPEND) &&
|
|
STREQ(kl_funcname(bt->instptr), "schedule")) {
|
|
/*
|
|
* This is an invalid (stale) schedule reference
|
|
* left in the task->thread. Move down the stack
|
|
* until the smp_call_function_interrupt return
|
|
* address is found.
|
|
*/
|
|
saddr = bt->stkptr;
|
|
while (readmem(saddr, KVADDR, &eip,
|
|
sizeof(ulong), "xendump esp", RETURN_ON_ERROR)) {
|
|
if (STREQ(kl_funcname(eip), "smp_call_function_interrupt")) {
|
|
bt->instptr = eip;
|
|
bt->stkptr = saddr;
|
|
bt->flags |= BT_XEN_STOP_THIS_CPU;
|
|
if (CRASHDEBUG(1))
|
|
error(INFO,
|
|
"switch schedule to smp_call_function_interrupt\n");
|
|
break;
|
|
}
|
|
saddr -= sizeof(void *);
|
|
if (saddr <= bt->stackbase)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (XENDUMP_DUMPFILE() && XEN() && is_idle_thread(bt->task) &&
|
|
is_task_active(bt->task) &&
|
|
(kt->xen_flags & XEN_SUSPEND) &&
|
|
STREQ(kl_funcname(bt->instptr), "schedule")) {
|
|
int framesize = 0;
|
|
/*
|
|
* This is an invalid (stale) schedule reference
|
|
* left in the task->thread. Move down the stack
|
|
* until the hypercall_page() return address is
|
|
* found, and fix up its framesize as we go.
|
|
*/
|
|
saddr = bt->stacktop;
|
|
while (readmem(saddr, KVADDR, &eip,
|
|
sizeof(ulong), "xendump esp", RETURN_ON_ERROR)) {
|
|
|
|
if (STREQ(kl_funcname(eip), "xen_idle"))
|
|
framesize += sizeof(ulong);
|
|
else if (framesize)
|
|
framesize += sizeof(ulong);
|
|
|
|
if (STREQ(kl_funcname(eip), "hypercall_page")) {
|
|
int framesize = 24;
|
|
bt->instptr = eip;
|
|
bt->stkptr = saddr;
|
|
if (CRASHDEBUG(1))
|
|
error(INFO,
|
|
"switch schedule to hypercall_page (framesize: %d)\n",
|
|
framesize);
|
|
FRAMESIZE_CACHE_ENTER(eip, &framesize);
|
|
break;
|
|
}
|
|
saddr -= sizeof(void *);
|
|
if (saddr <= bt->stackbase)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (XENDUMP_DUMPFILE() && XEN() && !is_idle_thread(bt->task) &&
|
|
is_task_active(bt->task) &&
|
|
STREQ(kl_funcname(bt->instptr), "schedule")) {
|
|
/*
|
|
* This is an invalid (stale) schedule reference
|
|
* left in the task->thread. Move down the stack
|
|
* until the smp_call_function_interrupt return
|
|
* address is found.
|
|
*/
|
|
saddr = bt->stacktop;
|
|
while (readmem(saddr, KVADDR, &eip,
|
|
sizeof(ulong), "xendump esp", RETURN_ON_ERROR)) {
|
|
if (STREQ(kl_funcname(eip), "smp_call_function_interrupt")) {
|
|
bt->instptr = eip;
|
|
bt->stkptr = saddr;
|
|
bt->flags |= BT_XEN_STOP_THIS_CPU;
|
|
if (CRASHDEBUG(1))
|
|
error(INFO,
|
|
"switch schedule to smp_call_function_interrupt\n");
|
|
break;
|
|
}
|
|
saddr -= sizeof(void *);
|
|
if (saddr <= bt->stackbase)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (STREQ(kl_funcname(bt->instptr), "crash_kexec") ||
|
|
STREQ(kl_funcname(bt->instptr), "crash_nmi_callback")) {
|
|
if (readmem(bt->stkptr-4, KVADDR, &contents, sizeof(ulong),
|
|
"stkptr-4 contents", RETURN_ON_ERROR|QUIET) &&
|
|
(contents == bt->instptr))
|
|
bt->stkptr -= 4;
|
|
}
|
|
|
|
if (!verify_back_trace(bt) && !recoverable(bt, ofp) &&
|
|
!BT_REFERENCE_CHECK(bt))
|
|
error(INFO, "cannot resolve stack trace:\n");
|
|
|
|
if (BT_REFERENCE_CHECK(bt))
|
|
return(0);
|
|
#endif
|
|
|
|
if (!XEN_HYPER_MODE()) {
|
|
if (!(tsp = kl_alloc_block(TASK_STRUCT_SZ, K_TEMP))) {
|
|
return(1);
|
|
}
|
|
if (kl_get_task_struct(task, 2, tsp)) {
|
|
kl_free_block(tsp);
|
|
return(1);
|
|
}
|
|
}
|
|
trace = (trace_t *)alloc_trace_rec(C_TEMP);
|
|
if (!trace) {
|
|
#ifdef REDHAT
|
|
error(INFO, "Could not alloc trace rec!\n");
|
|
#else
|
|
fprintf(KL_ERRORFP, "Could not alloc trace rec!\n");
|
|
#endif
|
|
return(1);
|
|
} else {
|
|
#ifdef REDHAT
|
|
saddr = kl_kernelstack(bt->stackbase);
|
|
eip = bt->instptr;
|
|
esp = bt->stkptr;
|
|
trace->bt = bt;
|
|
#else
|
|
saddr = kl_kernelstack(task);
|
|
if (kl_smp_dumptask(task)) {
|
|
eip = kl_dumpeip(task);
|
|
esp = kl_dumpesp(task);
|
|
} else {
|
|
if (LINUX_2_2_X(KL_LINUX_RELEASE)) {
|
|
eip = KL_UINT(K_PTR(tsp, "task_struct", "tss"),
|
|
"thread_struct", "eip");
|
|
esp = KL_UINT(K_PTR(tsp, "task_struct", "tss"),
|
|
"thread_struct", "esp");
|
|
} else {
|
|
eip = KL_UINT(
|
|
K_PTR(tsp, "task_struct", "thread"),
|
|
"thread_struct", "eip");
|
|
esp = KL_UINT(
|
|
K_PTR(tsp, "task_struct", "thread"),
|
|
"thread_struct", "esp");
|
|
}
|
|
}
|
|
#endif
|
|
if (esp < KL_PAGE_OFFSET || eip < KL_PAGE_OFFSET) {
|
|
#ifdef REDHAT
|
|
error(INFO, "Task in user space -- no backtrace\n");
|
|
#else
|
|
fprintf(KL_ERRORFP, "Task in user space, No backtrace\n");
|
|
#endif
|
|
return 1;
|
|
}
|
|
setup_trace_rec(saddr, 0, 0, trace);
|
|
if (KL_ERROR) {
|
|
#ifdef REDHAT
|
|
error(INFO, "Error setting up trace rec!\n");
|
|
#else
|
|
fprintf(KL_ERRORFP, "Error setting up trace rec!\n");
|
|
#endif
|
|
free_trace_rec(trace);
|
|
return(1);
|
|
}
|
|
#ifdef REDHAT
|
|
nframes = find_trace(eip, esp, 0, 0, trace, 0);
|
|
#else
|
|
find_trace(eip, esp, 0, 0, trace, 0);
|
|
trace_banner(ofp);
|
|
fprintf(ofp, "STACK TRACE FOR TASK: 0x%x", task);
|
|
|
|
if (KL_TYPEINFO()) {
|
|
fprintf(ofp, "(%s)\n\n",
|
|
(char *)K_PTR(tsp, "task_struct", "comm"));
|
|
} else {
|
|
fprintf(ofp, "(%s)\n\n",
|
|
(char *)K_PTR(tsp, "task_struct", "comm"));
|
|
}
|
|
#endif
|
|
print_trace(trace, flags, ofp);
|
|
}
|
|
if (!XEN_HYPER_MODE())
|
|
kl_free_block(tsp);
|
|
|
|
free_trace_rec(trace);
|
|
#ifdef REDHAT
|
|
if (KL_ERROR == KLE_PRINT_TRACE_ERROR) {
|
|
handle_trace_error(bt, nframes, ofp);
|
|
return(1);
|
|
}
|
|
#endif
|
|
return(0);
|
|
}
|
|
|
|
#ifdef REDHAT
|
|
/*
|
|
* Run find_trace() and check for any errors encountered.
|
|
*/
|
|
static int
|
|
verify_back_trace(struct bt_info *bt)
|
|
{
|
|
void *tsp;
|
|
kaddr_t saddr, eip, esp;
|
|
int errcnt;
|
|
trace_t *trace;
|
|
sframe_t *frmp;
|
|
|
|
errcnt = 0;
|
|
KL_ERROR = 0;
|
|
tsp = NULL;
|
|
|
|
if (!XEN_HYPER_MODE()) {
|
|
if (!(tsp = kl_alloc_block(TASK_STRUCT_SZ, K_TEMP)))
|
|
return FALSE;
|
|
|
|
if (kl_get_task_struct(bt->task, 2, tsp)) {
|
|
kl_free_block(tsp);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
trace = (trace_t *)alloc_trace_rec(C_TEMP);
|
|
if (!trace)
|
|
return FALSE;
|
|
|
|
saddr = kl_kernelstack(bt->stackbase);
|
|
|
|
eip = bt->instptr;
|
|
esp = bt->stkptr;
|
|
trace->bt = bt;
|
|
if (esp < KL_PAGE_OFFSET || eip < KL_PAGE_OFFSET)
|
|
return FALSE;
|
|
|
|
setup_trace_rec(saddr, 0, 0, trace);
|
|
if (KL_ERROR) {
|
|
free_trace_rec(trace);
|
|
return FALSE;
|
|
}
|
|
|
|
find_trace(eip, esp, 0, 0, trace, 0);
|
|
|
|
if ((frmp = trace->frame)) {
|
|
do {
|
|
if (frmp->error) {
|
|
/*
|
|
* We're guaranteed to run into an error when
|
|
* unwinding and IRQ stack, so bail out without
|
|
* reporting the error.
|
|
*/
|
|
if ((bt->flags & (BT_HARDIRQ|BT_SOFTIRQ)) &&
|
|
(bt->flags & (BT_LOOP_TRAP|BT_WRAP_TRAP)))
|
|
break;
|
|
|
|
errcnt++;
|
|
if (!(bt->flags & BT_SPECULATE) &&
|
|
!bt->frameptr)
|
|
bt->frameptr = frmp->fp;
|
|
}
|
|
if (BT_REFERENCE_CHECK(bt))
|
|
do_bt_reference_check(bt, frmp);
|
|
frmp = frmp->next;
|
|
} while (frmp != trace->frame);
|
|
}
|
|
|
|
if (!XEN_HYPER_MODE())
|
|
kl_free_block(tsp);
|
|
|
|
free_trace_rec(trace);
|
|
return (errcnt ? FALSE : TRUE);
|
|
}
|
|
|
|
/*
|
|
* Check a frame for a requested reference.
|
|
*/
|
|
static void
|
|
do_bt_reference_check(struct bt_info *bt, sframe_t *frmp)
|
|
{
|
|
int type;
|
|
struct syment *sp;
|
|
|
|
sp = frmp->prev && STREQ(frmp->funcname, "error_code") ?
|
|
x86_jmp_error_code((ulong)frmp->prev->pc) : NULL;
|
|
|
|
switch (bt->ref->cmdflags & (BT_REF_SYMBOL|BT_REF_HEXVAL))
|
|
{
|
|
case BT_REF_SYMBOL:
|
|
if (STREQ(kl_funcname(frmp->pc), bt->ref->str) ||
|
|
(sp && STREQ(sp->name, bt->ref->str)))
|
|
bt->ref->cmdflags |= BT_REF_FOUND;
|
|
break;
|
|
|
|
case BT_REF_HEXVAL:
|
|
if ((bt->ref->hexval == frmp->pc) ||
|
|
(sp && (bt->ref->hexval == sp->value)))
|
|
bt->ref->cmdflags |= BT_REF_FOUND;
|
|
if (frmp->flag & EX_FRAME) {
|
|
type = eframe_type(frmp->asp);
|
|
x86_dump_eframe_common(bt, (ulong *)frmp->asp,
|
|
(type == KERNEL_EFRAME));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This function is a repository for "known" find_trace() failures that
|
|
* can be "fixed" on the fly.
|
|
*
|
|
* Currently the routine only deals with BT_LOOP_TRAP/BT_WRAP_TRAP errors
|
|
* where get_framesize() leaves the bp in an invalid location, where
|
|
* where schedule() coming from schedule_timeout() is interrupted by a
|
|
* false return address in between, those where the cpu_idle() trail
|
|
* cannot be followed, and where the functions called by kernel_thread()
|
|
* can't find their way back to kernel_thread(). As new fixable trace
|
|
* instances are discovered, add them in.
|
|
*
|
|
* NOTE: the schedule() BT_LOOP_TRAP may have been subsequently fixed
|
|
* by the get_framesize() adjustment for schedule(), but it's worth
|
|
* keeping it around if a new schedule framesize anomoly pops up in
|
|
* the future.
|
|
*/
|
|
static int
|
|
recoverable(struct bt_info *bt, FILE *ofp)
|
|
{
|
|
ulong esp, eip;
|
|
sframe_t sframe;
|
|
struct stack_hook *hp;
|
|
struct bt_info btloc;
|
|
ulong kernel_thread;
|
|
int calls_schedule;
|
|
|
|
if (!(kt->flags & NO_RA_SEEK)) {
|
|
BCOPY(bt, &btloc, sizeof(struct bt_info));
|
|
btloc.flags &= ~(ulonglong)BT_ERROR_MASK;
|
|
btloc.flags |= BT_SPECULATE;
|
|
if (verify_back_trace(&btloc)) {
|
|
bt->flags &= ~(ulonglong)BT_ERROR_MASK;
|
|
bt->flags |= BT_SPECULATE;
|
|
if (CRASHDEBUG(1) || bt->debug)
|
|
error(INFO,
|
|
"recovered back trace with RA seek\n");
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (!gather_text_list(bt) ||
|
|
!STREQ(kl_funcname(bt->instptr), "schedule"))
|
|
return FALSE;
|
|
|
|
if (!is_idle_thread(bt->task) && !(bt->flags & BT_ERROR_MASK))
|
|
return FALSE;
|
|
|
|
esp = eip = 0;
|
|
calls_schedule = FALSE;
|
|
kernel_thread = 0;
|
|
|
|
for (hp = bt->textlist; hp->esp; hp++) {
|
|
if (STREQ(kl_funcname(hp->eip), "kernel_thread")) {
|
|
kernel_thread = hp->eip;
|
|
continue;
|
|
}
|
|
|
|
if (!calls_schedule &&
|
|
STREQ(x86_function_called_by(hp->eip-5), "schedule"))
|
|
calls_schedule = TRUE;
|
|
|
|
if (STREQ(kl_funcname(hp->eip), "schedule_timeout")) {
|
|
esp = hp->esp;
|
|
eip = hp->eip;
|
|
break;
|
|
}
|
|
|
|
if (STREQ(kl_funcname(hp->eip), "cpu_idle") &&
|
|
(bt->tc->pid == 0)) {
|
|
esp = hp->esp;
|
|
eip = hp->eip;
|
|
bt->flags |= BT_CPU_IDLE;
|
|
for ( ; BT_REFERENCE_CHECK(bt) && hp->esp; hp++) {
|
|
if (STREQ(kl_funcname(hp->eip), "rest_init") ||
|
|
STREQ(kl_funcname(hp->eip),
|
|
"start_kernel")) {
|
|
BZERO(&sframe, sizeof(sframe_t));
|
|
sframe.pc = hp->eip;
|
|
do_bt_reference_check(bt, &sframe);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
BCOPY(bt, &btloc, sizeof(struct bt_info));
|
|
btloc.flags &= ~(ulonglong)BT_ERROR_MASK;
|
|
|
|
if (esp && eip) {
|
|
btloc.instptr = eip;
|
|
btloc.stkptr = esp;
|
|
if (verify_back_trace(&btloc)) {
|
|
if (CRASHDEBUG(1) || bt->debug)
|
|
error(INFO, "recovered stack trace:\n");
|
|
if (!BT_REFERENCE_CHECK(bt))
|
|
fprintf(ofp, " #0 [%08lx] %s at %lx\n",
|
|
bt->stkptr,
|
|
kl_funcname(bt->instptr),
|
|
bt->instptr);
|
|
bt->instptr = eip;
|
|
bt->stkptr = esp;
|
|
bt->flags &= ~(ulonglong)BT_ERROR_MASK;
|
|
bt->flags |= BT_BUMP_FRAME_LEVEL;
|
|
FREEBUF(bt->textlist);
|
|
return TRUE;
|
|
}
|
|
|
|
if (bt->flags & BT_CPU_IDLE) {
|
|
if (CRASHDEBUG(1) || bt->debug)
|
|
error(INFO, "recovered stack trace:\n");
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (kernel_thread && calls_schedule && is_kernel_thread(bt->tc->task)) {
|
|
if (CRASHDEBUG(1) || bt->debug)
|
|
error(INFO, "recovered stack trace:\n");
|
|
if (BT_REFERENCE_CHECK(bt)) {
|
|
BZERO(&sframe, sizeof(sframe_t));
|
|
sframe.pc = kernel_thread;
|
|
do_bt_reference_check(bt, &sframe);
|
|
}
|
|
bt->flags |= BT_KERNEL_THREAD;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* If a trace is recoverable from this point finish it here. Otherwise,
|
|
* if a back trace fails and is unrecoverable, dump the text symbols along
|
|
* with any possible exception frames that can be found on the stack.
|
|
*/
|
|
static void
|
|
handle_trace_error(struct bt_info *bt, int nframes, FILE *ofp)
|
|
{
|
|
int cnt, level;
|
|
struct stack_hook *hp;
|
|
|
|
if (CRASHDEBUG(2) || (bt->debug >= 2)) {
|
|
for (hp = bt->textlist; hp->esp; hp++) {
|
|
char *func;
|
|
if ((func = x86_function_called_by(hp->eip-5)))
|
|
fprintf(ofp, "%lx %s calls %s\n", hp->eip,
|
|
kl_funcname(hp->eip), func);
|
|
}
|
|
}
|
|
|
|
if (bt->flags & BT_CPU_IDLE) {
|
|
for (hp = bt->textlist, level = 2; hp->esp; hp++) {
|
|
if (STREQ(kl_funcname(hp->eip), "rest_init") ||
|
|
STREQ(kl_funcname(hp->eip), "start_kernel"))
|
|
print_stack_entry(bt, level++, hp->esp,
|
|
hp->eip, kl_funcname(hp->eip),
|
|
NULL, ofp);
|
|
}
|
|
FREEBUF(bt->textlist);
|
|
return;
|
|
}
|
|
|
|
if (bt->flags & BT_KERNEL_THREAD) {
|
|
for (hp = bt->textlist; hp->esp; hp++) {
|
|
if (STREQ(kl_funcname(hp->eip), "kernel_thread"))
|
|
print_stack_entry(bt, nframes-1, hp->esp,
|
|
hp->eip, "kernel_thread", NULL, ofp);
|
|
}
|
|
FREEBUF(bt->textlist);
|
|
return;
|
|
}
|
|
|
|
error(INFO, "text symbols on stack:\n");
|
|
bt->flags |= BT_TEXT_SYMBOLS_PRINT|BT_ERROR_MASK;
|
|
back_trace(bt);
|
|
|
|
if (!XEN_HYPER_MODE()) {
|
|
bt->flags = BT_EFRAME_COUNT;
|
|
if ((cnt = machdep->eframe_search(bt))) {
|
|
error(INFO, "possible exception frame%s:\n",
|
|
cnt > 1 ? "s" : "");
|
|
bt->flags &= ~(ulonglong)BT_EFRAME_COUNT;
|
|
machdep->eframe_search(bt);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Print a stack entry, and its line number if requested.
|
|
*/
|
|
static void
|
|
print_stack_entry(struct bt_info *bt, int level, ulong esp, ulong eip,
|
|
char *funcname, sframe_t *frmp, FILE *ofp)
|
|
{
|
|
char buf1[BUFSIZE];
|
|
char buf2[BUFSIZE];
|
|
struct syment *sp;
|
|
struct load_module *lm;
|
|
|
|
if (frmp && frmp->prev && STREQ(frmp->funcname, "error_code") &&
|
|
(sp = x86_jmp_error_code((ulong)frmp->prev->pc)))
|
|
sprintf(buf1, " (via %s)", sp->name);
|
|
else if (frmp && (STREQ(frmp->funcname, "stext_lock") ||
|
|
STRNEQ(frmp->funcname, ".text.lock")) &&
|
|
(sp = x86_text_lock_jmp(eip, NULL)))
|
|
sprintf(buf1, " (via %s)", sp->name);
|
|
else
|
|
buf1[0] = NULLCHAR;
|
|
|
|
if ((sp = eframe_label(funcname, eip)))
|
|
funcname = sp->name;
|
|
|
|
fprintf(ofp, "%s#%d [%8lx] %s%s at %lx",
|
|
level < 10 ? " " : "", level, esp,
|
|
funcname_display(funcname, eip, bt, buf2),
|
|
strlen(buf1) ? buf1 : "", eip);
|
|
if (module_symbol(eip, NULL, &lm, NULL, 0))
|
|
fprintf(ofp, " [%s]", lm->mod_name);
|
|
fprintf(ofp, "\n");
|
|
|
|
if (bt->flags & BT_LINE_NUMBERS) {
|
|
get_line_number(eip, buf1, FALSE);
|
|
if (strlen(buf1))
|
|
fprintf(ofp, " %s\n", buf1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* The new process accounting stuff installs a label between system_call and
|
|
* ret_from_sys_call, confusing the code that recognizes exception frame
|
|
* symbols. This function has been put in place to catch that anomoly, as
|
|
* well as serving as a template for any future labels that get placed in the
|
|
* kernel entry point code. It returns the syment of the "real" kernel entry
|
|
* point.
|
|
*/
|
|
|
|
#define EFRAME_LABELS 10
|
|
static struct eframe_labels {
|
|
int init;
|
|
ulong syscall_labels[EFRAME_LABELS];
|
|
struct syment *syscall;
|
|
struct syment *syscall_end;
|
|
ulong tracesys_labels[EFRAME_LABELS];
|
|
struct syment *tracesys;
|
|
struct syment *tracesys_exit;
|
|
ulong sysenter_labels[EFRAME_LABELS];
|
|
struct syment *sysenter;
|
|
struct syment *sysenter_end;
|
|
} eframe_labels = { 0 };
|
|
|
|
static struct syment *
|
|
eframe_label(char *funcname, ulong eip)
|
|
{
|
|
int i;
|
|
struct eframe_labels *efp;
|
|
struct syment *sp;
|
|
|
|
if (XEN_HYPER_MODE())
|
|
return NULL; /* ODA: need support ? */
|
|
|
|
efp = &eframe_labels;
|
|
|
|
if (!efp->init) {
|
|
if (!(efp->syscall = symbol_search("system_call"))) {
|
|
if (CRASHDEBUG(1))
|
|
error(WARNING,
|
|
"\"system_call\" symbol does not exist\n");
|
|
}
|
|
if ((sp = symbol_search("ret_from_sys_call")))
|
|
efp->syscall_end = sp;
|
|
else if ((sp = symbol_search("syscall_badsys")))
|
|
efp->syscall_end = sp;
|
|
else {
|
|
if (CRASHDEBUG(1))
|
|
error(WARNING,
|
|
"neither \"ret_from_sys_call\" nor \"syscall_badsys\" symbols exist\n");
|
|
}
|
|
|
|
if (efp->syscall) {
|
|
efp->tracesys = symbol_search("tracesys");
|
|
efp->tracesys_exit = symbol_search("tracesys_exit");
|
|
}
|
|
|
|
if ((efp->sysenter = symbol_search("sysenter_entry")) ||
|
|
(efp->sysenter = symbol_search("ia32_sysenter_target"))) {
|
|
if ((sp = symbol_search("sysexit_ret_end_marker")))
|
|
efp->sysenter_end = sp;
|
|
else if (THIS_KERNEL_VERSION >= LINUX(2,6,32)) {
|
|
if ((sp = symbol_search("sysexit_audit")) ||
|
|
(sp = symbol_search("sysenter_exit")))
|
|
efp->sysenter_end =
|
|
next_symbol(NULL, sp);
|
|
else error(WARNING,
|
|
"cannot determine end of %s function\n",
|
|
efp->sysenter->name);
|
|
} else if ((sp = symbol_search("system_call")))
|
|
efp->sysenter_end = sp;
|
|
else
|
|
error(WARNING,
|
|
"neither \"sysexit_ret_end_marker\" nor \"system_call\" symbols exist\n");
|
|
}
|
|
|
|
efp->init = TRUE;
|
|
}
|
|
|
|
/*
|
|
* First search for the currently-known system_call labels.
|
|
*/
|
|
|
|
for (i = 0; (i < EFRAME_LABELS) && efp->syscall_labels[i]; i++) {
|
|
if (efp->syscall_labels[i] == eip)
|
|
return efp->syscall;
|
|
}
|
|
|
|
for (i = 0; (i < EFRAME_LABELS) && efp->tracesys_labels[i]; i++) {
|
|
if (efp->tracesys_labels[i] == eip)
|
|
return efp->syscall;
|
|
}
|
|
|
|
for (i = 0; (i < EFRAME_LABELS) && efp->sysenter_labels[i]; i++) {
|
|
if (efp->sysenter_labels[i] == eip)
|
|
return efp->sysenter;
|
|
}
|
|
|
|
/*
|
|
* If the eip fits in any of the label arrays, try to store it,
|
|
* but always return the real function it's referencing.
|
|
*/
|
|
if (efp->syscall && efp->syscall_end) {
|
|
if (((eip >= efp->syscall->value) &&
|
|
(eip < efp->syscall_end->value))) {
|
|
for (i = 0; i < EFRAME_LABELS; i++)
|
|
if (!efp->syscall_labels[i])
|
|
efp->syscall_labels[i] = eip;
|
|
return efp->syscall;
|
|
}
|
|
}
|
|
|
|
if (efp->tracesys && efp->tracesys_exit) {
|
|
if (((eip >= efp->tracesys->value) &&
|
|
(eip < efp->tracesys_exit->value))) {
|
|
for (i = 0; i < EFRAME_LABELS; i++)
|
|
if (!efp->tracesys_labels[i])
|
|
efp->tracesys_labels[i] = eip;
|
|
return efp->syscall;
|
|
}
|
|
}
|
|
|
|
if (efp->sysenter && efp->sysenter_end) {
|
|
if (((eip >= efp->sysenter->value) &&
|
|
(eip < efp->sysenter_end->value))) {
|
|
for (i = 0; i < EFRAME_LABELS; i++)
|
|
if (!efp->sysenter_labels[i])
|
|
efp->sysenter_labels[i] = eip;
|
|
return efp->sysenter;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* If it makes sense to display a different function/label name
|
|
* in a stack entry, it can be done here. Unlike eframe_label(),
|
|
* this routine won't cause the passed-in function name pointer
|
|
* to be changed -- this is strictly for display purposes only.
|
|
*/
|
|
static char *
|
|
funcname_display(char *funcname, ulong eip, struct bt_info *bt, char *buf)
|
|
{
|
|
struct syment *sp;
|
|
ulong offset;
|
|
|
|
if (bt->flags & BT_SYMBOL_OFFSET) {
|
|
sp = value_search(eip, &offset);
|
|
if (sp && offset)
|
|
return value_to_symstr(eip, buf, bt->radix);
|
|
}
|
|
|
|
if (STREQ(funcname, "nmi_stack_correct") &&
|
|
(sp = symbol_search("nmi")))
|
|
return sp->name;
|
|
|
|
return funcname;
|
|
}
|
|
|
|
|
|
/*
|
|
* Cache 2k starting from the passed-in text address. This sits on top
|
|
* of the instrbuf 256-byte cache, but we don't want to extend its size
|
|
* because we can run off the end of a module segment -- if this routine
|
|
* does so, it's benign. Tests of "foreach bt" result in more than an
|
|
* 80% cache-hit rate.
|
|
*/
|
|
#define TEXT_BLOCK_SIZE (2048)
|
|
|
|
static void
|
|
fill_instr_cache(kaddr_t pc, char *buf)
|
|
{
|
|
static kaddr_t last_block = 0;
|
|
static char block[TEXT_BLOCK_SIZE];
|
|
ulong offset;
|
|
|
|
if ((pc >= last_block) && ((pc+256) < (last_block+TEXT_BLOCK_SIZE))) {
|
|
offset = pc - last_block;
|
|
} else {
|
|
if (readmem(pc, KVADDR, block, TEXT_BLOCK_SIZE,
|
|
"fill_instr_cache", RETURN_ON_ERROR|QUIET)) {
|
|
last_block = pc;
|
|
offset = 0;
|
|
} else {
|
|
GET_BLOCK(pc, 256, block);
|
|
last_block = 0;
|
|
offset = 0;
|
|
}
|
|
}
|
|
|
|
BCOPY(&block[offset], buf, 256);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* print_traces()
|
|
*
|
|
* Output a list of all valid code addresses contained in a stack
|
|
* along with their function name and stack location.
|
|
*/
|
|
int
|
|
#ifdef REDHAT
|
|
print_traces(struct bt_info *bt, int level, int flags, FILE *ofp)
|
|
#else
|
|
print_traces(kaddr_t saddr, int level, int flags, FILE *ofp)
|
|
#endif
|
|
{
|
|
int nfrms;
|
|
char *fname, *cfname;
|
|
uaddr_t *wordp, *stackp;
|
|
trace_t *trace;
|
|
kaddr_t addr, isp, caddr, sbase;
|
|
#ifdef REDHAT
|
|
kaddr_t saddr = bt->stkptr;
|
|
#endif
|
|
|
|
stackp = (uaddr_t*)kl_alloc_block(STACK_SIZE, K_TEMP);
|
|
sbase = saddr - STACK_SIZE;
|
|
GET_BLOCK(sbase, STACK_SIZE, stackp);
|
|
if (KL_ERROR) {
|
|
kl_free_block(stackp);
|
|
return(1);
|
|
}
|
|
|
|
if (!(trace = (trace_t *)alloc_trace_rec(K_TEMP))) {
|
|
#ifdef REDHAT
|
|
error(INFO, "Could not alloc trace rec!\n");
|
|
#else
|
|
fprintf(KL_ERRORFP, "Could not alloc trace rec!\n");
|
|
#endif
|
|
kl_free_block(stackp);
|
|
return(1);
|
|
}
|
|
setup_trace_rec(saddr, 0, 0, trace);
|
|
#ifdef REDHAT
|
|
trace->bt = bt;
|
|
#endif
|
|
|
|
wordp = stackp;
|
|
while(wordp < (stackp + (STACK_SIZE / 4))) {
|
|
if ((addr = (kaddr_t)(*(uaddr_t*)wordp))) {
|
|
|
|
/* check to see if this is a valid code address
|
|
*/
|
|
if ((fname = kl_funcname(addr))) {
|
|
/* Now use the instruction to back up and
|
|
* see if this RA was saved after a call.
|
|
* If it was, then try to determine what
|
|
* function was called. At the very least,
|
|
* only print out info for true return
|
|
* addresses (coming right after a call
|
|
* instruction -- even if we can't tell
|
|
* what function was called).
|
|
*/
|
|
isp = sbase +
|
|
(((uaddr_t)wordp) - ((uaddr_t)stackp));
|
|
|
|
cfname = (char *)NULL;
|
|
caddr = 0;
|
|
if (get_jmp_instr(addr, isp,
|
|
&caddr, fname, &cfname)) {
|
|
wordp++;
|
|
continue;
|
|
}
|
|
|
|
/* We have found a valid jump address. Now,
|
|
* try and get a backtrace.
|
|
*/
|
|
nfrms = find_trace(addr, isp, 0, 0, trace, 0);
|
|
if (nfrms) {
|
|
if ((nfrms >= level) &&
|
|
(!trace->frame->prev->error ||
|
|
(flags & C_ALL))) {
|
|
fprintf(ofp, "\nPC=");
|
|
print_kaddr(addr, ofp, 0);
|
|
fprintf(ofp, " SP=");
|
|
print_kaddr(isp, ofp, 0);
|
|
fprintf(ofp, " SADDR=");
|
|
print_kaddr(saddr, ofp, 0);
|
|
fprintf(ofp, "\n");
|
|
trace_banner(ofp);
|
|
print_trace(trace, flags, ofp);
|
|
trace_banner(ofp);
|
|
}
|
|
free_sframes(trace);
|
|
}
|
|
}
|
|
wordp++;
|
|
} else {
|
|
wordp++;
|
|
}
|
|
}
|
|
kl_free_block(stackp);
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* do_list()
|
|
*
|
|
* Output a list of all valid code addresses contained in a stack
|
|
* along with their function name and stack location.
|
|
*/
|
|
int
|
|
#ifdef REDHAT
|
|
do_text_list(kaddr_t saddr, int size, FILE *ofp)
|
|
#else
|
|
do_list(kaddr_t saddr, int size, FILE *ofp)
|
|
#endif
|
|
{
|
|
char *fname, *cfname;
|
|
uaddr_t *wordp, *stackp;
|
|
kaddr_t addr, isp, caddr, sbase;
|
|
|
|
stackp = (uaddr_t*)kl_alloc_block(size, K_TEMP);
|
|
sbase = saddr - size;
|
|
GET_BLOCK(sbase, size, stackp);
|
|
if (KL_ERROR) {
|
|
kl_free_block(stackp);
|
|
return(1);
|
|
}
|
|
|
|
wordp = stackp;
|
|
while(wordp < (stackp + (size / 4))) {
|
|
if ((addr = (kaddr_t)(*(uaddr_t*)wordp))) {
|
|
|
|
/* check to see if this is a valid code address
|
|
*/
|
|
if ((fname = kl_funcname(addr))) {
|
|
/* Now use the instruction to back up and
|
|
* see if this RA was saved after a call.
|
|
* If it was, then try to determine what
|
|
* function was called. At the very least,
|
|
* only print out info for true return
|
|
* addresses (coming right after a call
|
|
* instruction -- even if we can't tell
|
|
* what function was called).
|
|
*/
|
|
isp = sbase +
|
|
(((uaddr_t)wordp) - ((uaddr_t)stackp));
|
|
|
|
cfname = (char *)NULL;
|
|
caddr = 0;
|
|
if (get_jmp_instr(addr, isp,
|
|
&caddr, fname, &cfname)) {
|
|
wordp++;
|
|
continue;
|
|
}
|
|
fprintf(ofp, "0x%x -- 0x%x (%s)",
|
|
isp, addr, fname);
|
|
if (cfname) {
|
|
fprintf(ofp, " --> 0x%x (%s)\n",
|
|
caddr, cfname);
|
|
} else {
|
|
fprintf(ofp, "\n");
|
|
}
|
|
}
|
|
wordp++;
|
|
} else {
|
|
wordp++;
|
|
}
|
|
}
|
|
kl_free_block(stackp);
|
|
return(0);
|
|
}
|
|
|
|
#ifndef REDHAT
|
|
/*
|
|
* add_frame()
|
|
*/
|
|
int
|
|
add_frame(trace_t *trace, kaddr_t fp, kaddr_t ra)
|
|
{
|
|
sframe_t *cf, *sf;
|
|
|
|
/* Check to make sure that sp is from the stack in the trace
|
|
* record.
|
|
*
|
|
* XXX -- todo
|
|
*/
|
|
sf = (sframe_t *)alloc_sframe(trace, C_PERM);
|
|
sf->fp = fp;
|
|
sf->ra = ra;
|
|
if ((cf = trace->frame)) {
|
|
do {
|
|
if (cf->fp && (sf->fp < cf->fp)) {
|
|
if (cf->next == cf) {
|
|
cf->prev = sf;
|
|
sf->next = cf;
|
|
cf->next = sf;
|
|
sf->prev = cf;
|
|
trace->frame = sf;
|
|
} else {
|
|
cf->prev->next = sf;
|
|
sf->prev = cf->prev;
|
|
cf->prev = sf;
|
|
sf->next = cf;
|
|
}
|
|
return(0);
|
|
}
|
|
cf = cf->next;
|
|
} while (cf != trace->frame);
|
|
cf = 0;
|
|
}
|
|
if (!cf) {
|
|
kl_enqueue((element_t **)&trace->frame, (element_t *)sf);
|
|
}
|
|
return(1);
|
|
}
|
|
|
|
/*
|
|
* finish_trace()
|
|
*/
|
|
void
|
|
finish_trace(trace_t *trace)
|
|
{
|
|
int level = 0, curstkidx = 0;
|
|
uaddr_t *sbp;
|
|
kaddr_t sbase, saddr;
|
|
sframe_t *sf;
|
|
|
|
sbp = trace->stack[curstkidx].ptr;
|
|
sbase = trace->stack[curstkidx].addr;
|
|
saddr = sbase + trace->stack[curstkidx].size;
|
|
|
|
if ((sf = trace->frame)) {
|
|
do {
|
|
if (!sf->pc) {
|
|
if (sf != trace->frame) {
|
|
sf->sp = sf->prev->fp + 4;
|
|
sf->pc = get_call_pc(sf->prev->ra);
|
|
}
|
|
if (!sf->pc) {
|
|
sf = sf->next;
|
|
continue;
|
|
}
|
|
}
|
|
sf->level = level++;
|
|
sf->frame_size = sf->fp - sf->sp + 4;
|
|
sf->funcname = kl_funcname(sf->pc);
|
|
sf->asp = (uaddr_t*)((uaddr_t)sbp +
|
|
(STACK_SIZE - (saddr - sf->sp)));
|
|
sf = sf->next;
|
|
} while (sf != trace->frame);
|
|
|
|
if (level > 0) {
|
|
sf = (sframe_t *)alloc_sframe(trace, C_PERM);
|
|
sf->level = level;
|
|
sf->sp = trace->frame->prev->fp + 4;
|
|
sf->pc = get_call_pc(trace->frame->prev->ra);
|
|
sf->funcname = kl_funcname(sf->pc);
|
|
if (sf->funcname &&
|
|
strstr(sf->funcname, "kernel_thread")) {
|
|
sf->ra = 0;
|
|
sf->fp = saddr - 4;
|
|
sf->asp = (uaddr_t*)((uaddr_t)sbp +
|
|
(STACK_SIZE - 12));
|
|
} else {
|
|
sf->fp = saddr - 20;
|
|
kl_get_kaddr(sf->fp, &sf->ra);
|
|
sf->asp = (uaddr_t*)((uaddr_t)sbp +
|
|
(STACK_SIZE - (saddr - sf->sp)));
|
|
}
|
|
sf->frame_size = sf->fp - sf->sp + 4;
|
|
kl_enqueue((element_t **)&trace->frame,
|
|
(element_t *)sf);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* dumptask_trace()
|
|
*/
|
|
int
|
|
dumptask_trace(
|
|
kaddr_t curtask,
|
|
dump_header_asm_t *dha,
|
|
int flags,
|
|
FILE *ofp)
|
|
{
|
|
kaddr_t eip, esp, saddr;
|
|
void *tsp;
|
|
trace_t *trace;
|
|
int i;
|
|
|
|
for (i = 0; i < dha->dha_smp_num_cpus; i++) {
|
|
if (curtask == (kaddr_t)dha->dha_smp_current_task[i]) {
|
|
eip = dha->dha_smp_regs[i].eip;
|
|
esp = dha->dha_smp_regs[i].esp;
|
|
break;
|
|
}
|
|
}
|
|
|
|
tsp = kl_alloc_block(TASK_STRUCT_SZ, K_TEMP);
|
|
if (!tsp) {
|
|
return(1);
|
|
}
|
|
if (kl_get_task_struct(curtask, 2, tsp)) {
|
|
kl_free_block(tsp);
|
|
return(1);
|
|
}
|
|
if (!(trace = alloc_trace_rec(K_TEMP))) {
|
|
fprintf(KL_ERRORFP, "Could not alloc trace rec!\n");
|
|
} else {
|
|
saddr = kl_kernelstack(curtask);
|
|
setup_trace_rec(saddr, 0, 0, trace);
|
|
find_trace(eip, esp, 0, 0, trace, 0);
|
|
trace_banner(ofp);
|
|
fprintf(ofp, "STACK TRACE FOR TASK: 0x%"FMTPTR"x (%s)\n\n",
|
|
curtask, (char*)K_PTR(tsp, "task_struct", "comm"));
|
|
print_trace(trace, flags, ofp);
|
|
trace_banner(ofp);
|
|
free_trace_rec(trace);
|
|
}
|
|
return(0);
|
|
}
|
|
#endif /* !REDHAT */
|
|
|
|
|
|
/*
|
|
* lkcdutils-4.1/lcrash/arch/i386/lib/dis.c
|
|
*/
|
|
|
|
/*
|
|
* Copyright 1999 Silicon Graphics, Inc. All rights reserved.
|
|
*/
|
|
#ifndef REDHAT
|
|
#include <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, ®_op);
|
|
mod = irp->modrm >> 6;
|
|
switch(mod_rm) {
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
case 5:
|
|
case 7:
|
|
reg = get_modrm_reg16(mod_rm, opdata, irp);
|
|
irp->operand[opnum].op_reg = reg;
|
|
irp->operand[opnum].op_type = (O_REG|O_BASE);
|
|
break;
|
|
|
|
case 6:
|
|
/* 16-bit displacement */
|
|
irp->operand[opnum].op_type = O_DISP;
|
|
irp->operand[opnum].op_disp = *(uint16_t*)codeptr;
|
|
codeptr += 2;
|
|
break;
|
|
case 8:
|
|
/* disp8[BX+SI] */
|
|
irp->operand[opnum].op_type = (O_REG|O_DISP);
|
|
irp->operand[opnum].op_reg = R_BX_SI;
|
|
irp->operand[opnum].op_disp = *(signed char*)codeptr;
|
|
codeptr++;
|
|
break;
|
|
case 9:
|
|
/* disp8[BX+DI] */
|
|
irp->operand[opnum].op_type = (O_REG|O_DISP);
|
|
irp->operand[opnum].op_reg = R_BX_DI;
|
|
irp->operand[opnum].op_disp = *(signed char*)codeptr;
|
|
codeptr++;
|
|
break;
|
|
case 10:
|
|
/* disp8[BP+SI] */
|
|
irp->operand[opnum].op_type = (O_REG|O_DISP);
|
|
irp->operand[opnum].op_reg = R_BP_SI;
|
|
irp->operand[opnum].op_disp = *(signed char*)codeptr;
|
|
codeptr++;
|
|
break;
|
|
|
|
case 11:
|
|
/* disp8[BP+DI] */
|
|
irp->operand[opnum].op_type = (O_REG|O_DISP);
|
|
irp->operand[opnum].op_reg = R_BP_DI;
|
|
irp->operand[opnum].op_disp = *(signed char*)codeptr;
|
|
codeptr++;
|
|
break;
|
|
|
|
case 12:
|
|
/* disp8[SI] */
|
|
irp->operand[opnum].op_type = (O_REG|O_DISP);
|
|
irp->operand[opnum].op_reg = R_SI;
|
|
irp->operand[opnum].op_disp = *(signed char*)codeptr;
|
|
codeptr++;
|
|
break;
|
|
|
|
case 13:
|
|
/* disp8[DI] */
|
|
irp->operand[opnum].op_type = (O_REG|O_DISP);
|
|
irp->operand[opnum].op_reg = R_DI;
|
|
irp->operand[opnum].op_disp = *(signed char*)codeptr;
|
|
codeptr++;
|
|
break;
|
|
|
|
case 14:
|
|
/* disp8[BP] */
|
|
irp->operand[opnum].op_type = (O_REG|O_DISP);
|
|
irp->operand[opnum].op_reg = R_BP;
|
|
irp->operand[opnum].op_disp = *(signed char*)codeptr;
|
|
codeptr++;
|
|
break;
|
|
|
|
case 15:
|
|
/* disp8[BX] */
|
|
irp->operand[opnum].op_type = (O_REG|O_DISP);
|
|
irp->operand[opnum].op_reg = R_BX;
|
|
irp->operand[opnum].op_disp = *(signed char*)codeptr;
|
|
codeptr++;
|
|
break;
|
|
|
|
case 16:
|
|
/* disp16[BX+SI] */
|
|
irp->operand[opnum].op_type = (O_REG|O_DISP);
|
|
irp->operand[opnum].op_reg = R_BX_SI;
|
|
irp->operand[opnum].op_disp = *(short*)codeptr;
|
|
codeptr += 2;
|
|
break;
|
|
|
|
case 17:
|
|
/* disp16[BX+DI] */
|
|
irp->operand[opnum].op_type = (O_REG|O_DISP);
|
|
irp->operand[opnum].op_reg = R_BX_DI;
|
|
irp->operand[opnum].op_disp = *(short*)codeptr;
|
|
codeptr += 2;
|
|
break;
|
|
|
|
case 18:
|
|
/* disp16[BP+SI] */
|
|
irp->operand[opnum].op_type = (O_REG|O_DISP);
|
|
irp->operand[opnum].op_reg = R_BP_SI;
|
|
irp->operand[opnum].op_disp = *(short*)codeptr;
|
|
codeptr += 2;
|
|
break;
|
|
|
|
case 19:
|
|
/* disp16[BP+DI] */
|
|
irp->operand[opnum].op_type = (O_REG|O_DISP);
|
|
irp->operand[opnum].op_reg = R_BP_DI;
|
|
irp->operand[opnum].op_disp = *(short*)codeptr;
|
|
codeptr += 2;
|
|
break;
|
|
|
|
case 20:
|
|
/* disp16[SI] */
|
|
irp->operand[opnum].op_type = (O_REG|O_DISP);
|
|
irp->operand[opnum].op_reg = R_SI;
|
|
irp->operand[opnum].op_disp = *(short*)codeptr;
|
|
codeptr += 2;
|
|
break;
|
|
|
|
case 21:
|
|
/* disp16[DI] */
|
|
irp->operand[opnum].op_type = (O_REG|O_DISP);
|
|
irp->operand[opnum].op_reg = R_DI;
|
|
irp->operand[opnum].op_disp = *(short*)codeptr;
|
|
codeptr += 2;
|
|
break;
|
|
|
|
case 22:
|
|
/* disp16[BP] */
|
|
irp->operand[opnum].op_type = (O_REG|O_DISP);
|
|
irp->operand[opnum].op_reg = R_BP;
|
|
irp->operand[opnum].op_disp = *(short*)codeptr;
|
|
codeptr += 2;
|
|
break;
|
|
|
|
case 23:
|
|
/* disp16[BX] */
|
|
irp->operand[opnum].op_type = (O_REG|O_DISP);
|
|
irp->operand[opnum].op_reg = R_BX;
|
|
irp->operand[opnum].op_disp = *(short*)codeptr;
|
|
codeptr += 2;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
get_modrm_data32(int opnum, int opdata, instr_rec_t *irp)
|
|
{
|
|
int mod ATTRIBUTE_UNUSED;
|
|
int reg, mod_rm, reg_op;
|
|
|
|
get_modrm_info(irp->modrm, &mod_rm, ®_op);
|
|
mod = irp->modrm >> 6;
|
|
switch(mod_rm) {
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
case 6:
|
|
case 7:
|
|
reg = get_modrm_reg32(mod_rm, opdata, irp);
|
|
irp->operand[opnum].op_reg = reg;
|
|
irp->operand[opnum].op_type = (O_REG|O_BASE);
|
|
break;
|
|
|
|
case 5:
|
|
/* 32-bit displacement */
|
|
irp->operand[opnum].op_type = O_DISP;
|
|
irp->operand[opnum].op_disp = *(kaddr_t*)codeptr;
|
|
codeptr += 4;
|
|
break;
|
|
case 8:
|
|
/* disp8[EAX] */
|
|
irp->operand[opnum].op_type = (O_REG|O_DISP);
|
|
irp->operand[opnum].op_reg = R_eAX;
|
|
irp->operand[opnum].op_disp = *(signed char*)codeptr;
|
|
codeptr++;
|
|
break;
|
|
case 9:
|
|
/* disp8[ECX] */
|
|
irp->operand[opnum].op_type = (O_REG|O_DISP);
|
|
irp->operand[opnum].op_reg = R_eCX;
|
|
irp->operand[opnum].op_disp = *(signed char*)codeptr;
|
|
codeptr++;
|
|
break;
|
|
case 10:
|
|
/* disp8[EDX] */
|
|
irp->operand[opnum].op_type = (O_REG|O_DISP);
|
|
irp->operand[opnum].op_reg = R_eDX;
|
|
irp->operand[opnum].op_disp = *(signed char*)codeptr;
|
|
codeptr++;
|
|
break;
|
|
|
|
case 11:
|
|
/* disp8[EBX] */
|
|
irp->operand[opnum].op_type = (O_REG|O_DISP);
|
|
irp->operand[opnum].op_reg = R_eBX;
|
|
irp->operand[opnum].op_disp = *(signed char*)codeptr;
|
|
codeptr++;
|
|
break;
|
|
|
|
case 13:
|
|
/* disp8[EBP] */
|
|
irp->operand[opnum].op_type = (O_REG|O_DISP);
|
|
irp->operand[opnum].op_reg = R_eBP;
|
|
irp->operand[opnum].op_disp = *(signed char*)codeptr;
|
|
codeptr++;
|
|
break;
|
|
|
|
case 14:
|
|
/* disp8[ESI] */
|
|
irp->operand[opnum].op_type = (O_REG|O_DISP);
|
|
irp->operand[opnum].op_reg = R_eSI;
|
|
irp->operand[opnum].op_disp = *(signed char*)codeptr;
|
|
codeptr++;
|
|
break;
|
|
case 15:
|
|
/* disp8[EDI] */
|
|
irp->operand[opnum].op_type = (O_REG|O_DISP);
|
|
irp->operand[opnum].op_reg = R_eDI;
|
|
irp->operand[opnum].op_disp = *(signed char*)codeptr;
|
|
codeptr++;
|
|
break;
|
|
|
|
case 16:
|
|
/* disp32[EAX] */
|
|
irp->operand[opnum].op_type = (O_REG|O_DISP);
|
|
irp->operand[opnum].op_reg = R_eAX;
|
|
irp->operand[opnum].op_disp = *(int*)codeptr;
|
|
codeptr += 4;
|
|
break;
|
|
|
|
case 17:
|
|
/* disp32[ECX] */
|
|
irp->operand[opnum].op_type = (O_REG|O_DISP);
|
|
irp->operand[opnum].op_reg = R_eCX;
|
|
irp->operand[opnum].op_disp = *(int*)codeptr;
|
|
codeptr += 4;
|
|
break;
|
|
|
|
case 18:
|
|
/* disp32[EDX] */
|
|
irp->operand[opnum].op_type = (O_REG|O_DISP);
|
|
irp->operand[opnum].op_reg = R_eDX;
|
|
irp->operand[opnum].op_disp = *(int*)codeptr;
|
|
codeptr += 4;
|
|
break;
|
|
|
|
case 19:
|
|
/* disp32[EBX] */
|
|
irp->operand[opnum].op_type = (O_REG|O_DISP);
|
|
irp->operand[opnum].op_reg = R_eBX;
|
|
irp->operand[opnum].op_disp = *(int*)codeptr;
|
|
codeptr += 4;
|
|
break;
|
|
|
|
case 4: /* [..][..] (SIB) */
|
|
case 12: /* disp8[..][..] (SIB) */
|
|
case 20: { /* disp32[..][..] (SIB) */
|
|
int rm ATTRIBUTE_UNUSED;
|
|
int s, i, b, mod, havebase;
|
|
|
|
s = (irp->sib >> 6) & 3;
|
|
i = (irp->sib >> 3) & 7;
|
|
b = irp->sib & 7;
|
|
mod = irp->modrm >> 6;
|
|
rm = irp->modrm & 7;
|
|
havebase = 1;
|
|
switch (mod) {
|
|
case 0:
|
|
if (b == 5) {
|
|
havebase = 0;
|
|
irp->operand[opnum].op_disp =
|
|
*(int*)codeptr;
|
|
irp->operand[opnum].op_type =
|
|
O_DISP;
|
|
codeptr += 4;
|
|
}
|
|
break;
|
|
case 1:
|
|
irp->operand[opnum].op_disp =
|
|
*(signed char*) codeptr;
|
|
codeptr++;
|
|
irp->operand[opnum].op_type = O_DISP;
|
|
break;
|
|
case 2:
|
|
irp->operand[opnum].op_disp =
|
|
*(int*)codeptr;
|
|
codeptr += 4;
|
|
irp->operand[opnum].op_type = O_DISP;
|
|
break;
|
|
}
|
|
if (havebase) {
|
|
irp->operand[opnum].op_base = b;
|
|
irp->operand[opnum].op_type |= O_BASE;
|
|
}
|
|
if (i != 4) {
|
|
irp->operand[opnum].op_index = i;
|
|
irp->operand[opnum].op_type |= O_INDEX;
|
|
}
|
|
if (s) {
|
|
irp->operand[opnum].op_scale = s;
|
|
irp->operand[opnum].op_type |= O_SCALE;
|
|
}
|
|
break;
|
|
}
|
|
case 21:
|
|
/* disp32[EBP] */
|
|
irp->operand[opnum].op_type = (O_REG|O_DISP);
|
|
irp->operand[opnum].op_reg = R_eBP;
|
|
irp->operand[opnum].op_disp = *(int*)codeptr;
|
|
codeptr += 4;
|
|
break;
|
|
case 22:
|
|
/* disp32[ESI] */
|
|
irp->operand[opnum].op_type = (O_REG|O_DISP);
|
|
irp->operand[opnum].op_reg = R_eSI;
|
|
irp->operand[opnum].op_disp = *(int*)codeptr;
|
|
codeptr += 4;
|
|
break;
|
|
case 23:
|
|
/* disp32[EDI] */
|
|
irp->operand[opnum].op_type = (O_REG|O_DISP);
|
|
irp->operand[opnum].op_reg = R_eDI;
|
|
irp->operand[opnum].op_disp = *(int*)codeptr;
|
|
codeptr += 4;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int
|
|
op_e(int opnum, int opdata, instr_rec_t *irp)
|
|
{
|
|
int reg, mod, mod_rm, reg_op;
|
|
|
|
get_modrm_info(irp->modrm, &mod_rm, ®_op);
|
|
mod = irp->modrm >> 6;
|
|
|
|
if (mod == 3) {
|
|
/* ((mod_rm >= 24) && (mod_rm <=31)) */
|
|
if (opdata == T_NONE) {
|
|
return(1);
|
|
}
|
|
if (irp->aflag) {
|
|
reg = get_modrm_reg32(mod_rm, opdata, irp);
|
|
} else {
|
|
reg = get_modrm_reg16(mod_rm, opdata, irp);
|
|
}
|
|
irp->operand[opnum].op_type = O_REG;
|
|
irp->operand[opnum].op_reg = reg;
|
|
if ((reg = R_BAD)) {
|
|
return(1);
|
|
} else {
|
|
return(0);
|
|
}
|
|
}
|
|
if (irp->aflag) {
|
|
get_modrm_data32(opnum, opdata, irp);
|
|
} else {
|
|
get_modrm_data16(opnum, opdata, irp);
|
|
}
|
|
if (seg_prefix(irp->prefixes)) {
|
|
irp->operand[opnum].op_type |= O_SEG;
|
|
irp->operand[opnum].op_seg = seg_prefix(irp->prefixes);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
static int
|
|
op_g(int opnum, int opdata, instr_rec_t *irp)
|
|
{
|
|
int reg, mod_rm, reg_op;
|
|
|
|
get_modrm_info(irp->modrm, &mod_rm, ®_op);
|
|
irp->operand[opnum].op_type = O_REG;
|
|
if ((reg_op < 0) || (reg_op >= 8)){
|
|
irp->operand[opnum].op_reg = R_BAD;
|
|
return(1);
|
|
}
|
|
switch(opdata) {
|
|
case T_b:
|
|
reg = reg_8[reg_op];
|
|
break;
|
|
case T_w:
|
|
reg = reg_16[reg_op];
|
|
break;
|
|
case T_d:
|
|
reg = reg_32[reg_op];
|
|
break;
|
|
case T_v:
|
|
if (irp->dflag) {
|
|
reg = reg_32[reg_op];
|
|
} else {
|
|
reg = reg_16[reg_op];
|
|
}
|
|
break;
|
|
default:
|
|
irp->operand[opnum].op_reg = R_BAD;
|
|
return(1);
|
|
}
|
|
irp->operand[opnum].op_reg = reg;
|
|
return(0);
|
|
}
|
|
|
|
static void
|
|
op_i(int opnum, int opdata, instr_rec_t *irp)
|
|
{
|
|
irp->operand[opnum].op_type = O_IMMEDIATE;
|
|
switch (opdata) {
|
|
case T_b:
|
|
irp->operand[opnum].op_addr = *(unsigned char*)codeptr;
|
|
codeptr++;
|
|
break;
|
|
case T_w:
|
|
irp->operand[opnum].op_addr = *(uint16_t*)codeptr;
|
|
codeptr += 2;
|
|
break;
|
|
case T_v:
|
|
if (irp->dflag) {
|
|
irp->operand[opnum].op_addr =
|
|
*(uint32_t*)codeptr;
|
|
codeptr += 4;
|
|
} else {
|
|
irp->operand[opnum].op_addr =
|
|
*(uint16_t*)codeptr;
|
|
codeptr += 2;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
op_s(int opnum, int opdata, instr_rec_t *irp)
|
|
{
|
|
int reg;
|
|
|
|
reg = (irp->modrm >> 3) & 7;
|
|
irp->operand[opnum].op_reg = reg_seg[reg];
|
|
irp->operand[opnum].op_type = O_REG;
|
|
}
|
|
|
|
static void
|
|
op_si(int opnum, int opdata, instr_rec_t *irp)
|
|
{
|
|
int val;
|
|
|
|
irp->operand[opnum].op_type = O_IMMEDIATE;
|
|
switch (opdata) {
|
|
case T_b:
|
|
val = *(signed char*)codeptr++;
|
|
irp->operand[opnum].op_addr = val;
|
|
break;
|
|
case T_v:
|
|
if (irp->dflag) {
|
|
irp->operand[opnum].op_addr = *(int*)codeptr;
|
|
codeptr += 4;
|
|
} else {
|
|
val = *(short*)codeptr;
|
|
irp->operand[opnum].op_addr = val;
|
|
codeptr += 2;
|
|
}
|
|
break;
|
|
case T_w:
|
|
val = *(short*)codeptr;
|
|
irp->operand[opnum].op_addr = val;
|
|
codeptr += 2;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
op_j(int opnum, int opdata, instr_rec_t *irp)
|
|
{
|
|
kaddr_t pc;
|
|
|
|
pc = instrbuf.addr + (instrbuf.ptr - instrbuf.buf);
|
|
pc += (codeptr - instrbuf.ptr);
|
|
switch (opdata) {
|
|
case T_b:
|
|
pc++;
|
|
pc += *(signed char *)codeptr++;
|
|
break;
|
|
case T_v:
|
|
if (irp->dflag) {
|
|
/* 32-bit */
|
|
pc += 4;
|
|
pc += *(int*)codeptr;
|
|
codeptr += 4;
|
|
} else {
|
|
/* 16-bit */
|
|
pc += 2;
|
|
pc += *(short*)codeptr;
|
|
codeptr += 2;
|
|
}
|
|
break;
|
|
}
|
|
irp->operand[opnum].op_type = O_ADDR;
|
|
irp->operand[opnum].op_addr = pc;
|
|
}
|
|
|
|
static void
|
|
op_m(int opnum, int opdata, instr_rec_t *irp)
|
|
{
|
|
op_e(opnum, 0, irp);
|
|
}
|
|
|
|
static void
|
|
op_o(int opnum, int opdata, instr_rec_t *irp)
|
|
{
|
|
if (irp->aflag) {
|
|
irp->operand[opnum].op_addr = *(uint32_t*)codeptr;
|
|
codeptr += 4;
|
|
} else {
|
|
irp->operand[opnum].op_addr = *(uint16_t*)codeptr;
|
|
codeptr += 2;
|
|
}
|
|
irp->operand[opnum].op_type = O_OFF;
|
|
}
|
|
|
|
static void
|
|
op_r(int opnum, int opdata, instr_rec_t *irp)
|
|
{
|
|
int rm;
|
|
rm = irp->modrm & 7;
|
|
switch (opdata) {
|
|
case T_d:
|
|
irp->operand[opnum].op_reg = reg_32[rm];
|
|
break;
|
|
case T_w:
|
|
irp->operand[opnum].op_reg = reg_16[rm];
|
|
break;
|
|
}
|
|
irp->operand[opnum].op_type = O_REG;
|
|
}
|
|
|
|
static void
|
|
op_x(int opnum, int opdata, instr_rec_t *irp)
|
|
{
|
|
irp->operand[opnum].op_seg = R_DS;
|
|
if (irp->aflag) {
|
|
irp->operand[opnum].op_reg = R_eSI;
|
|
} else {
|
|
irp->operand[opnum].op_reg = R_SI;
|
|
}
|
|
irp->operand[opnum].op_type = O_SEG;
|
|
}
|
|
|
|
static void
|
|
op_y(int opnum, int opdata, instr_rec_t *irp)
|
|
{
|
|
irp->operand[opnum].op_seg = R_ES;
|
|
if (irp->aflag) {
|
|
irp->operand[opnum].op_reg = R_eDI;
|
|
} else {
|
|
irp->operand[opnum].op_reg = R_DI;
|
|
}
|
|
irp->operand[opnum].op_type = O_SEG;
|
|
}
|
|
|
|
static void
|
|
get_operand_info(int opnum, instr_rec_t *irp)
|
|
{
|
|
int opcode, opdata;
|
|
|
|
opcode = opdata = 0;
|
|
|
|
switch(opnum) {
|
|
case 0:
|
|
opcode = irp->opcodep->Op1;
|
|
opdata = irp->opcodep->opdata1;
|
|
break;
|
|
case 1:
|
|
opcode = irp->opcodep->Op2;
|
|
opdata = irp->opcodep->opdata2;
|
|
break;
|
|
case 2:
|
|
opcode = irp->opcodep->Op3;
|
|
opdata = irp->opcodep->opdata3;
|
|
break;
|
|
}
|
|
switch (opcode) {
|
|
case M_A:
|
|
op_a(opnum, opdata, irp);
|
|
break;
|
|
|
|
case M_C:
|
|
op_c(opnum, opdata, irp);
|
|
break;
|
|
|
|
case M_D:
|
|
op_d(opnum, opdata, irp);
|
|
break;
|
|
|
|
case M_E:
|
|
op_e(opnum, opdata, irp);
|
|
break;
|
|
|
|
case M_indirE:
|
|
op_indir_e(opnum, opdata, irp);
|
|
break;
|
|
|
|
case M_G:
|
|
op_g(opnum, opdata, irp);
|
|
break;
|
|
|
|
case M_I:
|
|
op_i(opnum, opdata, irp);
|
|
break;
|
|
|
|
case M_sI:
|
|
op_si(opnum, opdata, irp);
|
|
break;
|
|
|
|
case M_J:
|
|
op_j(opnum, opdata, irp);
|
|
break;
|
|
|
|
case M_M:
|
|
op_m(opnum, opdata, irp);
|
|
break;
|
|
|
|
case M_O:
|
|
op_o(opnum, opdata, irp);
|
|
break;
|
|
|
|
case M_R:
|
|
op_r(opnum, opdata, irp);
|
|
break;
|
|
|
|
case M_S:
|
|
op_s(opnum, opdata, irp);
|
|
break;
|
|
|
|
case M_X:
|
|
op_x(opnum, opdata, irp);
|
|
break;
|
|
|
|
case M_Y:
|
|
op_y(opnum, opdata, irp);
|
|
break;
|
|
|
|
case M_REG:
|
|
case M_indirREG:
|
|
irp->operand[opnum].op_type = O_REG;
|
|
if (opdata >= R_AX) {
|
|
irp->operand[opnum].op_reg = opdata;
|
|
} else {
|
|
if (irp->dflag) {
|
|
irp->operand[opnum].op_reg =
|
|
reg_32[opdata];
|
|
} else {
|
|
irp->operand[opnum].op_reg =
|
|
reg_16[opdata];
|
|
}
|
|
}
|
|
if (opcode == M_indirREG) {
|
|
/* The O_BASE gets the right results */
|
|
irp->operand[opnum].op_type |= O_BASE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Temporary opcode_rec_s struct that we keep around for the times
|
|
* when we have to construct a special case instruction (e.g. some
|
|
* floating point instructions).
|
|
*/
|
|
static opcode_rec_t tempop;
|
|
static char fwait_name[] = "fwait";
|
|
|
|
int
|
|
get_instr_info(kaddr_t pc, instr_rec_t *irp)
|
|
{
|
|
int opcode, size = 0, p, prefixes = 0;
|
|
unsigned char modrm = 0;
|
|
opcode_rec_t *op;
|
|
|
|
if (instr_buf_init) {
|
|
bzero(&instrbuf, sizeof(instrbuf));
|
|
instr_buf_init = 0;
|
|
}
|
|
|
|
/* Check to see instrbuf is valid and if there are enough
|
|
* bytes in our instruction cache to cover the worst case
|
|
* scenario for this pc.
|
|
*/
|
|
if (!instrbuf.addr || (pc < instrbuf.addr) ||
|
|
(pc > (instrbuf.addr + instrbuf.size - 15))) {
|
|
instrbuf.addr = pc;
|
|
instrbuf.size = 256;
|
|
#ifdef REDHAT
|
|
fill_instr_cache(pc, (char *)instrbuf.buf);
|
|
#else
|
|
GET_BLOCK(pc, 256, instrbuf.buf);
|
|
#endif
|
|
if (KL_ERROR) {
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
/* Make sure that the instruction pointer points to the
|
|
* right byte in the buffer.
|
|
*/
|
|
instrbuf.ptr = instrbuf.buf + (pc - instrbuf.addr);
|
|
codeptr = instrbuf.ptr;
|
|
irp->addr = pc;
|
|
|
|
/* Check for prefixes
|
|
*/
|
|
while((p = is_prefix(*codeptr))) {
|
|
prefixes |= p;
|
|
codeptr++;
|
|
if ((prefixes & PREFIX_FWAIT) &&
|
|
((*codeptr < 0xd8) || (*codeptr > 0xdf))) {
|
|
|
|
/* If there is an fwait prefix that is not
|
|
* followed by a float instruction, we need to
|
|
* create a special instruction record so that
|
|
* the "fwait" gets printed out.
|
|
*/
|
|
bzero(&tempop, sizeof(tempop));
|
|
tempop.name = fwait_name;
|
|
irp->opcodep = &tempop;
|
|
size = ((unsigned)codeptr - (unsigned)instrbuf.ptr);
|
|
instrbuf.ptr = codeptr;
|
|
irp->size = size;
|
|
return(size);
|
|
}
|
|
}
|
|
if (prefixes & PREFIX_DATA) {
|
|
irp->dflag ^= 1;
|
|
}
|
|
if (prefixes & PREFIX_ADR) {
|
|
irp->aflag ^= 1;
|
|
}
|
|
|
|
/* Check for one or two byte opcode, capture the opcode and
|
|
* check for a ModR/M byte.
|
|
*/
|
|
if (*codeptr == 0x0f) {
|
|
opcode = *((unsigned short*)codeptr);
|
|
codeptr++;
|
|
op = &op_386_twobyte[*codeptr];
|
|
if(twobyte_has_modrm[*codeptr]) {
|
|
codeptr++;
|
|
modrm = *codeptr++;
|
|
} else {
|
|
codeptr++;
|
|
}
|
|
if (STREQ(op->name, "ud2a"))
|
|
codeptr += kt->BUG_bytes;
|
|
} else {
|
|
opcode = *codeptr;
|
|
op = &op_386[*codeptr];
|
|
if(onebyte_has_modrm[*codeptr]) {
|
|
codeptr++;
|
|
modrm = *codeptr++;
|
|
} else {
|
|
codeptr++;
|
|
}
|
|
}
|
|
/* See if the get_op bits from the modrm are needed to determine
|
|
* the actual instruction.
|
|
*/
|
|
if (op->Op1 == M_GRP) {
|
|
op = &grps[op->opdata1][(modrm & 0x38) >> 3];
|
|
|
|
/* Put something unique in opcode
|
|
*/
|
|
opcode = ((opcode << 8)|((modrm & 0x38) >> 3));
|
|
} else if (op->Op1 == M_FLOAT) {
|
|
int mod, rm, reg;
|
|
|
|
mod = modrm >> 6;
|
|
rm = modrm & 7;
|
|
reg = (modrm >> 3) & 7;
|
|
bzero(&tempop, sizeof(tempop));
|
|
if (mod != 3) {
|
|
tempop.name = float_mem[(opcode - 0xd8) * 8 + reg];
|
|
tempop.Op1 = M_E;
|
|
tempop.opdata1 = T_v;
|
|
op = &tempop;
|
|
} else {
|
|
op = &float_grps[opcode - 0xd8][reg];
|
|
if (op->Op1 == M_FGRP) {
|
|
tempop.name = fgrps[op->opdata1][rm];
|
|
/* instruction fnstsw is only one with
|
|
* strange arg
|
|
*/
|
|
if ((opcode == 0xdf) && (*codeptr == 0xe0)) {
|
|
irp->operand[1].op_type = O_REG;
|
|
irp->operand[1].op_reg = R_eAX;
|
|
}
|
|
op = &tempop;
|
|
}
|
|
}
|
|
}
|
|
irp->opcodep = op;
|
|
irp->opcode = opcode;
|
|
irp->modrm = modrm;
|
|
irp->prefixes = prefixes;
|
|
|
|
/* Check to see if this is a bad instruction (per a table entry)
|
|
*/
|
|
if (op->opdata1 == T_BAD) {
|
|
/* Back off the modrm if we grabbed one and return
|
|
* from here.
|
|
*/
|
|
if (modrm) {
|
|
codeptr--;
|
|
size = ((unsigned)codeptr - (unsigned)instrbuf.ptr);
|
|
instrbuf.ptr = codeptr;
|
|
irp->size = size;
|
|
return(size);
|
|
}
|
|
}
|
|
|
|
/* Check to see if there is an SIB byte.
|
|
*/
|
|
if (((modrm & 0xc0) != 0xc0) && ((modrm & 7) == 4)) {
|
|
/* There is an SIB byte
|
|
*/
|
|
irp->sib = *codeptr++;
|
|
irp->have_sib = 1;
|
|
}
|
|
|
|
/* Gather information on operands
|
|
*/
|
|
if (op->Op1 && (op->Op1 != M_BAD)) {
|
|
get_operand_info(0, irp);
|
|
}
|
|
if (op->Op2 && (op->Op2 != M_BAD)) {
|
|
get_operand_info(1, irp);
|
|
}
|
|
if (op->Op3 && (op->Op3 != M_BAD)) {
|
|
get_operand_info(2, irp);
|
|
}
|
|
|
|
/* Determine total instruction size and adjust instrbuf ptr
|
|
*/
|
|
size = ((unsigned)codeptr - (unsigned)instrbuf.ptr);
|
|
instrbuf.ptr = codeptr;
|
|
irp->size = size;
|
|
return(size);
|
|
}
|
|
|
|
static int
|
|
seg_prefix(int prefixes) {
|
|
if (prefixes & PREFIX_CS) {
|
|
return(R_CS);
|
|
} else if (prefixes & PREFIX_DS) {
|
|
return(R_DS);
|
|
} else if (prefixes & PREFIX_SS) {
|
|
return(R_SS);
|
|
} else if (prefixes & PREFIX_ES) {
|
|
return(R_ES);
|
|
} else if (prefixes & PREFIX_FS) {
|
|
return(R_FS);
|
|
} else if (prefixes & PREFIX_GS) {
|
|
return(R_GS);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
#ifdef NOT_USED
|
|
static void
|
|
print_seg_prefix(instr_rec_t *irp, FILE *ofp)
|
|
{
|
|
if (irp->prefixes & PREFIX_CS) {
|
|
fprintf(ofp, "%%cs:");
|
|
}
|
|
if (irp->prefixes & PREFIX_DS) {
|
|
fprintf(ofp, "%%ds:");
|
|
}
|
|
if (irp->prefixes & PREFIX_SS) {
|
|
fprintf(ofp, "%%ss:");
|
|
}
|
|
if (irp->prefixes & PREFIX_ES) {
|
|
fprintf(ofp, "%%es:");
|
|
}
|
|
if (irp->prefixes & PREFIX_FS) {
|
|
fprintf(ofp, "%%fs:");
|
|
}
|
|
if (irp->prefixes & PREFIX_GS) {
|
|
fprintf(ofp, "%%gs:");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifndef REDHAT
|
|
static int
|
|
print_prefixes(instr_rec_t *irp, FILE *ofp)
|
|
{
|
|
int cnt = 0;
|
|
|
|
if (irp->prefixes & PREFIX_REPZ) {
|
|
fprintf(ofp, "repz ");
|
|
cnt++;
|
|
}
|
|
if (irp->prefixes & PREFIX_REPNZ) {
|
|
fprintf(ofp, "repnz ");
|
|
cnt++;
|
|
}
|
|
if (irp->prefixes & PREFIX_LOCK) {
|
|
fprintf(ofp, "lock ");
|
|
cnt++;
|
|
}
|
|
if (irp->prefixes & PREFIX_ADR) {
|
|
if (irp->aflag) {
|
|
fprintf(ofp, "addr32 ");
|
|
} else {
|
|
fprintf(ofp, "addr16 ");
|
|
}
|
|
cnt++;
|
|
}
|
|
return(cnt);
|
|
}
|
|
|
|
static void
|
|
print_sib_value(int opnum, instr_rec_t *irp, FILE *ofp)
|
|
{
|
|
if (irp->operand[opnum].op_type & O_REG) {
|
|
if (irp->operand[opnum].op_type & O_BASE) {
|
|
fprintf(ofp, "(%s)",
|
|
reg_name[irp->operand[opnum].op_reg]);
|
|
} else {
|
|
fprintf(ofp, "%s",
|
|
reg_name[irp->operand[opnum].op_reg]);
|
|
}
|
|
return;
|
|
} else if (irp->operand[opnum].op_type & O_IMMEDIATE) {
|
|
fprintf(ofp, "$0x%x", irp->operand[opnum].op_addr);
|
|
return;
|
|
}
|
|
fprintf(ofp, "(");
|
|
if (irp->operand[opnum].op_type & O_BASE) {
|
|
fprintf(ofp, "%s,", reg_name[irp->operand[opnum].op_base]);
|
|
} else {
|
|
fprintf(ofp, ",");
|
|
}
|
|
if (irp->operand[opnum].op_type & O_INDEX) {
|
|
fprintf(ofp, "%s,", reg_name[irp->operand[opnum].op_index]);
|
|
}
|
|
fprintf(ofp, "%d)", (1 << irp->operand[opnum].op_scale));
|
|
}
|
|
|
|
static void
|
|
print_opvalue(int opnum, instr_rec_t *irp, FILE *ofp)
|
|
{
|
|
if (irp->operand[opnum].op_type & O_REG) {
|
|
if (irp->operand[opnum].op_type & (O_BASE|O_DISP)) {
|
|
fprintf(ofp, "(%s)",
|
|
reg_name[irp->operand[opnum].op_reg]);
|
|
} else {
|
|
fprintf(ofp, "%s",
|
|
reg_name[irp->operand[opnum].op_reg]);
|
|
}
|
|
} else if (irp->operand[opnum].op_type & O_IMMEDIATE) {
|
|
fprintf(ofp, "$0x%x", irp->operand[opnum].op_addr);
|
|
} else if (irp->operand[opnum].op_type & O_ADDR) {
|
|
/* jump or call address */
|
|
printaddr(irp->operand[opnum].op_addr, 0, ofp);
|
|
} else if (irp->operand[opnum].op_type & O_OFF) {
|
|
fprintf(ofp, "0x%x", irp->operand[opnum].op_addr);
|
|
}
|
|
}
|
|
|
|
int
|
|
print_instr(kaddr_t pc, FILE *ofp, int flag)
|
|
{
|
|
int p = 0, i, j, size, print_comma = 0;
|
|
instr_rec_t irp;
|
|
opcode_rec_t *op;
|
|
|
|
bzero(&irp, sizeof(irp));
|
|
/* XXX -- For now, make aflag and dflag equal to one. Should get
|
|
* this from some sort of configuration struct (set via
|
|
* initialization)
|
|
*/
|
|
irp.aflag = 1;
|
|
irp.dflag = 1;
|
|
size = get_instr_info(pc, &irp);
|
|
op = irp.opcodep;
|
|
if (!op) {
|
|
fprintf(ofp, "BAD INSTR (pc=0x%x)\n", pc);
|
|
return(0);
|
|
}
|
|
printaddr(pc, 1, ofp);
|
|
if (flag) {
|
|
fprintf(ofp, "0x%04x ", irp.opcode);
|
|
}
|
|
if (irp.prefixes) {
|
|
p = print_prefixes(&irp, ofp);
|
|
}
|
|
print_instrname(op->name, &irp, ofp);
|
|
/* HACK! but necessary to match i386-dis.c output for fwait.
|
|
*/
|
|
if (!strcmp(op->name, "fwait")) {
|
|
fprintf(ofp, "\n");
|
|
return(irp.size);
|
|
}
|
|
if (p || (strlen(op->name) >= 7)) {
|
|
fprintf(ofp, " ");
|
|
} else {
|
|
for (i = 0; i < (7 - strlen(op->name)); i++) {
|
|
fprintf(ofp, " ");
|
|
}
|
|
}
|
|
for (j = 0; j < 3; j++) {
|
|
if (irp.opcode == 0xc8) {
|
|
i = j;
|
|
} else {
|
|
i = 2 - j;
|
|
}
|
|
if(irp.operand[i].op_type) {
|
|
if (print_comma) {
|
|
fprintf(ofp, ",");
|
|
}
|
|
if (irp.operand[i].op_type & O_LPTR) {
|
|
fprintf(ofp, "0x%x,0x%x",
|
|
irp.operand[i].op_seg,
|
|
irp.operand[i].op_addr);
|
|
print_comma++;
|
|
continue;
|
|
}
|
|
if (irp.operand[i].op_type & O_CR) {
|
|
fprintf(ofp, "%%cr%d", irp.operand[i].op_reg);
|
|
print_comma++;
|
|
continue;
|
|
}
|
|
if (irp.operand[i].op_type & O_DB) {
|
|
fprintf(ofp, "%%db%d", irp.operand[i].op_reg);
|
|
print_comma++;
|
|
continue;
|
|
}
|
|
if (irp.operand[i].op_type & O_SEG) {
|
|
fprintf(ofp, "%s:(%s)",
|
|
reg_name[irp.operand[i].op_seg],
|
|
reg_name[irp.operand[i].op_reg]);
|
|
print_comma++;
|
|
continue;
|
|
}
|
|
if (irp.operand[i].op_type & O_INDIR) {
|
|
fprintf(ofp, "*");
|
|
}
|
|
if (irp.operand[i].op_type & O_DISP) {
|
|
fprintf(ofp, "0x%x", irp.operand[i].op_disp);
|
|
}
|
|
if (irp.have_sib) {
|
|
print_sib_value(i, &irp, ofp);
|
|
} else {
|
|
print_opvalue(i, &irp, ofp);
|
|
}
|
|
print_comma++;
|
|
}
|
|
}
|
|
if (flag) {
|
|
fprintf(ofp, " (%d %s)\n",
|
|
irp.size, (irp.size > 1) ? "bytes" : "byte");
|
|
} else {
|
|
fprintf(ofp, "\n");
|
|
}
|
|
return(irp.size);
|
|
}
|
|
|
|
void
|
|
list_instructions(FILE *ofp)
|
|
{
|
|
int i, j, print_comma = 0;
|
|
|
|
fprintf(ofp, "ONE BYTE INSTRUCTIONS:\n\n");
|
|
for(i = 0; i < 256; i++) {
|
|
fprintf(ofp, "0x%04x %s", i, op_386[i].name);
|
|
for (j = 0; j < (10 - strlen(op_386[i].name)); j++) {
|
|
fprintf(ofp, " ");
|
|
}
|
|
if (op_386[i].Op1) {
|
|
print_optype(op_386[i].Op1, op_386[i].opdata1, ofp);
|
|
print_comma++;
|
|
}
|
|
if (op_386[i].Op2) {
|
|
if (print_comma) {
|
|
fprintf(ofp, ",");
|
|
}
|
|
print_optype(op_386[i].Op2, op_386[i].opdata2, ofp);
|
|
print_comma++;
|
|
}
|
|
if (op_386[i].Op3) {
|
|
if (print_comma) {
|
|
fprintf(ofp, ",");
|
|
}
|
|
print_optype(op_386[i].Op3, op_386[i].opdata3, ofp);
|
|
}
|
|
fprintf(ofp, "\n");
|
|
|
|
}
|
|
|
|
fprintf(ofp, "\nTWO BYTE INSTRUCTIONS:\n\n");
|
|
for(i = 0; i < 256; i++) {
|
|
fprintf(ofp, "0x0f%02x %s", i, op_386_twobyte[i].name);
|
|
for (j = 0; j < (10 - strlen(op_386_twobyte[i].name)); j++) {
|
|
fprintf(ofp, " ");
|
|
}
|
|
if (op_386_twobyte[i].Op1) {
|
|
print_optype(op_386_twobyte[i].Op1,
|
|
op_386_twobyte[i].opdata1, ofp);
|
|
print_comma++;
|
|
}
|
|
if (op_386_twobyte[i].Op2) {
|
|
if (print_comma) {
|
|
fprintf(ofp, ",");
|
|
}
|
|
print_optype(op_386_twobyte[i].Op2,
|
|
op_386_twobyte[i].opdata2, ofp);
|
|
print_comma++;
|
|
}
|
|
if (op_386_twobyte[i].Op3) {
|
|
if (print_comma) {
|
|
fprintf(ofp, ",");
|
|
}
|
|
print_optype(op_386_twobyte[i].Op3,
|
|
op_386_twobyte[i].opdata3, ofp);
|
|
}
|
|
fprintf(ofp, "\n");
|
|
}
|
|
}
|
|
#endif /* !REDHAT */
|
|
|
|
void
|
|
free_instr_stream(instr_rec_t *irp)
|
|
{
|
|
instr_rec_t *ptr;
|
|
|
|
if(irp) {
|
|
while (irp->prev) {
|
|
irp = irp->prev;
|
|
}
|
|
while (irp) {
|
|
ptr = irp;
|
|
irp = irp->next;
|
|
kl_free_block(ptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
instr_rec_t *
|
|
get_instr_stream(kaddr_t pc, int bcount, int acount)
|
|
{
|
|
int size, count = 0;
|
|
kaddr_t addr, start_addr, end_addr;
|
|
syment_t *sp1, *sp2;
|
|
#ifdef REDHAT
|
|
syment_t *sp, *sp_next, *sp_next_next;
|
|
ulong offset;
|
|
#endif
|
|
instr_rec_t *fst = (instr_rec_t *)NULL, *lst, *ptr, *cur;
|
|
|
|
#ifdef REDHAT
|
|
cur = NULL;
|
|
if ((sp = x86_is_entry_tramp_address(pc, &offset)))
|
|
pc = sp->value + offset;
|
|
#endif
|
|
if (!(sp1 = kl_lkup_symaddr(pc))) {
|
|
return((instr_rec_t *)NULL);
|
|
}
|
|
start_addr = sp1->s_addr;
|
|
if (pc <= (sp1->s_addr + (bcount * 15))) {
|
|
if ((sp2 = kl_lkup_symaddr(sp1->s_addr - 4))) {
|
|
start_addr = sp2->s_addr;
|
|
}
|
|
}
|
|
#ifdef REDHAT
|
|
sp_next = next_symbol(NULL, sp1);
|
|
if (!sp_next)
|
|
return((instr_rec_t *)NULL);
|
|
sp_next_next = next_symbol(NULL, sp_next);
|
|
|
|
if (pc > (sp_next->s_addr - (acount * 15))) {
|
|
if (sp_next_next) {
|
|
end_addr = sp_next_next->s_addr;
|
|
} else {
|
|
end_addr = sp_next->s_addr;
|
|
}
|
|
} else {
|
|
end_addr = sp_next->s_addr;
|
|
}
|
|
#else
|
|
if (pc > (sp1->s_next->s_addr - (acount * 15))) {
|
|
if (sp1->s_next->s_next) {
|
|
end_addr = sp1->s_next->s_next->s_addr;
|
|
} else {
|
|
end_addr = sp1->s_next->s_addr;
|
|
}
|
|
} else {
|
|
end_addr = sp1->s_next->s_addr;
|
|
}
|
|
#endif
|
|
addr = start_addr;
|
|
while (addr <= pc) {
|
|
if (addr >= end_addr) {
|
|
/* We've gone too far (beyond the end of this
|
|
* function) The pc most likely was not valid
|
|
* (it pointed into the middle of an instruction).
|
|
*/
|
|
free_instr_stream(cur);
|
|
return((instr_rec_t *)NULL);
|
|
}
|
|
if (count <= bcount) {
|
|
/* Allocate another record
|
|
*/
|
|
cur = (instr_rec_t *)
|
|
kl_alloc_block(sizeof(instr_rec_t), K_TEMP);
|
|
count++;
|
|
cur->aflag = cur->dflag = 1;
|
|
if ((ptr = fst)) {
|
|
while (ptr->next) {
|
|
ptr = ptr->next;
|
|
}
|
|
ptr->next = cur;
|
|
cur->prev = ptr;
|
|
} else {
|
|
fst = cur;
|
|
}
|
|
} else {
|
|
/* Pull the last record to the front of the list
|
|
*/
|
|
ptr = fst;
|
|
if (ptr->next) {
|
|
fst = ptr->next;
|
|
fst->prev = (instr_rec_t *)NULL;
|
|
cur->next = ptr;
|
|
}
|
|
bzero(ptr, sizeof(*ptr));
|
|
ptr->aflag = ptr->dflag = 1;
|
|
if (ptr != fst) {
|
|
ptr->prev = cur;
|
|
}
|
|
cur = ptr;
|
|
|
|
}
|
|
size = get_instr_info(addr, cur);
|
|
if (size == 0) {
|
|
free_instr_stream(cur);
|
|
return((instr_rec_t *)NULL);
|
|
}
|
|
addr += size;
|
|
}
|
|
if (acount) {
|
|
lst = cur;
|
|
for (count = 0; count < acount; count++) {
|
|
ptr = (instr_rec_t *)
|
|
kl_alloc_block(sizeof(instr_rec_t), K_TEMP);
|
|
ptr->aflag = ptr->dflag = 1;
|
|
size = get_instr_info(addr, ptr);
|
|
if (size == 0) {
|
|
kl_free_block(ptr);
|
|
return(cur);
|
|
}
|
|
lst->next = ptr;
|
|
ptr->prev = lst;
|
|
lst = ptr;
|
|
addr += size;
|
|
}
|
|
}
|
|
return(cur);
|
|
}
|
|
|
|
#ifndef REDHAT
|
|
/*
|
|
* print_instr_stream()
|
|
*/
|
|
kaddr_t
|
|
print_instr_stream(kaddr_t value, int bcount, int acount, int flags, FILE *ofp)
|
|
{
|
|
kaddr_t v = value;
|
|
instr_rec_t *cur_irp, *irp;
|
|
|
|
if ((cur_irp = get_instr_stream(v, bcount, acount))) {
|
|
irp = cur_irp;
|
|
|
|
/* Walk back to the start of the stream and then
|
|
* print out all instructions in the stream.
|
|
*/
|
|
while (irp->prev) {
|
|
irp = irp->prev;
|
|
}
|
|
while (irp) {
|
|
if (flags & C_FULL) {
|
|
print_instr(irp->addr, ofp, 1);
|
|
} else {
|
|
print_instr(irp->addr, ofp, 0);
|
|
}
|
|
if (irp->addr >= value) {
|
|
v += irp->size;
|
|
}
|
|
irp = irp->next;
|
|
}
|
|
free_instr_stream(cur_irp);
|
|
}
|
|
return(v);
|
|
}
|
|
|
|
/*
|
|
* dump_instr() -- architecture specific instruction dump routine
|
|
*/
|
|
void
|
|
dump_instr(kaddr_t addr, uint64_t count, int flags, FILE *ofp)
|
|
{
|
|
fprintf(ofp, "This operation not supported for i386 architecture.\n");
|
|
}
|
|
#endif /* !REDHAT */
|
|
|
|
/*
|
|
* lkcdutils-4.1/libutil/kl_queue.c
|
|
*/
|
|
|
|
/*
|
|
* Copyright 2002 Silicon Graphics, Inc. All rights reserved.
|
|
*/
|
|
#ifndef REDHAT
|
|
#include <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 */
|