mirror of
https://github.com/netwide-assembler/nasm.git
synced 2024-11-27 08:10:07 +08:00
fe501957c0
Concentrate compiler dependencies to compiler.h; make sure compiler.h is included first in every .c file (since some prototypes may depend on the presence of feature request macros.) Actually use the conditional inclusion of various functions (totally broken in previous releases.)
359 lines
12 KiB
C
359 lines
12 KiB
C
/* ndisasm.c the Netwide Disassembler main module
|
|
*
|
|
* The Netwide Assembler is copyright (C) 1996 Simon Tatham and
|
|
* Julian Hall. All rights reserved. The software is
|
|
* redistributable under the licence given in the file "Licence"
|
|
* distributed in the NASM archive.
|
|
*/
|
|
|
|
#include "compiler.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <inttypes.h>
|
|
|
|
#include "insns.h"
|
|
#include "nasm.h"
|
|
#include "nasmlib.h"
|
|
#include "sync.h"
|
|
#include "disasm.h"
|
|
|
|
#define BPL 8 /* bytes per line of hex dump */
|
|
|
|
static const char *help =
|
|
"usage: ndisasm [-a] [-i] [-h] [-r] [-u] [-b bits] [-o origin] [-s sync...]\n"
|
|
" [-e bytes] [-k start,bytes] [-p vendor] file\n"
|
|
" -a or -i activates auto (intelligent) sync\n"
|
|
" -u same as -b 32\n"
|
|
" -b 16, -b 32 or -b 64 sets the processor mode\n"
|
|
" -h displays this text\n"
|
|
" -r or -v displays the version number\n"
|
|
" -e skips <bytes> bytes of header\n"
|
|
" -k avoids disassembling <bytes> bytes from position <start>\n"
|
|
" -p selects the preferred vendor instruction set (intel, amd, cyrix, idt)\n";
|
|
|
|
static void output_ins(uint32_t, uint8_t *, int, char *);
|
|
static void skip(uint32_t dist, FILE * fp);
|
|
|
|
static void ndisasm_error(int severity, const char *fmt, ...)
|
|
{
|
|
va_list va;
|
|
|
|
va_start(va, fmt);
|
|
vfprintf(stderr, fmt, va);
|
|
|
|
if (severity & ERR_FATAL)
|
|
exit(1);
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
char buffer[INSN_MAX * 2], *p, *ep, *q;
|
|
char outbuf[256];
|
|
char *pname = *argv;
|
|
char *filename = NULL;
|
|
uint32_t nextsync, synclen, initskip = 0L;
|
|
int lenread;
|
|
int32_t lendis;
|
|
int autosync = FALSE;
|
|
int bits = 16, b;
|
|
int eof = FALSE;
|
|
uint32_t prefer = 0;
|
|
int rn_error;
|
|
int32_t offset;
|
|
FILE *fp;
|
|
|
|
nasm_set_malloc_error(ndisasm_error);
|
|
|
|
offset = 0;
|
|
init_sync();
|
|
|
|
while (--argc) {
|
|
char *v, *vv, *p = *++argv;
|
|
if (*p == '-' && p[1]) {
|
|
p++;
|
|
while (*p)
|
|
switch (tolower(*p)) {
|
|
case 'a': /* auto or intelligent sync */
|
|
case 'i':
|
|
autosync = TRUE;
|
|
p++;
|
|
break;
|
|
case 'h':
|
|
fprintf(stderr, help);
|
|
return 0;
|
|
case 'r':
|
|
case 'v':
|
|
fprintf(stderr,
|
|
"NDISASM version %s compiled " __DATE__ "\n",
|
|
NASM_VER);
|
|
return 0;
|
|
case 'u': /* -u for -b 32, -uu for -b 64 */
|
|
if (bits < 64)
|
|
bits <<= 1;
|
|
p++;
|
|
break;
|
|
case 'b': /* bits */
|
|
v = p[1] ? p + 1 : --argc ? *++argv : NULL;
|
|
if (!v) {
|
|
fprintf(stderr, "%s: `-b' requires an argument\n",
|
|
pname);
|
|
return 1;
|
|
}
|
|
b = strtoul(v, &ep, 10);
|
|
if (*ep || !(bits == 16 || bits == 32 || bits == 64)) {
|
|
fprintf(stderr, "%s: argument to `-b' should"
|
|
" be 16, 32 or 64\n", pname);
|
|
} else {
|
|
bits = b;
|
|
}
|
|
p = ""; /* force to next argument */
|
|
break;
|
|
case 'o': /* origin */
|
|
v = p[1] ? p + 1 : --argc ? *++argv : NULL;
|
|
if (!v) {
|
|
fprintf(stderr, "%s: `-o' requires an argument\n",
|
|
pname);
|
|
return 1;
|
|
}
|
|
offset = readnum(v, &rn_error);
|
|
if (rn_error) {
|
|
fprintf(stderr,
|
|
"%s: `-o' requires a numeric argument\n",
|
|
pname);
|
|
return 1;
|
|
}
|
|
p = ""; /* force to next argument */
|
|
break;
|
|
case 's': /* sync point */
|
|
v = p[1] ? p + 1 : --argc ? *++argv : NULL;
|
|
if (!v) {
|
|
fprintf(stderr, "%s: `-s' requires an argument\n",
|
|
pname);
|
|
return 1;
|
|
}
|
|
add_sync(readnum(v, &rn_error), 0L);
|
|
if (rn_error) {
|
|
fprintf(stderr,
|
|
"%s: `-s' requires a numeric argument\n",
|
|
pname);
|
|
return 1;
|
|
}
|
|
p = ""; /* force to next argument */
|
|
break;
|
|
case 'e': /* skip a header */
|
|
v = p[1] ? p + 1 : --argc ? *++argv : NULL;
|
|
if (!v) {
|
|
fprintf(stderr, "%s: `-e' requires an argument\n",
|
|
pname);
|
|
return 1;
|
|
}
|
|
initskip = readnum(v, &rn_error);
|
|
if (rn_error) {
|
|
fprintf(stderr,
|
|
"%s: `-e' requires a numeric argument\n",
|
|
pname);
|
|
return 1;
|
|
}
|
|
p = ""; /* force to next argument */
|
|
break;
|
|
case 'k': /* skip a region */
|
|
v = p[1] ? p + 1 : --argc ? *++argv : NULL;
|
|
if (!v) {
|
|
fprintf(stderr, "%s: `-k' requires an argument\n",
|
|
pname);
|
|
return 1;
|
|
}
|
|
vv = strchr(v, ',');
|
|
if (!vv) {
|
|
fprintf(stderr,
|
|
"%s: `-k' requires two numbers separated"
|
|
" by a comma\n", pname);
|
|
return 1;
|
|
}
|
|
*vv++ = '\0';
|
|
nextsync = readnum(v, &rn_error);
|
|
if (rn_error) {
|
|
fprintf(stderr,
|
|
"%s: `-k' requires numeric arguments\n",
|
|
pname);
|
|
return 1;
|
|
}
|
|
synclen = readnum(vv, &rn_error);
|
|
if (rn_error) {
|
|
fprintf(stderr,
|
|
"%s: `-k' requires numeric arguments\n",
|
|
pname);
|
|
return 1;
|
|
}
|
|
add_sync(nextsync, synclen);
|
|
p = ""; /* force to next argument */
|
|
break;
|
|
case 'p': /* preferred vendor */
|
|
v = p[1] ? p + 1 : --argc ? *++argv : NULL;
|
|
if (!v) {
|
|
fprintf(stderr, "%s: `-p' requires an argument\n",
|
|
pname);
|
|
return 1;
|
|
}
|
|
if (!strcmp(v, "intel")) {
|
|
prefer = 0; /* Default */
|
|
} else if (!strcmp(v, "amd")) {
|
|
prefer = IF_AMD | IF_3DNOW;
|
|
} else if (!strcmp(v, "cyrix")) {
|
|
prefer = IF_CYRIX | IF_3DNOW;
|
|
} else if (!strcmp(v, "idt") || !strcmp(v, "centaur")
|
|
|| !strcmp(v, "winchip")) {
|
|
prefer = IF_3DNOW;
|
|
} else {
|
|
fprintf(stderr,
|
|
"%s: unknown vendor `%s' specified with `-p'\n",
|
|
pname, v);
|
|
return 1;
|
|
}
|
|
p = ""; /* force to next argument */
|
|
break;
|
|
default: /*bf */
|
|
fprintf(stderr, "%s: unrecognised option `-%c'\n",
|
|
pname, *p);
|
|
return 1;
|
|
}
|
|
} else if (!filename) {
|
|
filename = p;
|
|
} else {
|
|
fprintf(stderr, "%s: more than one filename specified\n",
|
|
pname);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (!filename) {
|
|
fprintf(stderr, help, pname);
|
|
return 0;
|
|
}
|
|
|
|
if (strcmp(filename, "-")) {
|
|
fp = fopen(filename, "rb");
|
|
if (!fp) {
|
|
fprintf(stderr, "%s: unable to open `%s': %s\n",
|
|
pname, filename, strerror(errno));
|
|
return 1;
|
|
}
|
|
} else
|
|
fp = stdin;
|
|
|
|
if (initskip > 0)
|
|
skip(initskip, fp);
|
|
|
|
/*
|
|
* This main loop is really horrible, and wants rewriting with
|
|
* an axe. It'll stay the way it is for a while though, until I
|
|
* find the energy...
|
|
*/
|
|
|
|
p = q = buffer;
|
|
nextsync = next_sync(offset, &synclen);
|
|
do {
|
|
uint32_t to_read = buffer + sizeof(buffer) - p;
|
|
if (to_read > nextsync - offset - (p - q))
|
|
to_read = nextsync - offset - (p - q);
|
|
if (to_read) {
|
|
lenread = fread(p, 1, to_read, fp);
|
|
if (lenread == 0)
|
|
eof = TRUE; /* help along systems with bad feof */
|
|
} else
|
|
lenread = 0;
|
|
p += lenread;
|
|
if ((uint32_t)offset == nextsync) {
|
|
if (synclen) {
|
|
fprintf(stdout, "%08"PRIX32" skipping 0x%"PRIX32" bytes\n", offset, synclen);
|
|
offset += synclen;
|
|
skip(synclen, fp);
|
|
}
|
|
p = q = buffer;
|
|
nextsync = next_sync(offset, &synclen);
|
|
}
|
|
while (p > q && (p - q >= INSN_MAX || lenread == 0)) {
|
|
lendis =
|
|
disasm(q, outbuf, sizeof(outbuf), bits, offset, autosync,
|
|
prefer);
|
|
if (!lendis || lendis > (p - q)
|
|
|| (uint32_t)lendis > nextsync - offset)
|
|
lendis = eatbyte(q, outbuf, sizeof(outbuf));
|
|
output_ins(offset, q, lendis, outbuf);
|
|
q += lendis;
|
|
offset += lendis;
|
|
}
|
|
if (q >= buffer + INSN_MAX) {
|
|
uint8_t *r = buffer, *s = q;
|
|
int count = p - q;
|
|
while (count--)
|
|
*r++ = *s++;
|
|
p -= (q - buffer);
|
|
q = buffer;
|
|
}
|
|
} while (lenread > 0 || !(eof || feof(fp)));
|
|
|
|
if (fp != stdin)
|
|
fclose(fp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void output_ins(uint32_t offset, uint8_t *data,
|
|
int datalen, char *insn)
|
|
{
|
|
int bytes;
|
|
fprintf(stdout, "%08"PRIX32" ", offset);
|
|
|
|
bytes = 0;
|
|
while (datalen > 0 && bytes < BPL) {
|
|
fprintf(stdout, "%02X", *data++);
|
|
bytes++;
|
|
datalen--;
|
|
}
|
|
|
|
fprintf(stdout, "%*s%s\n", (BPL + 1 - bytes) * 2, "", insn);
|
|
|
|
while (datalen > 0) {
|
|
fprintf(stdout, " -");
|
|
bytes = 0;
|
|
while (datalen > 0 && bytes < BPL) {
|
|
fprintf(stdout, "%02X", *data++);
|
|
bytes++;
|
|
datalen--;
|
|
}
|
|
fprintf(stdout, "\n");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Skip a certain amount of data in a file, either by seeking if
|
|
* possible, or if that fails then by reading and discarding.
|
|
*/
|
|
static void skip(uint32_t dist, FILE * fp)
|
|
{
|
|
char buffer[256]; /* should fit on most stacks :-) */
|
|
|
|
/*
|
|
* Got to be careful with fseek: at least one fseek I've tried
|
|
* doesn't approve of SEEK_CUR. So I'll use SEEK_SET and
|
|
* ftell... horrible but apparently necessary.
|
|
*/
|
|
if (fseek(fp, dist + ftell(fp), SEEK_SET)) {
|
|
while (dist > 0) {
|
|
uint32_t len = (dist < sizeof(buffer) ?
|
|
dist : sizeof(buffer));
|
|
if (fread(buffer, 1, len, fp) < len) {
|
|
perror("fread");
|
|
exit(1);
|
|
}
|
|
dist -= len;
|
|
}
|
|
}
|
|
}
|