minix/commands/dis386/misc.c
Ben Gras 98ddbffe6e dis386 - a disassembler for ack
- it can disassemble object files (dis386o) and executables
    (dis386a)
  - only useful for as long as we still have ack
2010-10-04 13:26:53 +00:00

938 lines
20 KiB
C

/*
* misc.c: interface to Bruce Evan's dis86 package.
*
* $Id: misc.c,v 1.1 1997/10/20 12:00:00 cwr Exp cwr $
*
* Heavily modified by C W Rose.
*/
/* Version settings */
#define MINIX
#undef OS2
#undef TEST
#include <sys/types.h>
#ifdef MINIX
#include <minix/config.h>
#include <minix/const.h>
#include <a.out.h>
#endif
#ifdef OS2
typedef unsigned char u8_t;
typedef unsigned int u16_t;
typedef unsigned long u32_t;
#include </local/minix/minix/config.h>
#include </local/minix/minix/const.h>
#include </local/minix/a.out.h>
#endif
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "const.h"
#include "type.h"
#undef S_ABS /* clash with a.out.h */
#include "out.h" /* ACK compiler output header */
#include "var.h" /* db header */
#include "dis386.h" /* dis386 package header */
/* Standard defines */
#define FAILED -1
#define MAYBE 0
#define OK 1
/* Local defines */
#ifndef lint
static char *Version = "@(#) misc.c $Revision: 1.1 $ $Date: 1997/10/20 12:00:00 $";
#endif
/* Global variables */
PRIVATE bool_t forceupper;
PRIVATE bool_t someupper = TRUE;
PRIVATE count_t stringcount = 0;
PRIVATE char *string_ptr = (char *)0; /* stringptr ambiguous at 8th char */
PRIVATE char *stringstart = (char *)0;
/* Externals */
/* Forward declarations */
#if 0
PUBLIC void closestring(void); /* */
PUBLIC u8_pt get8(void); /* */
PUBLIC u16_t get16(void); /* */
PUBLIC u32_t get32(void); /* */
PUBLIC void openstring(char *string, int len); /* */
PUBLIC void outbyte(char_pt byte); /* */
PUBLIC void outcolon(void); /* */
PUBLIC void outcomma(void); /* */
PUBLIC void outh4(u4_pt num); /* */
PUBLIC void outh8(u8_pt num); /* */
PUBLIC void outh16(u16_t num); /* */
PUBLIC void outh32(u32_t num); /* */
PUBLIC bool_pt outnl(void); /* */
PUBLIC count_t outsegaddr(struct address_s *ap, offset_t addr); /* */
PUBLIC count_t outsegreg(offset_t num); /* */
PUBLIC void outspace(void); /* */
PUBLIC void outstr(char *s); /* */
PUBLIC void outtab(void); /* */
PUBLIC void outustr(char *s); /* */
PUBLIC count_t stringpos(void); /* */
PUBLIC count_t stringtab(void); /* */
PUBLIC void outrel(struct nlist *sp, offset_t off); /* */
PUBLIC void outsym(struct nlist *sp, offset_t off); /* */
PUBLIC struct nlist *findrval(offset_t value, int where);/* */
PUBLIC struct nlist *findsval(offset_t value, int where);/* */
PUBLIC int dasm(offset_t addr, offset_t count); /* */
#endif
PRIVATE u8_pt peek8(struct address_s *ap); /* */
PRIVATE u16_t peek16(struct address_s *ap); /* */
PRIVATE u32_t peek32(struct address_s *ap); /* */
PRIVATE struct nlist *find_arval(offset_t value, int where); /* */
PRIVATE struct nlist *find_orval(offset_t value, int where); /* */
PRIVATE struct nlist *find_asval(offset_t value, int where); /* */
PRIVATE struct nlist *find_osval(offset_t value, int where); /* */
PRIVATE int dis_one(void); /* */
/*
* Close string device.
*/
PUBLIC void closestring()
{
stringcount = 0;
stringstart = string_ptr = (char *)0;
}
/*
* Get 8 bits from current instruction pointer and advance pointer.
*/
PUBLIC u8_pt get8()
{
u8_pt temp;
temp = peek8(&uptr);
++uptr.off;
return temp;
}
/*
* Get 16 bits from current instruction pointer and advance pointer.
*/
PUBLIC u16_pt get16()
{
u16_pt temp;
temp = peek16(&uptr);
uptr.off += 2;
return temp;
}
/*
* Get 32 bits from current instruction pointer and advance pointer.
*/
PUBLIC u32_t get32()
{
u32_t temp;
temp = peek32(&uptr);
uptr.off += 4;
return temp;
}
/*
* Open string device.
*/
PUBLIC void openstring(string, len)
char *string; int len;
{
while (--len >= 0)
string[len] = '\0';
stringcount = 0;
stringstart = string_ptr = string;
}
/*
* Print char to currently open output devices.
*/
PUBLIC void outbyte(char_pt byte)
{
/* convert to upper case if required */
if (forceupper && byte >= 'a' && byte <= 'z')
byte += 'A' - 'a';
/* increment the output line character count, allowing for tab stops */
if (string_ptr != NULL) {
if ((*string_ptr++ = byte) == '\t')
stringcount = 8 * (stringcount / 8 + 1);
else
++stringcount;
}
else
(void) fputc(byte, stdout);
}
/*
* Print colon.
*/
PUBLIC void outcolon()
{
outbyte(':');
}
/*
* Print comma.
*/
PUBLIC void outcomma()
{
outbyte(',');
}
/*
* Print 4 bits hex.
*/
PUBLIC void outh4(u4_pt num)
{
static char hexdigits[] = "0123456789abcdef";
forceupper = someupper;
outbyte(hexdigits[num % 16]);
forceupper = FALSE;
}
/*
* Print 8 bits hex.
*/
PUBLIC void outh8(u8_pt num)
{
outh4(num / 16);
outh4(num);
}
/*
* Print 16 bits hex.
*/
PUBLIC void outh16(u16_pt num)
{
outh8(num / 256);
outh8(num);
}
/*
* Print 32 bits hex.
*/
PUBLIC void outh32(u32_t num)
{
outh16((u16_t) (num >> 16));
outh16((u16_t) num);
}
/*
* Print newline.
*/
PUBLIC bool_pt outnl()
{
/* bool_pt avoids change in type.h */
outstr("\n");
return OK;
}
/*
* Print segmented address.
*/
PUBLIC count_t outsegaddr(struct address_s *ap, offset_t addr)
{
count_t bytes_printed;
bytes_printed = 2;
if (ap->base == regs.csbase)
outustr("cs");
else if (ap->base == regs.dsbase)
outustr("ds");
else if (ap->base == regs.esbase)
outustr("es");
else if (processor >= 386 && ap->base == regs.fsbase)
outustr("fs");
else if (processor >= 386 && ap->base == regs.gsbase)
outustr("gs");
else if (ap->base == regs.ssbase)
outustr("ss");
else
bytes_printed = outsegreg(ap->base);
if (bytes_printed > 4)
outbyte('+');
else
outcolon();
bytes_printed++;
if (ap->off >= 0x10000) {
outh32(ap->off + addr);
return bytes_printed + 8;
}
else {
outh16((u16_pt) ap->off + addr);
return bytes_printed + 4;
}
}
/*
* Print segment register.
*/
PUBLIC count_t outsegreg(offset_t num)
{
if ((num % HCLICK_SIZE) != 0 || num >= 0x100000) {
outh32(num);
return 8;
}
outh16((u16_pt) (num / HCLICK_SIZE));
return 4;
}
/*
* Print space.
*/
PUBLIC void outspace()
{
outbyte(' ');
}
/*
* Print string.
*/
PUBLIC void outstr(char *s)
{
while (*s)
outbyte(*s++);
}
/*
* Print tab.
*/
PUBLIC void outtab()
{
outbyte('\t');
}
/*
* Print string, perhaps converting case to upper.
*/
PUBLIC void outustr(char *s)
{
forceupper = someupper;
while (*s)
outbyte(*s++);
forceupper = FALSE;
}
/*
* p e e k 8
*
* Get a byte from the process.
*
* Returns: byte Success
*
* Note: aborts on read error.
*/
PRIVATE u8_pt peek8(struct address_s *ap)
{
unsigned int uj;
/* with luck buffering should make this fairly quick */
if (fseek(disfp, (long)(ap->off), SEEK_CUR) != 0) {
fprintf(stderr, "Cannot seek forward in object file\n");
exit(1);
}
uj = fgetc(disfp) & 0377;
if (fseek(disfp, -(long)(ap->off + 1), SEEK_CUR) != 0) {
fprintf(stderr, "Cannot seek backward in object file\n");
exit(1);
}
return uj;
}
/*
* p e e k 1 6
*
* Get a 16-bit short from the process.
*
* Returns: 2 bytes Success
*
* Note: aborts on read error.
*/
PRIVATE u16_t peek16(struct address_s *ap)
{
unsigned int uj;
/* with luck buffering should make this fairly quick */
if (fseek(disfp, (long)(ap->off), SEEK_CUR) != 0) {
fprintf(stderr, "Cannot seek forward in object file\n");
exit(1);
}
/* Intel has right to left byte ordering */
#if 1
uj = fgetc(disfp) & 0377;
uj |= (fgetc(disfp) & 0377) << 8;
#else
uj = fgetc(disfp) & 0377;
uj <<= 8;
uj |= fgetc(disfp) & 0377;
#endif
if (fseek(disfp, -(long)(ap->off + 2), SEEK_CUR) != 0) {
fprintf(stderr, "Cannot seek backward in object file\n");
exit(1);
}
return uj;
}
/*
* p e e k 3 2
*
* Get a 32-bit int from the process.
*
* Returns: 4 bytes Success
*
* Note: aborts on read error.
*/
PRIVATE u32_t peek32(struct address_s *ap)
{
unsigned int uj;
/* with luck buffering should make this fairly quick */
if (fseek(disfp, (long)(ap->off), SEEK_CUR) != 0) {
fprintf(stderr, "Cannot seek forward in object file\n");
exit(1);
}
#if 1
/* Intel has right to left byte ordering */
uj = fgetc(disfp) & 0377;
uj |= (fgetc(disfp) & 0377) << 8;
uj |= (fgetc(disfp) & 0377) << 16;
uj |= (fgetc(disfp) & 0377) << 24;
#else
uj = fgetc(disfp) & 0377;
uj <<= 8;
uj |= fgetc(disfp) & 0377;
uj <<= 8;
uj |= fgetc(disfp) & 0377;
uj <<= 8;
uj |= fgetc(disfp) & 0377;
#endif
if (fseek(disfp, -(long)(ap->off + 4), SEEK_CUR) != 0) {
fprintf(stderr, "Cannot seek backward in object file\n");
exit(1);
}
return uj;
}
/*
* Return current offset of string device.
*/
PUBLIC count_t stringpos()
{
return string_ptr - stringstart;
}
/*
* Return current "tab" spot of string device.
*/
PUBLIC count_t stringtab()
{
return stringcount;
}
/******************** sym.c ***********************/
/*
* f i n d r v a l
*
* Check if an address refers to a relocation structure,
* and if so return the table entry.
*
* Returns: Pointer to struct nlist Success
* Null pointer Failure
*
* Note that the nlist interface must be maintained for use by unasm().
*/
PUBLIC struct nlist *findrval(offset_t value, int where)
{
if (aoutfp != (FILE *)NULL)
return (find_arval(value, where));
else if (objfp != (FILE *)NULL)
return (find_orval(value, where));
else
return (struct nlist *)NULL;
}
/*
* f i n d _ a r v a l
*
* Check if an address refers to an a.out file relocation structure,
* and if so return the table entry.
*
* Returns: Pointer to struct nlist Success
* Null pointer Failure
*
* Note that the nlist interface must be maintained for use by unasm().
* ### Do any available ACK compilers have this feature?
*/
PRIVATE struct nlist *find_arval(offset_t value, int where)
{
return (struct nlist *)NULL;
}
/*
* f i n d _ o r v a l
*
* Check if an address refers to an object file relocation structure,
* and if so return the table entry.
*
* Returns: Pointer to struct nlist Success
* Null pointer Failure
*
* Note that the nlist interface must be maintained for use by unasm().
* The table entry is stored in a static buffer which is overwritten
* on successive calls.
*/
PRIVATE struct nlist *find_orval(offset_t value, int where)
{
char data[20];
int j, k, status;
long int lj;
static struct nlist sym;
/* we need to have an object file */
if (objfp == (FILE *)NULL) return (struct nlist *)NULL;
/* Sections in an object file usually have the order text, rom, data, bss.
* The order is actually set out in the section data header. Assume that
* the first user section is text, and all else is data.
*/
if (where != CSEG && where != DSEG)
return(struct nlist *)NULL;
/* check for a relocation entry */
status = FAILED;
for (j = 0 ; j < o_hdrbuf.oh_nrelo ; j++) {
if (value == o_reltab[j].or_addr) {
/* abandon non-matching section entries */
if (where == CSEG && (o_reltab[j].or_sect & S_TYP) != S_MIN)
continue;
if (where == DSEG && ((o_reltab[j].or_sect & S_TYP) <= S_MIN ||
(o_reltab[j].or_sect & S_TYP) > (S_MIN + 3)))
continue;
/* the address is an offset from the symbol or section base */
if (o_reltab[j].or_nami < o_hdrbuf.oh_nname) {
lj = o_symtab[o_reltab[j].or_nami].on_foff -
(long)OFF_CHAR(o_hdrbuf);
/* check that addressing isn't messed up */
assert(lj >= 0 && lj < o_hdrbuf.oh_nchar);
/* name size is defined by SZ_NAME */
sprintf(data, "%-13s", o_strtab + lj);
/* convert from rel table to executable symbol table format */
for (k = 0 ; k < sizeof(sym.n_name) ; k++) {
sym.n_name[k] = data[k];/* 8 characters */
}
sym.n_value = o_symtab[o_reltab[j].or_nami].on_valu;
/* long */
#if 1
sym.n_sclass = (where == CSEG) ? N_TEXT : N_DATA;
#else
sym.n_sclass = (o_symtab[o_reltab[j].or_nami].on_type &
S_TYP) - S_MIN; /* unsigned char */
#endif
sym.n_numaux = 0; /* unsigned char */
sym.n_type = 0; /* unsigned short */
status = OK;
break;
}
/* the address is an absolute number relative to the pc */
else if (o_reltab[j].or_nami == o_hdrbuf.oh_nname) {
strcpy(data, "Absolute");
/* convert from relocation data to executable symbol table format */
for (k = 0 ; k < sizeof(sym.n_name) ; k++) {
sym.n_name[k] = data[k];
}
sym.n_value = 0;
sym.n_sclass = (where == CSEG) ? N_TEXT : N_DATA;
sym.n_numaux = 0;
sym.n_type = 0;
status = OK;
break;
}
}
}
return (status == OK ? &sym : (struct nlist *)NULL);
}
/*
* f i n d s v a l
*
* Check if an address refers to a symbol,
* and if so return the table entry.
*
* Returns: Pointer to struct nlist Success
* Null pointer Failure
*
* Note that the nlist interface must be maintained for use by unasm().
*/
PUBLIC struct nlist *findsval(offset_t value, int where)
{
if (aoutfp != (FILE *)NULL)
return (find_asval(value, where));
else if (objfp != (FILE *)NULL)
return (find_osval(value, where));
else
return (struct nlist *)NULL;
}
/*
* f i n d _ a s v a l
*
* Check if an address refers to an a.out file symbol,
* and if so return the table entry.
*
* Returns: Pointer to struct nlist Success
* Null pointer Failure
*
* Note that the nlist interface must be maintained for use by unasm().
* The table entry is stored in a static buffer which is overwritten
* on successive calls.
*/
PRIVATE struct nlist *find_asval(offset_t value, int where)
{
int j, status;
static struct nlist sym;
/* Sections in an a.out file have the order text, data, bss
* but this function is called only with CSEG and DSEG.
*/
if (where != CSEG && where != DSEG)
return(struct nlist *)NULL;
/* do a linear search for a symbol, as the symbol tables are unsorted */
status = FAILED;
for (j = 0 ; j < (a_hdrbuf.a_syms / sizeof(struct nlist)) ; j++) {
if (value == a_symtab[j].n_value &&
((where == CSEG && (a_symtab[j].n_sclass & N_SECT) == N_TEXT) ||
(where == DSEG && ((a_symtab[j].n_sclass & N_SECT) == N_DATA ||
(a_symtab[j].n_sclass & N_SECT) == N_BSS)))) {
(void) memcpy(&sym, &a_symtab[j], sizeof(struct nlist));
status = OK;
break;
}
}
return (status == OK) ? &sym : (struct nlist *)NULL;
}
/*
* f i n d _ o s v a l
*
* Check if an address refers to an object file symbol,
* and if so return the table entry.
*
* Returns: Pointer to struct nlist Success
* Null pointer Failure
*
* Note that the nlist interface must be maintained for use by unasm().
* The table entry is stored in a static buffer which is overwritten
* on successive calls.
*/
PRIVATE struct nlist *find_osval(offset_t value, int where)
{
int j, k, sec, status;
long int lj;
struct locname *np;
static struct nlist sym;
/* Sections in an object file usually have the order text, rom, data, bss.
* The order is actually set out in the section data header. Assume that
* the first user section is text, and all else is data.
*/
if (where != CSEG && where != DSEG)
return(struct nlist *)NULL;
/* do a linear search for a local symbol, as the tables are unsorted */
status = FAILED;
if (where == DSEG) {
/* nb. hardcoded assumption of section order */
for (sec = 1 ; status == FAILED && sec < 4 ; sec++) {
for (np = locsym[sec] ; status == FAILED && np !=
(struct locname *)NULL ; np = np->l_next) {
if (np->l_value == value) {
for (k = 0 ; k < sizeof(sym.n_name) ; k++) {
sym.n_name[k] = np->l_name[k];/* 8 characters */
}
sym.n_value = value; /* long */
sym.n_sclass = N_DATA; /* unsigned char */
sym.n_numaux = 0; /* unsigned char */
sym.n_type = 0; /* unsigned short */
status = OK;
}
}
}
}
/* do a linear search for a symbol, as the symbol tables are unsorted */
for (j = 0 ; status == FAILED && j < o_hdrbuf.oh_nname ; j++) {
if (value == o_symtab[j].on_valu) {
/* abandon non-matching section entries */
if (where == CSEG && (o_symtab[j].on_type & S_TYP) != S_MIN)
continue;
if (where == DSEG && ((o_symtab[j].on_type & S_TYP) <= S_MIN ||
(o_symtab[j].on_type & S_TYP) > (S_MIN + 3)))
continue;
#if 0
((where == CSEG && sect == (o_symtab[j].on_type & S_TYP)) ||
(where == DSEG && sect <= (o_symtab[j].on_type & S_TYP)))) {
#endif
/* find the name in the object file symbol table */
lj = o_symtab[j].on_foff - (long)OFF_CHAR(o_hdrbuf);
/* check that the offset addressing isn't messed up */
assert(lj >= 0 && lj < o_hdrbuf.oh_nchar);
/* convert from object to executable symbol table format */
for (k = 0 ; k < sizeof(sym.n_name) ; k++) {
sym.n_name[k] = *(o_strtab + lj + k);
/* 8 characters */
}
sym.n_value = o_symtab[j].on_valu; /* long */
sym.n_sclass = (where == CSEG) ? N_TEXT : N_DATA;
/* unsigned char */
sym.n_numaux = 0; /* unsigned char */
sym.n_type = 0; /* unsigned short */
status = OK;
}
}
return (status == OK ? &sym : (struct nlist *)NULL);
}
/*
* o u t r e l
*
* Output a symbol name from an nlist structure.
*
* Returns: Nothing Always
*
* Note that the nlist interface must be maintained for use by unasm().
* The label may be a segment name, in which case the address is relative
* to that segment and must be dereferenced further.
*/
PUBLIC void outrel(struct nlist *sp, offset_t off)
{
char data[20];
int j, k;
struct nlist *spnew;
/* get a local copy of the label */
for (j = 0 ; j < 20 ; j++) {
data[j] = sp->n_name[j];
if (data[j] == ' ' || data[j] == '\0')
break;
}
data[j] = '\0';
data[8] = '\0';
/* see if we have a section name */
for (k = 0 ; k < 4 ; k++) {
if (strcmp(data, o_secnam[k]) == 0) {
/* look up the name in the appropriate section */
if ((spnew = findsval(off, (k ? DSEG : CSEG))) != (struct nlist *)NULL) {
/* get a local copy of the label */
for (j = 0 ; j < 20 ; j++) {
data[j] = spnew->n_name[j];
if (data[j] == '\0') break;
}
data[8] = '\0';
}
}
}
/* output the result */
for (j = 0 ; data[j] != 0 ; j++)
outbyte(data[j]);
}
/*
* o u t s y m
*
* Output a symbol name from an nlist structure.
*
* Returns: Nothing Always
*
* Note that the nlist interface must be maintained for use by unasm().
*/
PUBLIC void outsym(struct nlist *sp, offset_t off)
{
char *s;
char *send;
/* output the symbol name */
for (s = sp->n_name, send = s + sizeof sp->n_name; *s != 0 && s < send; ++s)
outbyte(*s);
/* if the required address is offset from the name, output that too */
if ((off -= sp->n_value) != 0) {
outbyte('+');
if (off >= 0x10000)
outh32(off);
else if (off >= 0x100)
outh16((u16_pt) off);
else
outh8((u8_pt) off);
}
}
/*
* d a s m
*
* Disassemble a stream of instructions.
*
* Returns: OK Success
* FAILED Otherwise
*/
PUBLIC int dasm(offset_t addr, offset_t count)
{
#if (_WORD_SIZE == 4)
bits32 = TRUE; /* set mode */
#else
bits32 = FALSE;
#endif
processor = bits32 ? 386 : 0;
uptr.off = 0;
uptr.base = 0;
while (uptr.off < count) {
addrbase = addr;
/* assume that the object file text segment is first */
if (objfp != (FILE *)NULL && uptr.off >= o_sectab[0].os_flen)
return FAILED;
if (aoutfp != (FILE *)NULL && uptr.off >= (A_DATAPOS(a_hdrbuf) - 1))
return FAILED;
if (dis_one() == FAILED)
return FAILED;
}
return OK;
}
/*
* d i s _ o n e
*
* Disassemble a single instruction.
*
* Returns: OK Always
*
* File read failures are handled at a low level by simply
* baling out of the program; the startup checks on file
* readability should make this a rare occurrence. Hence
* there are no error returns from this routine.
* The output is written into a static line buffer, which
* is overwritten on successive calls.
*/
PRIVATE int dis_one()
{
int idone, column, maxcol;
static char line[81];
struct address_s newuptr;
struct address_s olduptr;
struct nlist *sp;
do {
/* output a label */
if ((sp = findsval(uptr.off + addrbase, CSEG)) != NULL
&& sp->n_value == uptr.off + addrbase) {
outsym(sp, uptr.off + addrbase);
outbyte(':');
(void) outnl();
}
/* park the current address */
olduptr = uptr;
/* initialise the string input */
openstring(line, sizeof(line));
/* output an instruction */
idone = puti();
/* terminate the line buffer */
line[stringpos()] = 0;
/* deinitialise the string input */
closestring();
/* park the new address, set by puti() */
newuptr = uptr;
/* get back the current address */
uptr = olduptr;
/* output the segment data */
column = outsegaddr(&uptr, addrbase);
outspace();
outspace();
column += 2;
/* output the raw bytes of the current instruction */
while (uptr.off != newuptr.off) {
outh8(get8());
column += 2;
}
/* format the disassembled output */
maxcol = bits32 ? 24 : 16;
while (column < maxcol) {
outtab();
column += 8;
}
outtab();
/* display the collected buffer */
outstr(line);
(void) outnl();
} while (!idone); /* eat all prefixes */
return OK;
}
/*
* EOF
*/