added buffer length parameter to prevent vulnerability to buffer
[platform/upstream/nasm.git] / ndisasm.c
1 /* ndisasm.c   the Netwide Disassembler main module
2  *
3  * The Netwide Assembler is copyright (C) 1996 Simon Tatham and
4  * Julian Hall. All rights reserved. The software is
5  * redistributable under the licence given in the file "Licence"
6  * distributed in the NASM archive.
7  */
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <ctype.h>
13 #include <errno.h>
14
15 #include "insns.h"
16 #include "nasm.h"
17 #include "nasmlib.h"
18 #include "sync.h"
19 #include "disasm.h"
20
21 #define BPL 8                          /* bytes per line of hex dump */
22
23 static const char *help =
24 "usage: ndisasm [-a] [-i] [-h] [-r] [-u] [-b bits] [-o origin] [-s sync...]\n"
25 "               [-e bytes] [-k start,bytes] [-p vendor] file\n"
26 "   -a or -i activates auto (intelligent) sync\n"
27 "   -u sets USE32 (32-bit mode)\n"
28 "   -b 16 or -b 32 sets number of bits too\n"
29 "   -h displays this text\n"
30 "   -r or -v displays the version number\n"
31 "   -e skips <bytes> bytes of header\n"
32 "   -k avoids disassembling <bytes> bytes from position <start>\n"
33 "   -p selects the preferred vendor instruction set (intel, amd, cyrix, idt)\n";
34
35 static void output_ins (unsigned long, unsigned char *, int, char *);
36 static void skip (unsigned long dist, FILE *fp);
37
38 int main(int argc, char **argv) 
39 {
40     unsigned char buffer[INSN_MAX * 2], *p, *q;
41     char outbuf[256];
42     char *pname = *argv;
43     char *filename = NULL;
44     unsigned long nextsync, synclen, initskip = 0L;
45     int lenread;
46     long lendis;
47     int autosync = FALSE;
48     int bits = 16;
49     int eof = FALSE;
50     unsigned long prefer = 0;
51     int rn_error;
52     long offset;
53     FILE *fp;
54
55     offset = 0;
56     init_sync();
57
58     while (--argc) {
59         char *v, *vv, *p = *++argv;
60         if (*p == '-' && p[1]) {
61             p++;
62             while (*p) switch (tolower(*p)) {
63               case 'a':                /* auto or intelligent sync */
64               case 'i':
65                 autosync = TRUE;
66                 p++;
67                 break;
68               case 'h':
69                 fprintf(stderr, help);
70                 return 0;
71               case 'r':
72               case 'v':
73                 fprintf(stderr, "NDISASM version %s compiled " __DATE__ "\n", NASM_VER);
74                 return 0;
75               case 'u':                /* USE32 */
76                 bits = 32;
77                 p++;
78                 break;
79               case 'b':                /* bits */
80                 v = p[1] ? p+1 : --argc ? *++argv : NULL;
81                 if (!v) {
82                     fprintf(stderr, "%s: `-b' requires an argument\n", pname);
83                     return 1;
84                 }
85                 if (!strcmp(v, "16"))
86                     bits = 16;
87                 else if (!strcmp(v, "32"))
88                     bits = 32;
89                 else {
90                     fprintf(stderr, "%s: argument to `-b' should"
91                             " be `16' or `32'\n", pname);
92                 }
93                 p = "";                /* force to next argument */
94                 break;
95               case 'o':                /* origin */
96                 v = p[1] ? p+1 : --argc ? *++argv : NULL;
97                 if (!v) {
98                     fprintf(stderr, "%s: `-o' requires an argument\n", pname);
99                     return 1;
100                 }
101                 offset = readnum (v, &rn_error);
102                 if (rn_error) {
103                     fprintf(stderr, "%s: `-o' requires a numeric argument\n",
104                             pname);
105                     return 1;
106                 }
107                 p = "";                /* force to next argument */
108                 break;
109               case 's':                /* sync point */
110                 v = p[1] ? p+1 : --argc ? *++argv : NULL;
111                 if (!v) {
112                     fprintf(stderr, "%s: `-s' requires an argument\n", pname);
113                     return 1;
114                 }
115                 add_sync (readnum (v, &rn_error), 0L);
116                 if (rn_error) {
117                     fprintf(stderr, "%s: `-s' requires a numeric argument\n",
118                             pname);
119                     return 1;
120                 }
121                 p = "";                /* force to next argument */
122                 break;
123               case 'e':                /* skip a header */
124                 v = p[1] ? p+1 : --argc ? *++argv : NULL;
125                 if (!v) {
126                     fprintf(stderr, "%s: `-e' requires an argument\n", pname);
127                     return 1;
128                 }
129                 initskip = readnum (v, &rn_error);
130                 if (rn_error) {
131                     fprintf(stderr, "%s: `-e' requires a numeric argument\n",
132                             pname);
133                     return 1;
134                 }
135                 p = "";                /* force to next argument */
136                 break;
137               case 'k':                /* skip a region */
138                 v = p[1] ? p+1 : --argc ? *++argv : NULL;
139                 if (!v) {
140                     fprintf(stderr, "%s: `-k' requires an argument\n", pname);
141                     return 1;
142                 }
143                 vv = strchr(v, ',');
144                 if (!vv) {
145                     fprintf(stderr, "%s: `-k' requires two numbers separated"
146                             " by a comma\n", pname);
147                     return 1;
148                 }
149                 *vv++ = '\0';
150                 nextsync = readnum (v, &rn_error);
151                 if (rn_error) {
152                     fprintf(stderr, "%s: `-k' requires numeric arguments\n",
153                             pname);
154                     return 1;
155                 }
156                 synclen = readnum (vv, &rn_error);
157                 if (rn_error) {
158                     fprintf(stderr, "%s: `-k' requires numeric arguments\n",
159                             pname);
160                     return 1;
161                 }
162                 add_sync (nextsync, synclen);
163                 p = "";                /* force to next argument */
164                 break;
165             case 'p':                  /* preferred vendor */
166                 v = p[1] ? p+1 : --argc ? *++argv : NULL;
167                 if (!v) {
168                     fprintf(stderr, "%s: `-p' requires an argument\n", pname);
169                     return 1;
170                 }
171                 if ( !strcmp(v, "intel") ) {
172                   prefer = 0;   /* Default */
173                 } else if ( !strcmp(v, "amd") ) {
174                   prefer = IF_AMD|IF_3DNOW;
175                 } else if ( !strcmp(v, "cyrix") ) {
176                   prefer = IF_CYRIX|IF_3DNOW;
177                 } else if ( !strcmp(v, "idt") || !strcmp(v, "centaur") ||
178                             !strcmp(v, "winchip") ) {
179                   prefer = IF_3DNOW;
180                 } else {
181                   fprintf(stderr, "%s: unknown vendor `%s' specified with `-p'\n", pname, v);
182                   return 1;
183                 }
184                 p = "";                /* force to next argument */
185                 break;
186             default:                                                    /*bf*/
187                 fprintf(stderr, "%s: unrecognised option `-%c'\n",
188                         pname, *p);
189                 return 1;
190             }
191         } else if (!filename) {
192             filename = p;
193         } else {
194             fprintf(stderr, "%s: more than one filename specified\n", pname);
195             return 1;
196         }
197     }
198
199     if (!filename) {
200         fprintf(stderr, help, pname);
201         return 0;
202     }
203
204     if (strcmp(filename, "-")) {
205         fp = fopen(filename, "rb");
206         if (!fp) {
207             fprintf(stderr, "%s: unable to open `%s': %s\n",
208                     pname, filename, strerror(errno));
209             return 1;
210         }
211     } else
212         fp = stdin;
213
214     if (initskip > 0)
215         skip (initskip, fp);
216
217     /*
218      * This main loop is really horrible, and wants rewriting with
219      * an axe. It'll stay the way it is for a while though, until I
220      * find the energy...
221      */
222
223     p = q = buffer;
224     nextsync = next_sync (offset, &synclen);
225     do {
226         unsigned long to_read = buffer+sizeof(buffer)-p;
227         if (to_read > nextsync-offset-(p-q))
228             to_read = nextsync-offset-(p-q);
229         if (to_read) {
230             lenread = fread (p, 1, to_read, fp);
231             if (lenread == 0)
232                 eof = TRUE;            /* help along systems with bad feof */
233         } else
234             lenread = 0;
235         p += lenread;
236         if ((unsigned long)offset == nextsync) {
237             if (synclen) {
238                 fprintf(stdout, "%08lX  skipping 0x%lX bytes\n", offset, synclen);
239                 offset += synclen;
240                 skip (synclen, fp);
241             }
242             p = q = buffer;
243             nextsync = next_sync (offset, &synclen);
244         }
245         while (p > q && (p - q >= INSN_MAX || lenread == 0)) {
246             lendis = disasm (q, outbuf, sizeof(outbuf), bits, offset, autosync, prefer);
247             if (!lendis || lendis > (p - q) ||
248                 (unsigned long)lendis > nextsync-offset)
249                 lendis = eatbyte (q, outbuf, sizeof(outbuf));
250             output_ins (offset, q, lendis, outbuf);
251             q += lendis;
252             offset += lendis;
253         }
254         if (q >= buffer+INSN_MAX) {
255             unsigned char *r = buffer, *s = q;
256             int count = p - q;
257             while (count--)
258                 *r++ = *s++;
259             p -= (q - buffer);
260             q = buffer;
261         }
262     } while (lenread > 0 || !(eof || feof(fp)));
263
264     if (fp != stdin)
265         fclose (fp);
266
267     return 0;
268 }
269
270 static void output_ins (unsigned long offset, unsigned char *data,
271                         int datalen, char *insn) 
272 {
273     int bytes;
274     fprintf(stdout, "%08lX  ", offset);
275
276     bytes = 0;
277     while (datalen > 0 && bytes < BPL) {
278         fprintf(stdout, "%02X", *data++);
279         bytes++;
280         datalen--;
281     }
282
283     fprintf(stdout, "%*s%s\n", (BPL+1-bytes)*2, "", insn);
284
285     while (datalen > 0) {
286         fprintf(stdout, "         -");
287         bytes = 0;
288         while (datalen > 0 && bytes < BPL) {
289             fprintf(stdout, "%02X", *data++);
290             bytes++;
291             datalen--;
292         }
293         fprintf(stdout, "\n");
294     }
295 }
296
297 /*
298  * Skip a certain amount of data in a file, either by seeking if
299  * possible, or if that fails then by reading and discarding.
300  */
301 static void skip (unsigned long dist, FILE *fp) 
302 {
303     char buffer[256];                  /* should fit on most stacks :-) */
304
305     /*
306      * Got to be careful with fseek: at least one fseek I've tried
307      * doesn't approve of SEEK_CUR. So I'll use SEEK_SET and
308      * ftell... horrible but apparently necessary.
309      */
310     if (fseek (fp, dist+ftell(fp), SEEK_SET)) {
311         while (dist > 0) {
312             unsigned long len = (dist < sizeof(buffer) ?
313                                  dist : sizeof(buffer));
314             if (fread (buffer, 1, len, fp) < len) {
315                 perror("fread");
316                 exit(1);
317             }
318             dist -= len;
319         }
320     }
321 }