home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Source Code 1992 March
/
Source_Code_CD-ROM_Walnut_Creek_March_1992.iso
/
usenet
/
altsrcs
/
2
/
2431
/
disasm.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-01-01
|
31KB
|
1,527 lines
/*
* Xenix disassembly program by P.Garbha (pgd@compuram.bbt.se)
*
* 80386 instruction printer by Pace Willisson (pace@prep.ai.mit.edu)
* July 1988
* Copyright (C) 1988 Free Software Foundation, Inc.
*/
#include <stdio.h>
#include <ctype.h>
#include <a.out.h>
#include <sys/relsym.h>
#include <string.h>
#include "386opcodes.h"
#define MAXSEG 20
#define THRESHOLD 0x100
static int prefixes;
static char obuf[100];
static char *obufp;
static char scratchbuf[1000];
static unsigned char *start_codep;
static unsigned char *codep;
static int mod;
static int rm;
static int reg;
struct xexec xoutx;
struct xext xoutxe;
struct xseg xoutxs[MAXSEG];
struct {
short signature;
short lengthb; /* length of file, lsb */
short lengthp; /* length of file in 512-byte pages */
short relitems; /* number of relocation table items */
short hdrsize; /* size of header in 16-byte paragraphs */
short maxparas1;
short maxparas2;
short stackoff; /* Offset of stack segment in load module */
short initsp; /* Initial sp */
short checksum; /* Word checksum */
short initip; /* Initial ip */
short csoff; /* Offset of code segment */
short reloff; /* Offset of the first relocation item */
short ovlno; /* Overlay number */
} exehdr;
int segcount = 0;
struct xseg *textsegp = 0, *datasegp = 0, *symsegp = 0;
int textseg = -1, dataseg = -1;
unsigned char *textp, *datap, *xtextp;
int textsize, datasize, database;
struct symtab {
char *symstring;
unsigned int symval;
short symrefcount;
short symflags;
};
#define TEXTSEG 0000001
#define DATASEG 0000002
#define GENERATED 0000004
#define BYTEREF 0000010
#define WORDREF 0000020
#define DWORDREF 0000040
#define JUMPREF 0000100
#define CALLREF 0000200
#define DEFINED 0000400
#define PUSHLIMMED 0001000
#define ASCIZ 0002000
#define ADDRREF 0004000
#define NOENTER 0100000
struct symtab *lookup(), *enter();
char *symname(), *getimmed(), *datasymbol(), *ascizdata();
#define SYMHS 20033
struct symtab *symhash[SYMHS];
int symcount = 0;
int pass2 = 0;
init()
{
register int i;
for (i = 0; i < SYMHS; i++) {
symhash[i] = NULL;
}
}
main(argc, argv)
char **argv;
{
init();
if (argc < 2) fatal("No input file");
loadaout(argv[1]);
dopass1();
symtabpass();
dopass2();
dodata();
exit(0);
}
dopass1()
{
int ipc;
char buf[200];
int len;
ipc = 0;
while (ipc < textsize) {
len = i386dis(ipc, textp+ipc, buf);
ipc += len;
}
}
dopass2()
{
int ipc;
int oipc = 0;
char buf[200];
int len;
register struct symtab *lbp;
int i, mode;
int unreachable = 0, firsttime = 0;
pass2 = 1;
ipc = 0;
while (ipc < textsize) {
if (textp != xtextp) abort();
lbp = lookup(ipc, TEXTSEG);
if (lbp && lbp->symrefcount == 1 && lbp->symflags & (WORDREF|DWORDREF) && !(lbp->symflags & (CALLREF|JUMPREF))) {
mode = lbp->symflags;
printf("%s:\t\t; %0x (%d)\n", symname(lbp), ipc, lbp->symrefcount);
for (;;) {
register unsigned int addr;
register struct symtab *symp;
codep = textp+ipc;
addr = *(unsigned int *)codep;
symp = lookup(addr, TEXTSEG|JUMPREF);
if (symp)
printf("\t.dword\t%s\n", symname(symp));
else
printf("\t.dword\t%#x\n", addr);
len = 4;
++ipc;
for (i = 1; i < len; i++) {
if (lbp = lookup(ipc, TEXTSEG)) {
/*
* This is a hack, just to
* void breaking a jump table
* by a garbage label.
* We assume no code segment
* starts with the 0 opcode
*/
if (textp[ipc] < 0xf) {
ipc++;
continue;
}
printf("Code overlapping, backing up\n");
oipc = ipc + len;
break;
}
ipc++;
}
if (i < len || i == len && lookup(ipc, TEXTSEG))
break;
}
} else {
register int opcode;
opcode = *(textp+ipc);
if (lbp) {
printf("%s:\t\t; %0x (%d)\n", symname(lbp), ipc, lbp->symrefcount);
unreachable = 0;
}
if (unreachable && opcode == 0) {
printf("\t.byte\t0\t\t; unreachable");
len = 1;
} else if (unreachable && opcode == 0x90) {
printf("\tnop\t\t\t; unreachable");
len = 1;
} else {
if (unreachable && firsttime) {
printf("Warning: Unreachable code\n");
firsttime = 0;
}
len = i386dis(ipc, textp+ipc, buf);
putchar('\t');
fputs(buf, stdout);
}
/*
* Check for moving into unreachable code
*/
if (opcode==0xeb||opcode==0xe9||opcode==0xc3||opcode==0xc2||opcode==0xa||opcode==0xcb) {
unreachable = firsttime = 1;
}
/* printf("\t\t; %#x", *start_codep); */
putchar('\n');
++ipc;
for (i = 1; i < len; i++) {
register struct symtab *symp;
if (symp = lookup(ipc, TEXTSEG)) {
if (unreachable) {
printf("Code overlapping, backing up\n");
break;
}
printf("Warning: Hidden label: %s\n", symname(symp));
}
ipc++;
}
}
}
}
/*
* Print out data area
*/
dodata()
{
int dpc;
register struct symtab *lbp;
register int c;
dpc = 0;
while (dpc < datasize) {
lbp = lookup(dpc+database, DATASEG);
while (lbp && (lbp->symflags & ASCIZ)) {
for (;;) {
dpc++;
if (dpc >= datasize)
return;
if (lbp = lookup(dpc+database, DATASEG))
break;
}
}
if (lbp) {
printf("%s:\t\t; %0x (%d)", symname(lbp), dpc+database, lbp->symrefcount);
if (lbp->symflags & BYTEREF) printf(" BYTE ");
if (lbp->symflags & WORDREF) printf(" WORD ");
if (lbp->symflags & DWORDREF) printf(" DWORD ");
if (lbp->symflags & ASCIZ) printf(" ASCIZ ");
if (lbp->symflags & ADDRREF) printf(" ADDR ");
putchar('\n');
}
if (!pass2) {
dpc++;
continue;
}
switch (lbp ? lbp->symflags & (BYTEREF|WORDREF|DWORDREF|ADDRREF) : BYTEREF) {
case BYTEREF:
default:
deflt:
c = datap[dpc];
printf("\t.byte\t%#x\t;", c);
if (c>=' ' && c < 127)
printf("'%c'", c);
else
printf(" ");
printf(" %#x\n", dpc /*+database */);
dpc++;
break;
case WORDREF:
if (dpc+1 < datasize
&& !lookup(dpc+database+1, DATASEG)) {
printf("\t.word\t%#x\t; %d\t%#x\n",
*((unsigned short *)(datap+dpc)), dpc);
dpc += 2;
break;
}
goto deflt;
case DWORDREF:
if (dpc+3 < datasize
&& !lookup(dpc+database+1, DATASEG)
&& !lookup(dpc+database+2, DATASEG)
&& !lookup(dpc+database+3, DATASEG)) {
printf("\t.dword\t%#x\t; %d\t%#x\n",
*((unsigned long *)(datap+dpc)), dpc);
dpc += 4;
break;
}
goto deflt;
}
}
}
ckprefix ()
{
prefixes = 0;
while (1) {
switch (*codep) {
case 0xf3:
prefixes |= PREFIX_REPZ;
break;
case 0xf2:
prefixes |= PREFIX_REPNZ;
break;
case 0xf0:
prefixes |= PREFIX_LOCK;
break;
case 0x2e:
prefixes |= PREFIX_CS;
break;
case 0x36:
prefixes |= PREFIX_SS;
break;
case 0x3e:
prefixes |= PREFIX_DS;
break;
case 0x26:
prefixes |= PREFIX_ES;
break;
case 0x64:
prefixes |= PREFIX_FS;
break;
case 0x65:
prefixes |= PREFIX_GS;
break;
case 0x66:
prefixes |= PREFIX_DATA;
break;
case 0x67:
prefixes |= PREFIX_ADR;
break;
case 0x9b:
prefixes |= PREFIX_FWAIT;
break;
default:
return;
}
codep++;
}
}
static int dflag;
static int aflag;
static int Sflag;
static char op1out[100], op2out[100], op3out[100];
static int start_pc;
/*
* disassemble the first instruction in 'inbuf'. You have to make
* sure all of the bytes of the instruction are filled in.
* On the 386's of 1988, the maximum length of an instruction is 15 bytes.
* (see topic "Redundant prefixes" in the "Differences from 8086"
* section of the "Virtual 8086 Mode" chapter.)
* 'pc' should be the address of this instruction, it will
* be used to print the target address if this is a relative jump or call
* 'outbuf' gets filled in with the disassembled instruction. it should
* be long enough to hold the longest disassembled instruction.
* 100 bytes is certainly enough, unless symbol printing is added later
* The function returns the length of this instruction in bytes.
*/
i386dis (pc, inbuf, outbuf)
int pc;
unsigned char *inbuf;
char *outbuf;
{
struct dis386 *dp;
char *p;
int i;
int enter_instruction;
char *first, *second, *third;
int needcomma;
obuf[0] = 0;
op1out[0] = 0;
op2out[0] = 0;
op3out[0] = 0;
start_pc = pc;
start_codep = inbuf;
codep = inbuf;
ckprefix ();
if (*codep == 0xc8)
enter_instruction = 1;
else
enter_instruction = 0;
obufp = obuf;
if (prefixes & PREFIX_REPZ)
oappend ("repz ");
if (prefixes & PREFIX_REPNZ)
oappend ("repnz ");
if (prefixes & PREFIX_LOCK)
oappend ("lock ");
if ((prefixes & PREFIX_FWAIT) && ((*codep < 0xd8) || (*codep > 0xdf))) {
/* fwait not followed by floating point instruction */
oappend ("fwait");
strcpy (outbuf, obuf);
return (1);
}
/* these would be initialized to 0 if disassembling for 8086 or 286 */
dflag = aflag = Sflag = 0;
if ((xoutx.x_cpu & XC_CPU) == XC_386) {
dflag = aflag = 1;
}
if (prefixes & PREFIX_DATA)
dflag ^= 1;
if (prefixes & PREFIX_ADR) {
aflag ^= 1;
oappend ("addr16 ");
}
if (*codep == 0x0f)
dp = &dis386_twobyte[*++codep];
else
dp = &dis386[*codep];
codep++;
mod = (*codep >> 6) & 3;
reg = (*codep >> 3) & 7;
rm = *codep & 7;
if (dp->name == NULL && dp->bytemode1 == FLOATCODE) {
dofloat ();
} else {
if (dp->name == NULL)
dp = &grps[dp->bytemode1][reg];
putop (dp->name);
obufp = op1out;
if (dp->op1)
(*dp->op1)(dp->bytemode1);
obufp = op2out;
if (dp->op2)
(*dp->op2)(dp->bytemode2);
obufp = op3out;
if (dp->op3)
(*dp->op3)(dp->bytemode3);
}
obufp = obuf + strlen (obuf);
/* for (i = strlen (obuf); i < 6; i++)
oappend (" ");
oappend (" "); */
oappend ("\t");
/* enter instruction is printed with operands in the
* same order as the intel book; everything else
* is printed in reverse order
*/
if (enter_instruction) {
first = op1out;
second = op2out;
third = op3out;
} else {
first = op3out;
second = op2out;
third = op1out;
}
needcomma = 0;
if (*first) {
oappend (first);
needcomma = 1;
}
if (*second) {
if (needcomma)
oappend (",");
oappend (second);
needcomma = 1;
}
if (*third) {
if (needcomma)
oappend (",");
oappend (third);
}
strcpy (outbuf, obuf);
return (codep - inbuf);
}
#include "387opcodes.h"
dofloat ()
{
struct dis386 *dp;
unsigned char floatop;
floatop = codep[-1];
if (mod != 3) {
putop (float_mem[(floatop - 0xd8) * 8 + reg]);
obufp = op1out;
OP_E (v_mode);
return;
}
codep++;
dp = &float_reg[floatop - 0xd8][reg];
if (dp->name == NULL) {
putop (fgrps[dp->bytemode1][rm]);
/* instruction fnstsw is only one with strange arg */
if (floatop == 0xdf && *codep == 0xe0)
strcpy (op1out, "%eax");
} else {
putop (dp->name);
obufp = op1out;
if (dp->op1)
(*dp->op1)(dp->bytemode1);
obufp = op2out;
if (dp->op2)
(*dp->op2)(dp->bytemode2);
}
}
/* ARGSUSED */
OP_ST (ignore)
{
oappend ("%st");
}
/* ARGSUSED */
OP_STi (ignore)
{
sprintf (scratchbuf, "%%st(%d)", rm);
oappend (scratchbuf);
}
/* capital letters in template are macros */
putop (template)
char *template;
{
char *p;
for (p = template; *p; p++) {
switch (*p) {
default:
*obufp++ = *p;
break;
case 'C': /* For jcxz/jecxz */
if (aflag == 0)
*obufp++ = 'e';
break;
case 'N':
if ((prefixes & PREFIX_FWAIT) == 0)
*obufp++ = 'n';
break;
case 'S':
/* operand size flag */
Sflag = 1;
if (dflag)
*obufp++ = 'l';
else
*obufp++ = 'w';
break;
}
}
*obufp = 0;
}
oappend (s)
char *s;
{
strcpy (obufp, s);
obufp += strlen (s);
*obufp = 0;
}
append_prefix ()
{
if (prefixes & PREFIX_CS)
oappend ("%cs:");
if (prefixes & PREFIX_DS)
oappend ("%ds:");
if (prefixes & PREFIX_SS)
oappend ("%ss:");
if (prefixes & PREFIX_ES)
oappend ("%es:");
if (prefixes & PREFIX_FS)
oappend ("%fs:");
if (prefixes & PREFIX_GS)
oappend ("%gs:");
}
OP_indirE (bytemode)
{
oappend ("*");
OP_E (bytemode);
}
OP_E (bytemode)
{
int disp;
int havesib; /* have "scaled index base" byte */
int didoutput = 0;
int base;
int index;
int scale;
int havebase;
/* skip mod/rm byte */
codep++;
havesib = 0;
havebase = 0;
disp = 0;
if (mod == 3) { /* Register mod's */
switch (bytemode) {
case b_mode:
oappend (names8[rm]);
break;
case w_mode:
oappend (names16[rm]);
break;
case v_mode:
if (dflag)
oappend (names32[rm]);
else
oappend (names16[rm]);
break;
default:
oappend ("<bad dis table>");
break;
}
return;
}
append_prefix();
if (rm == 4 && aflag) {
havesib = 1;
havebase = 1;
scale = (*codep >> 6) & 3;
index = (*codep >> 3) & 7;
base = *codep & 7;
codep++;
}
if (!aflag && mod < 3) {
switch (mod) {
case 0:
if (rm == 6) {
disp = get16();
oappend(datasymbol(disp, WORDREF, 1));
return;
}
break;
case 1:
sprintf(scratchbuf, "%#x", *codep++);
oappend(scratchbuf);
break;
case 2:
disp = get16();
oappend(datasymbol(disp, WORDREF, 1));
break;
}
switch (rm) {
case 0: oappend("(%bx,%si)"); return;
case 1: oappend("(%bx,%di)"); return;
case 2: oappend("(%bp,%si)"); return;
case 3: oappend("(%bp,%di)"); return;
case 4: oappend("(%si)"); return;
case 5: oappend("(%di)"); return;
case 6: oappend("(%bp)"); return;
case 7: oappend("(%bx)"); return;
}
}
switch (mod) {
case 0:
switch (rm) {
case 4: /* DS:[d32+(scaled index)] */
if (!aflag)
break;
/* implies havesib and havebase */
if (base == 5) {
havebase = 0;
disp = get32 ();
}
break;
case 5: /* DS:d32 */
if (!aflag)
break;
disp = get32 ();
break;
case 6: /* DS:d16 */
if (aflag)
break;
disp = get16 ();
break;
default: /* DS:[Ereg] */
havebase = 1;
base = rm;
break;
}
break;
case 1: /* DS:[Ereg+d8] */
disp = *(char *)codep++;
if (rm != 4) {
havebase = 1;
base = rm;
}
break;
case 2: /* DS:[Ereg+d32] */
if (aflag) {
disp = get32 ();
if (rm != 4) {
havebase = 1;
base = rm;
}
} else {
disp = get16();
}
break;
}
/* if (mod < 3) { */
if (havesib) {
if (mod == 0) {
if (base == 5) { /* DS:[d32+(scaled index)] */
if (prefixes & PREFIX_CS) {
register struct symtab *symp;
int mode = scale==2 ? WORDREF
: scale==3 ? DWORDREF
: 0;
symp = lookup(disp, TEXTSEG|mode);
if (pass2)
if (symp)
sprintf (scratchbuf, "%s", symname(symp));
else
sprintf (scratchbuf, "%s", datasymbol(disp, mode, 1));
oappend(scratchbuf);
} else
oappend(datasymbol(disp, 0, 1));
}
} else if (base == 5) { /* SS:[EBP+...] */
sprintf (scratchbuf, "%d", disp);
oappend(scratchbuf);
} else if (base == 4) { /* SS:[ESP+...] */
sprintf(scratchbuf, "%d", disp);
oappend(scratchbuf);
} else { /* DS:[...] */
if (mod == 1) { /* Byte offset */
sprintf (scratchbuf, "%d", disp);
oappend(scratchbuf);
} else { /* Longword offset */
oappend(datasymbol(disp, 0, 1));
}
}
} else if (aflag && rm == 5 || /* DS:d32 or SS:[EBP...] */
!aflag && rm == 6) { /* DS:d16 */
if (mod == 0) { /* DS:d32 or DS:d16 */
/* Normal "direct" data address */
oappend(datasymbol(/* Sflag && !dflag ? disp + database : */ disp,
bytemode==b_mode ? BYTEREF :
bytemode==w_mode ? WORDREF :
Sflag ? dflag ? DWORDREF
: WORDREF : 0, 1));
} else {
sprintf(scratchbuf, "%d", disp);
oappend(scratchbuf);
}
#if 0
} else if (mod == 2) { /* DS:[ereg+d8] */
sprintf(scratchbuf, "%d", disp);
oappend(scratchbuf);
#endif
} else { /* DS:[ereg+d32] */
oappend(datasymbol(disp, 0, 1));
}
/* } */
if (havebase || havesib) {
oappend ("(");
if (!havebase && !havesib) oappend("????");
if (havebase)
oappend ((aflag ? names32 : names16_OP_E)[base]);
if (havesib) {
if (index != 4) {
sprintf (scratchbuf, ",%s", names32[index]);
oappend (scratchbuf);
sprintf (scratchbuf, ",%d", 1 << scale);
oappend (scratchbuf);
}
}
oappend (")");
}
}
OP_G (bytemode)
{
switch (bytemode) {
case b_mode:
oappend (names8[reg]);
break;
case w_mode:
oappend (names16[reg]);
break;
case d_mode:
oappend (names32[reg]);
break;
case v_mode:
if (dflag)
oappend (names32[reg]);
else
oappend (names16[reg]);
break;
default:
oappend ("<internal disassembler error>");
break;
}
}
get32 ()
{
int x = 0;
x = *codep++ & 0xff;
x |= (*codep++ & 0xff) << 8;
x |= (*codep++ & 0xff) << 16;
x |= (*codep++ & 0xff) << 24;
return (x);
}
get16 ()
{
int x = 0;
x = *codep++ & 0xff;
x |= (*codep++ & 0xff) << 8;
return (x);
}
OP_REG (code)
{
char *s;
switch (code) {
case indir_dx_reg:
s = "(%dx)";
break;
case ax_reg:
case cx_reg:
case dx_reg:
case bx_reg:
case sp_reg:
case bp_reg:
case si_reg:
case di_reg:
s = names16[code - ax_reg];
break;
case es_reg:
case ss_reg:
case cs_reg:
case ds_reg:
case fs_reg:
case gs_reg:
s = names_seg[code - es_reg];
break;
case al_reg:
case ah_reg:
case cl_reg:
case ch_reg:
case dl_reg:
case dh_reg:
case bl_reg:
case bh_reg:
s = names8[code - al_reg];
break;
case eAX_reg:
case eCX_reg:
case eDX_reg:
case eBX_reg:
case eSP_reg:
case eBP_reg:
case eSI_reg:
case eDI_reg:
if (dflag)
s = names32[code - eAX_reg];
else
s = names16[code - eAX_reg];
break;
default:
s = "<internal disassembler error>";
break;
}
oappend (s);
}
OP_I (bytemode)
{
int op;
switch (bytemode) {
case b_mode:
op = *codep++ & 0xff;
break;
case v_mode:
if (dflag)
op = get32 ();
else
op = get16 ();
break;
case w_mode:
op = get16 ();
break;
default:
oappend ("<internal disassembler error>");
return;
}
oappend (getimmed(op));
}
OP_sI (bytemode)
{
int op;
switch (bytemode) {
case b_mode:
op = *(char *)codep++;
break;
case v_mode:
if (dflag)
op = get32 ();
else
op = (short)get16();
break;
case w_mode:
op = (short)get16 ();
break;
default:
oappend ("<internal disassembler error>");
return;
}
oappend (getimmed(op));
}
/*
* Jump opcodes
*/
OP_J (bytemode)
{
int disp, addr;
int mask = -1;
register struct symtab *symp;
switch (bytemode) {
case b_mode:
disp = *(char *)codep++;
break;
case v_mode:
if (dflag)
disp = get32 ();
else {
disp = (short)get16 ();
/* for some reason, a data16 prefix on a jump instruction
means that the pc is masked to 16 bits after the
displacement is added! */
mask = 0xffff;
}
break;
default:
oappend ("<internal disassembler error>");
return;
}
addr = (start_pc + codep - start_codep + disp) & mask;
symp = lookup(addr, TEXTSEG|JUMPREF);
if (pass2) {
if (symp)
sprintf (scratchbuf, "%s", symname(symp));
else
sprintf (scratchbuf, "%s", datasymbol(addr, 0, 1));
oappend (scratchbuf);
}
}
/* ARGSUSED */
OP_SEG (dummy)
{
static char *sreg[] = {
"%es","%cs","%ss","%ds","%fs","%gs","%?","%?",
};
oappend (sreg[reg]);
}
/*
* A subroutine address, or a jump/call far destination
*/
OP_DIR (size)
{
int seg, offset, addr;
register struct symtab *symp;
switch (size) {
case lptr:
if (aflag) {
offset = get32 ();
seg = get16 ();
} else {
offset = get16 ();
seg = get16 ();
}
sprintf (scratchbuf, "0x%x,0x%x", seg, offset);
oappend (scratchbuf);
break;
case v_mode: /* Call instruction destination */
if (aflag)
offset = get32 ();
else
offset = (short)get16 ();
addr = start_pc + codep - start_codep + offset;
symp = lookup(addr, TEXTSEG|CALLREF);
if (pass2)
if (symp)
oappend(symname(symp));
else
oappend(datasymbol(addr, 0, 1));
break;
default:
oappend ("<internal disassembler error>");
break;
}
}
/* ARGSUSED */
OP_OFF (bytemode)
{
int off;
if (aflag)
off = get32 ();
else
off = get16 ();
oappend(datasymbol(off, 0, 0));
}
/* ARGSUSED */
OP_ESDI (dummy)
{
oappend ("%es:(");
oappend (aflag ? "%edi" : "%di");
oappend (")");
}
/* ARGSUSED */
OP_DSSI (dummy)
{
oappend ("%ds:(");
oappend (aflag ? "%esi" : "%si");
oappend (")");
}
/* ARGSUSED */
OP_ONE (dummy)
{
oappend ("1");
}
/* ARGSUSED */
OP_C (dummy)
{
codep++; /* skip mod/rm */
sprintf (scratchbuf, "%%cr%d", reg);
oappend (scratchbuf);
}
/* ARGSUSED */
OP_D (dummy)
{
codep++; /* skip mod/rm */
sprintf (scratchbuf, "%%db%d", reg);
oappend (scratchbuf);
}
/* ARGSUSED */
OP_T (dummy)
{
codep++; /* skip mod/rm */
sprintf (scratchbuf, "%%tr%d", reg);
oappend (scratchbuf);
}
OP_rm (bytemode)
{
switch (bytemode) {
case d_mode:
oappend (names32[rm]);
break;
case w_mode:
oappend (names16[rm]);
break;
}
}
/*
* Load the file to disassemble, into memory
* Decoding the Xenix a.out header
*/
loadaout(fname)
{
int fd;
register int i;
short magic;
fd = open(fname, 0);
if (fd == 0) {
perror(fname); exit(1);
}
if (read(fd, &magic, 2) != 2) {
perror(fname); exit(1);
}
lseek(fd, 0L, 0);
if (magic == X_MAGIC) {
if (read(fd, &xoutx, sizeof xoutx) != sizeof xoutx) {
perror(fname); exit(1);
}
/* if ((xoutx.x_cpu & XC_CPU) != XC_386)
fatal("Not a 80386 executable file - %s\n", fname); */
if (read(fd, &xoutxe, sizeof xoutxe) != sizeof xoutxe) {
perror(fname); exit(1);
}
if (!(xoutx.x_renv & XE_SEG))
fatal("Not a Xenix segmented file - %s\n", fname);
lseek(fd, xoutxe.xe_segpos, 0);
segcount = xoutxe.xe_segsize / sizeof(struct xseg);
if (segcount > MAXSEG)
fatal("Too many segments (%d)", segcount);
if (read(fd, xoutxs, segcount * sizeof(struct xseg)) != segcount * sizeof(struct xseg)) {
perror(fname); exit(1);
}
for (i = 0; i < segcount; i++) {
switch (xoutxs[i].xs_type) {
case XS_TTEXT:
if (textsegp) fatal("Multiple text segments");
textsegp = &xoutxs[i];
textseg = textsegp->xs_seg;
break;
case XS_TDATA:
if (datasegp) fatal("Multiple data segments");
datasegp = &xoutxs[i];
dataseg = datasegp->xs_seg;
break;
case XS_TSYMS:
if (xoutxs[i].xs_attr != XS_SXSEG)
continue;
if (symsegp) fatal("Multiple symbol segments");
symsegp = &xoutxs[i];
break;
case XS_TREL:
break;
default:
fatal("Unknown segment: %x", xoutxs[i].xs_type);
}
}
if (textsegp == NULL) fatal("Undefined text segment");
if (datasegp == NULL) fatal("Undefined data segment");
if (symsegp != NULL)
readsymbols(fd);
textp = (unsigned char *)malloc(textsegp->xs_psize);
if (textp == NULL) fatal("Out of memory");
xtextp = textp;
datap = (unsigned char *)malloc(datasegp->xs_psize);
if (datap == NULL) fatal("Out of memory");
lseek(fd, textsegp->xs_filpos, 0);
if (read(fd, textp, textsegp->xs_psize) != textsegp->xs_psize) {
perror(fname); fatal("File read error (text segment)");
}
lseek(fd, datasegp->xs_filpos, 0);
if (read(fd, datap, datasegp->xs_psize) != datasegp->xs_psize) {
perror(fname);
fatal("File read error (data segment)");
}
textsize = textsegp->xs_vsize;
datasize = datasegp->xs_vsize;
database = datasegp->xs_rbase;
} else if (magic == 0x5a4d) {
/* msdos .exe file */
if (read(fd, &exehdr, sizeof exehdr) != sizeof exehdr) {
perror(fname); exit(1);
}
datasize = exehdr.csoff * 16;
textsize = exehdr.lengthp * 512 - datasize - exehdr.hdrsize*16;
if (exehdr.lengthb)
textsize += exehdr.lengthb - 512;
database = 0;
textp = (unsigned char *)malloc(textsize);
if (textp == NULL) fatal("Out of memory");
xtextp = textp;
datap = (unsigned char *)malloc(datasize);
if (datap == NULL) fatal("Out of memory");
lseek(fd, exehdr.hdrsize * 16, 0);
if (read(fd, datap, datasize) != datasize) {
perror(fname);
fatal("File read error (data segment)");
}
if (read(fd, textp, textsize) != textsize) {
perror(fname); fatal("File read error (text segment)");
}
} else
fatal("Not a recognized executable file - %s\n", fname);
close(fd);
}
readsymbols(fd)
{
register char *symp;
char *symendp;
int type, seg, value;
char *name;
int i;
symp = (char *)malloc(symsegp->xs_psize);
if (symp == NULL) fatal("Out of memory");
lseek(fd, symsegp->xs_filpos, 0);
if ((i = read(fd, symp, symsegp->xs_psize)) != symsegp->xs_psize)
fatal("Symbol Table read error");
symendp = symp + symsegp->xs_psize;
while (symp < symendp) {
type = ((struct sym *)symp)->s_type;
seg = ((struct sym *)symp)->s_seg;
value = ((struct sym *)symp)->s_value;
name = symp + sizeof(struct sym);
symp += sizeof(struct sym) + strlen(name) + 1;
if (seg == textseg)
enter(name, value, TEXTSEG|DEFINED);
else if (seg == dataseg)
enter(name, value, DATASEG|DEFINED);
}
}
fatal(f, args)
{
vfprintf(stderr, f, &args);
fprintf(stderr, "\n");
exit(1);
}
/*
* Look up a symbol in symboltable.
* Enter it as undefined if not
* already there.
*/
struct symtab *lookup(addr, ref)
unsigned int addr;
{
register struct symtab *symtp,**symhp;
int seg = ref & (TEXTSEG|DATASEG);
symhp = &symhash[addr % SYMHS];
while (symtp = *symhp) {
if (addr == symtp->symval && (seg & symtp->symflags)) {
if (!pass2) {
symtp->symflags |= ref;
symtp->symrefcount++;
}
return symtp;
}
if ((++symhp) >= &symhash[SYMHS])
symhp = symhash;
}
if (pass2 || ref & NOENTER || !aflag && addr < THRESHOLD)
return NULL;
symtp = enter(NULL, addr, ref|GENERATED);
symtp->symrefcount++;
return symtp;
}
/*
* enter a symbol into symboltable.
*/
struct symtab *enter(id, addr, ref)
char *id;
unsigned addr;
{
register struct symtab *symtp, **symhp, **osymhp;
int seg = ref & (DATASEG|TEXTSEG);
symcount++;
osymhp = symhp = &symhash[addr % SYMHS];
while (symtp = *symhp) {
if (++symhp >= &symhash[SYMHS])
symhp = symhash;
if (symhp == osymhp)
fatal("Symbol table overflow");
if (addr == symtp->symval && (seg & symtp->symflags))
return symtp;
/* fprintf(stderr, "Multiple definitions of symbol %s addr=%x, seg=%d, ref=%d\n", id ? id : "", addr, seg, ref); */
}
symtp = (struct symtab *)malloc(sizeof(struct symtab));
*symhp = symtp;
symtp->symflags = ref;
symtp->symval = addr;
symtp->symrefcount = 0;
if (id)
symtp->symstring = strdup(id);
else {
symtp->symstring = NULL;
symtp->symflags |= GENERATED;
}
return symtp;
}
char *
symname(symp)
struct symtab *symp;
{
static char id[20];
if (symp->symstring)
return symp->symstring;
sprintf(id, "%s0%x", symp->symflags & TEXTSEG ?
(symp->symflags & CALLREF ? "P_" : "L") :
symp->symflags & DATASEG ? "D" : "X", symp->symval);
return id;
}
char *
getimmed(val)
unsigned int val;
{
static char scratch[20];
register struct symtab *symp;
if (*start_codep == 0x68)
sprintf(scratch, "$%s", datasymbol(val, PUSHLIMMED, 0));
else
sprintf(scratch, "$%s", datasymbol(val, 0, 0));
return scratch;
}
char *
datasymbol(addr, ref, hexdef)
unsigned int addr;
{
register struct symtab *symp;
static char scratch[20];
if (addr == 0) {
return "0";
}
if (addr >= database && addr < database + xoutx.x_data + xoutx.x_bss) {
symp = lookup(addr, DATASEG|ref);
if (pass2) {
if (symp != NULL) {
if (symp->symflags & ASCIZ)
return ascizdata(addr);
if (symp->symstring)
return symp->symstring;
}
sprintf(scratch, "D_0%x", addr);
return scratch;
}
}
if (addr < 10 /* || !hexdef */)
sprintf(scratch, "%d", addr);
else
sprintf(scratch, "%#x", addr);
return scratch;
}
scomp(syp1, syp2)
register struct symtab **syp1, **syp2;
{
if ((*syp1)->symval < (*syp2)->symval)
return -1;
else if ((*syp1)->symval > (*syp2)->symval)
return 1;
return 0;
}
symtabpass()
{
struct symtab *symarr[SYMHS];
register int i, j;
int labno;
register struct symtab *symp;
char id[20];
int syc;
/*
* Look for "case" jump tables
*/
for (i = 0; i < SYMHS; i++) {
if ((symp = symhash[i]) && symp->symrefcount == 1 && symp->symflags & (WORDREF|DWORDREF) && !(symp->symflags & (CALLREF|JUMPREF))) {
unsigned int addr = symp->symval;
unsigned int disp;
register struct symtab *labp;
while (addr < textsize) {
register int j;
for (j = 1; j < 4; j++)
if (lookup(addr+j, NOENTER))
goto bloop;
disp = *(unsigned int *)(textp+addr);
if (disp > 0 && disp < textsize)
labp = lookup(disp, TEXTSEG|JUMPREF);
addr += 4;
}
bloop: ;
}
}
syc = 0;
for (i = 0; i < SYMHS; i++)
if (symarr[syc] = symhash[i])
syc++;
qsort(symarr, syc, sizeof(struct symtab *), scomp);
printf("%d symbols defined\n", syc);
/*
* Assign label names for code labels
*/
labno = 0;
for (i = 0; i < syc; i++) {
if ((symp = symarr[i]) && symp->symstring == NULL
&& symp->symflags & TEXTSEG) {
sprintf(id, "L%d", ++labno);
symp->symstring = strdup(id);
}
}
/*
* Look for asciz strings
*/
for (i = 0; i < syc; i++) {
unsigned addr1, addr2;
if ((symp = symarr[i]) == NULL)
continue;
if (symp->symflags & DATASEG && symp->symflags & PUSHLIMMED
&& symp->symrefcount == 1) {
for (j = i+1; j < syc; j++)
if (symarr[j] && symarr[j]->symflags & DATASEG)
break;
if (j == syc)
break;
addr1 = symp->symval;
addr2 = symarr[j]->symval;
if (addr2 > database + xoutx.x_data
|| addr1 < database)
continue;
for (j = addr1; j < addr2; j++)
if (datap[j - database] == '\0')
break;
dummy();
if (j < addr2 && ((j + 4) & ~3) == addr2)
/* if (j == addr2-1 || j == addr2-2 || j == addr2-3) */
symarr[i]->symflags |= ASCIZ;
}
}
/*
* Assign label names for data labels
*/
labno = 0;
for (i = 0; i < syc; i++) {
if ((symp = symarr[i]) && symp->symstring == NULL
&& symp->symflags & DATASEG) {
sprintf(id, "D%d", ++labno);
symp->symstring = strdup(id);
}
}
}
dummy() {}
char *
ascizdata(addr)
{
register unsigned char *cp1 = datap + addr - database;
static char buf[1000];
register unsigned char *cp2 = buf;
register c;
*cp2++ = '"';
while (c = *cp1++) {
switch (c) {
case '\n': *cp2++ = '\\'; *cp2++ = 'n'; break;
case '\t': *cp2++ = '\\'; *cp2++ = 't'; break;
case '\r': *cp2++ = '\\'; *cp2++ = 'r'; break;
default:
if (c >= ' ' && c < 127)
*cp2++ = c;
else {
*cp2++ = '\\';
if (c & 0300) *cp2++ = (c >> 6) + '0';
if (c & 0070) *cp2++ = ((c >> 3) & 7) + '0';
*cp2++ = (c & 7) + '\0';
}
}
if (cp2 >= buf+sizeof(buf)-2)
fatal("Too long asciz string");
}
*cp2++ = '"';
*cp2++ = '\0';
return buf;
}