]>
Commit | Line | Data |
---|---|---|
14c7c974 A |
1 | /* |
2 | * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
f083c6c3 A |
6 | * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. |
7 | * | |
8 | * This file contains Original Code and/or Modifications of Original Code | |
9 | * as defined in and that are subject to the Apple Public Source License | |
10 | * Version 2.0 (the 'License'). You may not use this file except in | |
11 | * compliance with the License. Please obtain a copy of the License at | |
12 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
13 | * file. | |
14c7c974 A |
14 | * |
15 | * The Original Code and all software distributed under the License are | |
f083c6c3 | 16 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
14c7c974 A |
17 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
18 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
f083c6c3 A |
19 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
20 | * Please see the License for the specific language governing rights and | |
21 | * limitations under the License. | |
14c7c974 A |
22 | * |
23 | * @APPLE_LICENSE_HEADER_END@ | |
24 | */ | |
25 | /* The Netwide Assembler main program module | |
26 | * | |
27 | * The Netwide Assembler is copyright (C) 1996 Simon Tatham and | |
28 | * Julian Hall. All rights reserved. The software is | |
29 | * redistributable under the licence given in the file "Licence" | |
30 | * distributed in the NASM archive. | |
31 | */ | |
32 | ||
33 | #include <stdio.h> | |
34 | #include <stdarg.h> | |
35 | #include <stdlib.h> | |
36 | #include <string.h> | |
37 | #include <ctype.h> | |
38 | ||
39 | #include "nasm.h" | |
40 | #include "nasmlib.h" | |
41 | #include "preproc.h" | |
42 | #include "parser.h" | |
43 | #include "eval.h" | |
44 | #include "assemble.h" | |
45 | #include "labels.h" | |
46 | #include "outform.h" | |
47 | #include "listing.h" | |
48 | ||
49 | static void report_error (int, char *, ...); | |
50 | static void parse_cmdline (int, char **); | |
51 | static void assemble_file (char *); | |
52 | static int getkw (char *buf, char **value); | |
53 | static void register_output_formats(void); | |
54 | static void usage(void); | |
55 | ||
56 | static char *obuf; | |
57 | static char inname[FILENAME_MAX]; | |
58 | static char outname[FILENAME_MAX]; | |
59 | static char listname[FILENAME_MAX]; | |
60 | static int lineno; /* for error reporting */ | |
61 | static int lineinc; /* set by [LINE] or [ONELINE] */ | |
62 | static int globallineno; /* for forward-reference tracking */ | |
63 | static int pass; | |
64 | static struct ofmt *ofmt = NULL; | |
65 | ||
66 | static FILE *ofile = NULL; | |
67 | static int sb = 16; /* by default */ | |
68 | ||
69 | static int use_stdout = FALSE; /* by default, errors to stderr */ | |
70 | ||
71 | static long current_seg, abs_seg; | |
72 | static struct RAA *offsets; | |
73 | static long abs_offset; | |
74 | ||
75 | static struct SAA *forwrefs; /* keep track of forward references */ | |
76 | static int forwline; | |
77 | ||
78 | static Preproc *preproc; | |
79 | static int preprocess_only; | |
80 | ||
81 | /* used by error function to report location */ | |
82 | static char currentfile[FILENAME_MAX]; | |
83 | ||
84 | /* | |
85 | * Which of the suppressible warnings are suppressed. Entry zero | |
86 | * doesn't do anything. Initial defaults are given here. | |
87 | */ | |
88 | static char suppressed[1+ERR_WARN_MAX] = { | |
89 | 0, FALSE, TRUE, FALSE | |
90 | }; | |
91 | ||
92 | /* | |
93 | * The option names for the suppressible warnings. As before, entry | |
94 | * zero does nothing. | |
95 | */ | |
96 | static char *suppressed_names[1+ERR_WARN_MAX] = { | |
97 | NULL, "macro-params", "orphan-labels", "number-overflow" | |
98 | }; | |
99 | ||
100 | /* | |
101 | * The explanations for the suppressible warnings. As before, entry | |
102 | * zero does nothing. | |
103 | */ | |
104 | static char *suppressed_what[1+ERR_WARN_MAX] = { | |
105 | NULL, "macro calls with wrong no. of params", | |
106 | "labels alone on lines without trailing `:'", | |
107 | "numeric constants greater than 0xFFFFFFFF" | |
108 | }; | |
109 | ||
110 | /* | |
111 | * This is a null preprocessor which just copies lines from input | |
112 | * to output. It's used when someone explicitly requests that NASM | |
113 | * not preprocess their source file. | |
114 | */ | |
115 | ||
116 | static void no_pp_reset (char *, int, efunc, evalfunc, ListGen *); | |
117 | static char *no_pp_getline (void); | |
118 | static void no_pp_cleanup (void); | |
119 | static Preproc no_pp = { | |
120 | no_pp_reset, | |
121 | no_pp_getline, | |
122 | no_pp_cleanup | |
123 | }; | |
124 | ||
125 | /* | |
126 | * get/set current offset... | |
127 | */ | |
128 | #define get_curr_ofs (current_seg==NO_SEG?abs_offset:\ | |
129 | raa_read(offsets,current_seg)) | |
130 | #define set_curr_ofs(x) (current_seg==NO_SEG?(void)(abs_offset=(x)):\ | |
131 | (void)(offsets=raa_write(offsets,current_seg,(x)))) | |
132 | ||
133 | static int want_usage; | |
134 | static int terminate_after_phase; | |
135 | ||
136 | int main(int argc, char **argv) { | |
137 | want_usage = terminate_after_phase = FALSE; | |
138 | ||
139 | nasm_set_malloc_error (report_error); | |
140 | offsets = raa_init(); | |
141 | forwrefs = saa_init ((long)sizeof(int)); | |
142 | ||
143 | preproc = &nasmpp; | |
144 | preprocess_only = FALSE; | |
145 | ||
146 | seg_init(); | |
147 | ||
148 | register_output_formats(); | |
149 | ||
150 | parse_cmdline(argc, argv); | |
151 | ||
152 | if (terminate_after_phase) { | |
153 | if (want_usage) | |
154 | usage(); | |
155 | return 1; | |
156 | } | |
157 | ||
158 | if (ofmt->stdmac) | |
159 | pp_extra_stdmac (ofmt->stdmac); | |
160 | eval_global_info (ofmt, lookup_label); | |
161 | ||
162 | if (preprocess_only) { | |
163 | char *line; | |
164 | ||
165 | if (*outname) { | |
166 | ofile = fopen(outname, "w"); | |
167 | if (!ofile) | |
168 | report_error (ERR_FATAL | ERR_NOFILE, | |
169 | "unable to open output file `%s'", outname); | |
170 | } else | |
171 | ofile = NULL; | |
172 | ||
173 | eval_info ("%", 0L, 0L); /* disallow labels, $ or $$ in exprs */ | |
174 | ||
175 | preproc->reset (inname, 2, report_error, evaluate, &nasmlist); | |
176 | strcpy(currentfile,inname); | |
177 | lineno = 0; | |
178 | lineinc = 1; | |
179 | while ( (line = preproc->getline()) ) { | |
180 | int ln, li; | |
181 | char buf[FILENAME_MAX]; | |
182 | ||
183 | lineno += lineinc; | |
184 | /* | |
185 | * We must still check for %line directives, so that we | |
186 | * can report errors accurately. | |
187 | */ | |
188 | if (!strncmp(line, "%line", 5) && | |
189 | sscanf(line, "%%line %d+%d %s", &ln, &li, buf) == 3) { | |
190 | lineno = ln - li; | |
191 | lineinc = li; | |
192 | strncpy (currentfile, buf, FILENAME_MAX-1); | |
193 | currentfile[FILENAME_MAX-1] = '\0'; | |
194 | } | |
195 | if (ofile) { | |
196 | fputs(line, ofile); | |
197 | fputc('\n', ofile); | |
198 | } else | |
199 | puts(line); | |
200 | nasm_free (line); | |
201 | } | |
202 | preproc->cleanup(); | |
203 | if (ofile) | |
204 | fclose(ofile); | |
205 | if (ofile && terminate_after_phase) | |
206 | remove(outname); | |
207 | } else { | |
208 | /* | |
209 | * We must call ofmt->filename _anyway_, even if the user | |
210 | * has specified their own output file, because some | |
211 | * formats (eg OBJ and COFF) use ofmt->filename to find out | |
212 | * the name of the input file and then put that inside the | |
213 | * file. | |
214 | */ | |
215 | ofmt->filename (inname, outname, report_error); | |
216 | ||
217 | ofile = fopen(outname, "wb"); | |
218 | if (!ofile) { | |
219 | report_error (ERR_FATAL | ERR_NOFILE, | |
220 | "unable to open output file `%s'", outname); | |
221 | } | |
222 | /* | |
223 | * We must call init_labels() before ofmt->init() since | |
224 | * some object formats will want to define labels in their | |
225 | * init routines. (eg OS/2 defines the FLAT group) | |
226 | */ | |
227 | init_labels (); | |
228 | ofmt->init (ofile, report_error, define_label, evaluate); | |
229 | assemble_file (inname); | |
230 | if (!terminate_after_phase) { | |
231 | ofmt->cleanup (); | |
232 | cleanup_labels (); | |
233 | } | |
234 | /* | |
235 | * We had an fclose on the output file here, but we | |
236 | * actually do that in all the object file drivers as well, | |
237 | * so we're leaving out the one here. | |
238 | * fclose (ofile); | |
239 | */ | |
240 | if (terminate_after_phase) { | |
241 | remove(outname); | |
242 | if (listname[0]) | |
243 | remove(listname); | |
244 | } | |
245 | } | |
246 | ||
247 | if (want_usage) | |
248 | usage(); | |
249 | raa_free (offsets); | |
250 | saa_free (forwrefs); | |
251 | ||
252 | if (terminate_after_phase) | |
253 | return 1; | |
254 | else | |
255 | return 0; | |
256 | } | |
257 | ||
258 | static int process_arg (char *p, char *q) { | |
259 | char *param; | |
260 | int i; | |
261 | int advance = 0; | |
262 | ||
263 | if (!p || !p[0]) | |
264 | return 0; | |
265 | ||
266 | if (p[0]=='-') { | |
267 | switch (p[1]) { | |
268 | case 's': | |
269 | use_stdout = TRUE; | |
270 | break; | |
271 | case 'o': /* these parameters take values */ | |
272 | case 'f': | |
273 | case 'p': | |
274 | case 'd': | |
275 | case 'i': | |
276 | case 'l': | |
277 | if (p[2]) /* the parameter's in the option */ | |
278 | param = p+2; | |
279 | else if (!q) { | |
280 | report_error (ERR_NONFATAL | ERR_NOFILE | ERR_USAGE, | |
281 | "option `-%c' requires an argument", | |
282 | p[1]); | |
283 | break; | |
284 | } else | |
285 | advance = 1, param = q; | |
286 | if (p[1]=='o') { /* output file */ | |
287 | strcpy (outname, param); | |
288 | } else if (p[1]=='f') { /* output format */ | |
289 | ofmt = ofmt_find(param); | |
290 | if (!ofmt) { | |
291 | report_error (ERR_FATAL | ERR_NOFILE | ERR_USAGE, | |
292 | "unrecognised output format `%s'", | |
293 | param); | |
294 | } | |
295 | } else if (p[1]=='p') { /* pre-include */ | |
296 | pp_pre_include (param); | |
297 | } else if (p[1]=='d') { /* pre-define */ | |
298 | pp_pre_define (param); | |
299 | } else if (p[1]=='i') { /* include search path */ | |
300 | pp_include_path (param); | |
301 | } else if (p[1]=='l') { /* listing file */ | |
302 | strcpy (listname, param); | |
303 | } | |
304 | break; | |
305 | case 'h': | |
306 | fprintf(use_stdout ? stdout : stderr, | |
307 | "usage: nasm [-o outfile] [-f format] [-l listfile]" | |
308 | " [options...] filename\n"); | |
309 | fprintf(use_stdout ? stdout : stderr, | |
310 | " or nasm -r for version info\n\n"); | |
311 | fprintf(use_stdout ? stdout : stderr, | |
312 | " -e means preprocess only; " | |
313 | "-a means don't preprocess\n"); | |
314 | fprintf(use_stdout ? stdout : stderr, | |
315 | " -s means send errors to stdout not stderr\n"); | |
316 | fprintf(use_stdout ? stdout : stderr, | |
317 | " -i<path> adds a pathname to the include file" | |
318 | " path\n -p<file> pre-includes a file;" | |
319 | " -d<macro>[=<value] pre-defines a macro\n"); | |
320 | fprintf(use_stdout ? stdout : stderr, | |
321 | " -w+foo enables warnings about foo; " | |
322 | "-w-foo disables them\n where foo can be:\n"); | |
323 | for (i=1; i<=ERR_WARN_MAX; i++) | |
324 | fprintf(use_stdout ? stdout : stderr, | |
325 | " %-16s%s (default %s)\n", | |
326 | suppressed_names[i], suppressed_what[i], | |
327 | suppressed[i] ? "off" : "on"); | |
328 | fprintf(use_stdout ? stdout : stderr, | |
329 | "\nvalid output formats for -f are" | |
330 | " (`*' denotes default):\n"); | |
331 | ofmt_list(ofmt, use_stdout ? stdout : stderr); | |
332 | exit (0); /* never need usage message here */ | |
333 | break; | |
334 | case 'r': | |
335 | fprintf(use_stdout ? stdout : stderr, | |
336 | "NASM version %s\n", NASM_VER); | |
337 | exit (0); /* never need usage message here */ | |
338 | break; | |
339 | case 'e': /* preprocess only */ | |
340 | preprocess_only = TRUE; | |
341 | break; | |
342 | case 'a': /* assemble only - don't preprocess */ | |
343 | preproc = &no_pp; | |
344 | break; | |
345 | case 'w': | |
346 | if (p[2] != '+' && p[2] != '-') { | |
347 | report_error (ERR_NONFATAL | ERR_NOFILE | ERR_USAGE, | |
348 | "invalid option to `-w'"); | |
349 | } else { | |
350 | for (i=1; i<=ERR_WARN_MAX; i++) | |
351 | if (!nasm_stricmp(p+3, suppressed_names[i])) | |
352 | break; | |
353 | if (i <= ERR_WARN_MAX) | |
354 | suppressed[i] = (p[2] == '-'); | |
355 | else | |
356 | report_error (ERR_NONFATAL | ERR_NOFILE | ERR_USAGE, | |
357 | "invalid option to `-w'"); | |
358 | } | |
359 | break; | |
360 | default: | |
361 | report_error (ERR_NONFATAL | ERR_NOFILE | ERR_USAGE, | |
362 | "unrecognised option `-%c'", | |
363 | p[1]); | |
364 | break; | |
365 | } | |
366 | } else { | |
367 | if (*inname) { | |
368 | report_error (ERR_NONFATAL | ERR_NOFILE | ERR_USAGE, | |
369 | "more than one input file specified"); | |
370 | } else | |
371 | strcpy(inname, p); | |
372 | } | |
373 | ||
374 | return advance; | |
375 | } | |
376 | ||
377 | static void parse_cmdline(int argc, char **argv) { | |
378 | char *envreal, *envcopy, *p, *q, *arg, *prevarg; | |
379 | char separator = ' '; | |
380 | ||
381 | *inname = *outname = *listname = '\0'; | |
382 | ||
383 | /* | |
384 | * First, process the NASM environment variable. | |
385 | */ | |
386 | envreal = getenv("NASM"); | |
387 | arg = NULL; | |
388 | if (envreal) { | |
389 | envcopy = nasm_strdup(envreal); | |
390 | p = envcopy; | |
391 | if (*p && *p != '-') | |
392 | separator = *p++; | |
393 | while (*p) { | |
394 | q = p; | |
395 | while (*p && *p != separator) p++; | |
396 | while (*p == separator) *p++ = '\0'; | |
397 | prevarg = arg; | |
398 | arg = q; | |
399 | if (process_arg (prevarg, arg)) | |
400 | arg = NULL; | |
401 | } | |
402 | nasm_free (envcopy); | |
403 | } | |
404 | if (arg) | |
405 | process_arg (arg, NULL); | |
406 | ||
407 | /* | |
408 | * Now process the actual command line. | |
409 | */ | |
410 | while (--argc) { | |
411 | int i; | |
412 | argv++; | |
413 | i = process_arg (argv[0], argc > 1 ? argv[1] : NULL); | |
414 | argv += i, argc -= i; | |
415 | } | |
416 | ||
417 | if (!*inname) | |
418 | report_error (ERR_NONFATAL | ERR_NOFILE | ERR_USAGE, | |
419 | "no input file specified"); | |
420 | } | |
421 | ||
422 | static void assemble_file (char *fname) { | |
423 | char *value, *p, *q, *special, *line; | |
424 | insn output_ins; | |
425 | int i, rn_error, validid; | |
426 | long seg, offs; | |
427 | struct tokenval tokval; | |
428 | expr *e; | |
429 | ||
430 | /* pass one */ | |
431 | pass = 1; | |
432 | current_seg = ofmt->section(NULL, pass, &sb); | |
433 | preproc->reset(fname, 1, report_error, evaluate, &nasmlist); | |
434 | strcpy(currentfile,fname); | |
435 | lineno = 0; | |
436 | lineinc = 1; | |
437 | globallineno = 0; | |
438 | offs = get_curr_ofs; | |
439 | eval_info (NULL, current_seg, offs); /* set $ */ | |
440 | while ( (line = preproc->getline()) ) { | |
441 | lineno += lineinc; | |
442 | globallineno++; | |
443 | ||
444 | if (line[0] == '%') { | |
445 | int ln, li; | |
446 | char buf[FILENAME_MAX]; | |
447 | ||
448 | /* | |
449 | * This will be a line number directive. They come | |
450 | * straight from the preprocessor, so we'll subject | |
451 | * them to only minimal error checking. | |
452 | */ | |
453 | if (strncmp(line, "%line", 5)) { | |
454 | if (preproc == &no_pp) | |
455 | report_error (ERR_WARNING, "unknown `%%' directive in " | |
456 | " preprocessed source"); | |
457 | } else if (sscanf(line, "%%line %d+%d %s", &ln, &li, buf) != 3) { | |
458 | report_error (ERR_WARNING, "bogus line number directive in" | |
459 | " preprocessed source"); | |
460 | } else { | |
461 | lineno = ln - li; | |
462 | lineinc = li; | |
463 | strncpy (currentfile, buf, FILENAME_MAX-1); | |
464 | currentfile[FILENAME_MAX-1] = '\0'; | |
465 | } | |
466 | continue; | |
467 | } | |
468 | ||
469 | /* here we parse our directives; this is not handled by the 'real' | |
470 | * parser. */ | |
471 | if ( (i = getkw (line, &value)) ) { | |
472 | switch (i) { | |
473 | case 1: /* [SEGMENT n] */ | |
474 | seg = ofmt->section (value, pass, &sb); | |
475 | if (seg == NO_SEG) { | |
476 | report_error (ERR_NONFATAL, | |
477 | "segment name `%s' not recognised", | |
478 | value); | |
479 | } else { | |
480 | current_seg = seg; | |
481 | } | |
482 | break; | |
483 | case 2: /* [EXTERN label:special] */ | |
484 | if (*value == '$') | |
485 | value++; /* skip initial $ if present */ | |
486 | q = value; | |
487 | validid = TRUE; | |
488 | if (!isidstart(*q)) | |
489 | validid = FALSE; | |
490 | while (*q && *q != ':') { | |
491 | if (!isidchar(*q)) | |
492 | validid = FALSE; | |
493 | q++; | |
494 | } | |
495 | if (!validid) { | |
496 | report_error (ERR_NONFATAL, | |
497 | "identifier expected after EXTERN"); | |
498 | break; | |
499 | } | |
500 | if (*q == ':') { | |
501 | *q++ = '\0'; | |
502 | special = q; | |
503 | } else | |
504 | special = NULL; | |
505 | if (!is_extern(value)) { /* allow re-EXTERN to be ignored */ | |
506 | declare_as_global (value, special, report_error); | |
507 | define_label (value, seg_alloc(), 0L, NULL, FALSE, TRUE, | |
508 | ofmt, report_error); | |
509 | } | |
510 | break; | |
511 | case 3: /* [BITS bits] */ | |
512 | switch (atoi(value)) { | |
513 | case 16: | |
514 | case 32: | |
515 | sb = atoi(value); | |
516 | break; | |
517 | default: | |
518 | report_error(ERR_NONFATAL, | |
519 | "`%s' is not a valid argument to [BITS]", | |
520 | value); | |
521 | break; | |
522 | } | |
523 | break; | |
524 | case 4: /* [GLOBAL symbol:special] */ | |
525 | if (*value == '$') | |
526 | value++; /* skip initial $ if present */ | |
527 | q = value; | |
528 | validid = TRUE; | |
529 | if (!isidstart(*q)) | |
530 | validid = FALSE; | |
531 | while (*q && *q != ':') { | |
532 | if (!isidchar(*q)) | |
533 | validid = FALSE; | |
534 | q++; | |
535 | } | |
536 | if (!validid) { | |
537 | report_error (ERR_NONFATAL, | |
538 | "identifier expected after GLOBAL"); | |
539 | break; | |
540 | } | |
541 | if (*q == ':') { | |
542 | *q++ = '\0'; | |
543 | special = q; | |
544 | } else | |
545 | special = NULL; | |
546 | declare_as_global (value, special, report_error); | |
547 | break; | |
548 | case 5: /* [COMMON symbol size:special] */ | |
549 | p = value; | |
550 | validid = TRUE; | |
551 | if (!isidstart(*p)) | |
552 | validid = FALSE; | |
553 | while (*p && !isspace(*p)) { | |
554 | if (!isidchar(*p)) | |
555 | validid = FALSE; | |
556 | p++; | |
557 | } | |
558 | if (!validid) { | |
559 | report_error (ERR_NONFATAL, | |
560 | "identifier expected after COMMON"); | |
561 | break; | |
562 | } | |
563 | if (*p) { | |
564 | long size; | |
565 | ||
566 | while (*p && isspace(*p)) | |
567 | *p++ = '\0'; | |
568 | q = p; | |
569 | while (*q && *q != ':') | |
570 | q++; | |
571 | if (*q == ':') { | |
572 | *q++ = '\0'; | |
573 | special = q; | |
574 | } else | |
575 | special = NULL; | |
576 | size = readnum (p, &rn_error); | |
577 | if (rn_error) | |
578 | report_error (ERR_NONFATAL, "invalid size specified" | |
579 | " in COMMON declaration"); | |
580 | else | |
581 | define_common (value, seg_alloc(), size, | |
582 | special, ofmt, report_error); | |
583 | } else | |
584 | report_error (ERR_NONFATAL, "no size specified in" | |
585 | " COMMON declaration"); | |
586 | break; | |
587 | case 6: /* [ABSOLUTE address] */ | |
588 | current_seg = NO_SEG; | |
589 | stdscan_reset(); | |
590 | stdscan_bufptr = value; | |
591 | tokval.t_type = TOKEN_INVALID; | |
592 | e = evaluate(stdscan, NULL, &tokval, NULL, 1, report_error, | |
593 | NULL); | |
594 | if (e) { | |
595 | if (!is_reloc(e)) | |
596 | report_error (ERR_NONFATAL, "cannot use non-" | |
597 | "relocatable expression as ABSOLUTE" | |
598 | " address"); | |
599 | else { | |
600 | abs_seg = reloc_seg(e); | |
601 | abs_offset = reloc_value(e); | |
602 | } | |
603 | } else | |
604 | abs_offset = 0x100;/* don't go near zero in case of / */ | |
605 | break; | |
606 | default: | |
607 | if (!ofmt->directive (line+1, value, 1)) | |
608 | report_error (ERR_NONFATAL, "unrecognised directive [%s]", | |
609 | line+1); | |
610 | break; | |
611 | } | |
612 | } else { | |
613 | parse_line (1, line, &output_ins, | |
614 | report_error, evaluate, eval_info); | |
615 | if (output_ins.forw_ref) | |
616 | *(int *)saa_wstruct(forwrefs) = globallineno; | |
617 | ||
618 | /* | |
619 | * Hack to prevent phase error in the code | |
620 | * rol ax,x | |
621 | * x equ 1 | |
622 | * | |
623 | * We rule that the presence of a forward reference | |
624 | * cancels out the UNITY property of the number 1. This | |
625 | * isn't _strictly_ necessary in pass one, since the | |
626 | * problem occurs in pass two, but for the sake of | |
627 | * having the passes as near to identical as we can | |
628 | * manage, we do it like this. | |
629 | */ | |
630 | if (output_ins.forw_ref) { | |
631 | int i; | |
632 | for (i=0; i<output_ins.operands; i++) | |
633 | output_ins.oprs[i].type &= ~ONENESS; | |
634 | } | |
635 | ||
636 | if (output_ins.opcode == I_EQU) { | |
637 | /* | |
638 | * Special `..' EQUs get processed in pass two, | |
639 | * except `..@' macro-processor EQUs which are done | |
640 | * in the normal place. | |
641 | */ | |
642 | if (!output_ins.label) | |
643 | report_error (ERR_NONFATAL, | |
644 | "EQU not preceded by label"); | |
645 | else if (output_ins.label[0] != '.' || | |
646 | output_ins.label[1] != '.' || | |
647 | output_ins.label[2] == '@') { | |
648 | if (output_ins.operands == 1 && | |
649 | (output_ins.oprs[0].type & IMMEDIATE) && | |
650 | output_ins.oprs[0].wrt == NO_SEG) { | |
651 | define_label (output_ins.label, | |
652 | output_ins.oprs[0].segment, | |
653 | output_ins.oprs[0].offset, | |
654 | NULL, FALSE, FALSE, ofmt, report_error); | |
655 | } else if (output_ins.operands == 2 && | |
656 | (output_ins.oprs[0].type & IMMEDIATE) && | |
657 | (output_ins.oprs[0].type & COLON) && | |
658 | output_ins.oprs[0].segment == NO_SEG && | |
659 | output_ins.oprs[0].wrt == NO_SEG && | |
660 | (output_ins.oprs[1].type & IMMEDIATE) && | |
661 | output_ins.oprs[1].segment == NO_SEG && | |
662 | output_ins.oprs[1].wrt == NO_SEG) { | |
663 | define_label (output_ins.label, | |
664 | output_ins.oprs[0].offset | SEG_ABS, | |
665 | output_ins.oprs[1].offset, | |
666 | NULL, FALSE, FALSE, ofmt, report_error); | |
667 | } else | |
668 | report_error(ERR_NONFATAL, "bad syntax for EQU"); | |
669 | } | |
670 | } else { | |
671 | if (output_ins.label) | |
672 | define_label (output_ins.label, | |
673 | current_seg==NO_SEG ? abs_seg : current_seg, | |
674 | offs, NULL, TRUE, FALSE, ofmt, report_error); | |
675 | offs += insn_size (current_seg, offs, sb, | |
676 | &output_ins, report_error); | |
677 | set_curr_ofs (offs); | |
678 | } | |
679 | cleanup_insn (&output_ins); | |
680 | } | |
681 | nasm_free (line); | |
682 | offs = get_curr_ofs; | |
683 | eval_info (NULL, current_seg, offs); /* set $ */ | |
684 | } | |
685 | preproc->cleanup(); | |
686 | ||
687 | if (terminate_after_phase) { | |
688 | fclose(ofile); | |
689 | remove(outname); | |
690 | if (want_usage) | |
691 | usage(); | |
692 | exit (1); | |
693 | } | |
694 | ||
695 | /* pass two */ | |
696 | pass = 2; | |
697 | saa_rewind (forwrefs); | |
698 | if (*listname) | |
699 | nasmlist.init(listname, report_error); | |
700 | { | |
701 | int *p = saa_rstruct (forwrefs); | |
702 | if (p) | |
703 | forwline = *p; | |
704 | else | |
705 | forwline = -1; | |
706 | } | |
707 | current_seg = ofmt->section(NULL, pass, &sb); | |
708 | raa_free (offsets); | |
709 | offsets = raa_init(); | |
710 | preproc->reset(fname, 2, report_error, evaluate, &nasmlist); | |
711 | strcpy(currentfile,fname); | |
712 | lineno = 0; | |
713 | lineinc = 1; | |
714 | globallineno = 0; | |
715 | offs = get_curr_ofs; | |
716 | eval_info (NULL, current_seg, offs); /* set $ */ | |
717 | while ( (line = preproc->getline()) ) { | |
718 | lineno += lineinc; | |
719 | globallineno++; | |
720 | ||
721 | if (line[0] == '%') { | |
722 | int ln, li; | |
723 | char buf[FILENAME_MAX]; | |
724 | ||
725 | /* | |
726 | * This will be a line number directive. They come | |
727 | * straight from the preprocessor, so we'll subject | |
728 | * them to only minimal error checking. | |
729 | */ | |
730 | if (!strncmp(line, "%line", 5) && | |
731 | sscanf(line, "%%line %d+%d %s", &ln, &li, buf) == 3) { | |
732 | lineno = ln - li; | |
733 | lineinc = li; | |
734 | strncpy (currentfile, buf, FILENAME_MAX-1); | |
735 | currentfile[FILENAME_MAX-1] = '\0'; | |
736 | } | |
737 | continue; | |
738 | } | |
739 | ||
740 | /* here we parse our directives; this is not handled by | |
741 | * the 'real' parser. */ | |
742 | if ( (i = getkw (line, &value)) ) { | |
743 | switch (i) { | |
744 | case 1: /* [SEGMENT n] */ | |
745 | seg = ofmt->section (value, pass, &sb); | |
746 | if (seg == NO_SEG) { | |
747 | report_error (ERR_PANIC, | |
748 | "invalid segment name on pass two"); | |
749 | } else | |
750 | current_seg = seg; | |
751 | break; | |
752 | case 2: /* [EXTERN label] */ | |
753 | q = value; | |
754 | while (*q && *q != ':') | |
755 | q++; | |
756 | if (*q == ':') { | |
757 | *q++ = '\0'; | |
758 | ofmt->symdef(value, 0L, 0L, 3, q); | |
759 | } | |
760 | break; | |
761 | case 3: /* [BITS bits] */ | |
762 | switch (atoi(value)) { | |
763 | case 16: | |
764 | case 32: | |
765 | sb = atoi(value); | |
766 | break; | |
767 | default: | |
768 | report_error(ERR_PANIC, | |
769 | "invalid [BITS] value on pass two", | |
770 | value); | |
771 | break; | |
772 | } | |
773 | break; | |
774 | case 4: /* [GLOBAL symbol] */ | |
775 | q = value; | |
776 | while (*q && *q != ':') | |
777 | q++; | |
778 | if (*q == ':') { | |
779 | *q++ = '\0'; | |
780 | ofmt->symdef(value, 0L, 0L, 3, q); | |
781 | } | |
782 | break; | |
783 | case 5: /* [COMMON symbol size] */ | |
784 | q = value; | |
785 | while (*q && *q != ':') { | |
786 | if (isspace(*q)) | |
787 | *q = '\0'; | |
788 | q++; | |
789 | } | |
790 | if (*q == ':') { | |
791 | *q++ = '\0'; | |
792 | ofmt->symdef(value, 0L, 0L, 3, q); | |
793 | } | |
794 | break; | |
795 | case 6: /* [ABSOLUTE addr] */ | |
796 | current_seg = NO_SEG; | |
797 | stdscan_reset(); | |
798 | stdscan_bufptr = value; | |
799 | tokval.t_type = TOKEN_INVALID; | |
800 | e = evaluate(stdscan, NULL, &tokval, NULL, 2, report_error, | |
801 | NULL); | |
802 | if (e) { | |
803 | if (!is_reloc(e)) | |
804 | report_error (ERR_PANIC, "non-reloc ABSOLUTE address" | |
805 | " in pass two"); | |
806 | else { | |
807 | abs_seg = reloc_seg(e); | |
808 | abs_offset = reloc_value(e); | |
809 | } | |
810 | } else | |
811 | report_error (ERR_PANIC, "invalid ABSOLUTE address " | |
812 | "in pass two"); | |
813 | break; | |
814 | default: | |
815 | if (!ofmt->directive (line+1, value, 2)) | |
816 | report_error (ERR_PANIC, "invalid directive on pass two"); | |
817 | break; | |
818 | } | |
819 | } else { | |
820 | parse_line (2, line, &output_ins, | |
821 | report_error, evaluate, eval_info); | |
822 | if (globallineno == forwline) { | |
823 | int *p = saa_rstruct (forwrefs); | |
824 | if (p) | |
825 | forwline = *p; | |
826 | else | |
827 | forwline = -1; | |
828 | output_ins.forw_ref = TRUE; | |
829 | } else | |
830 | output_ins.forw_ref = FALSE; | |
831 | ||
832 | /* | |
833 | * Hack to prevent phase error in the code | |
834 | * rol ax,x | |
835 | * x equ 1 | |
836 | */ | |
837 | if (output_ins.forw_ref) { | |
838 | int i; | |
839 | for (i=0; i<output_ins.operands; i++) | |
840 | output_ins.oprs[i].type &= ~ONENESS; | |
841 | } | |
842 | ||
843 | obuf = line; | |
844 | if (output_ins.label) | |
845 | define_label_stub (output_ins.label, report_error); | |
846 | if (output_ins.opcode == I_EQU) { | |
847 | /* | |
848 | * Special `..' EQUs get processed here, except | |
849 | * `..@' macro processor EQUs which are done above. | |
850 | */ | |
851 | if (output_ins.label[0] == '.' && | |
852 | output_ins.label[1] == '.' && | |
853 | output_ins.label[2] != '@') { | |
854 | if (output_ins.operands == 1 && | |
855 | (output_ins.oprs[0].type & IMMEDIATE)) { | |
856 | define_label (output_ins.label, | |
857 | output_ins.oprs[0].segment, | |
858 | output_ins.oprs[0].offset, | |
859 | NULL, FALSE, FALSE, ofmt, report_error); | |
860 | } else if (output_ins.operands == 2 && | |
861 | (output_ins.oprs[0].type & IMMEDIATE) && | |
862 | (output_ins.oprs[0].type & COLON) && | |
863 | output_ins.oprs[0].segment == NO_SEG && | |
864 | (output_ins.oprs[1].type & IMMEDIATE) && | |
865 | output_ins.oprs[1].segment == NO_SEG) { | |
866 | define_label (output_ins.label, | |
867 | output_ins.oprs[0].offset | SEG_ABS, | |
868 | output_ins.oprs[1].offset, | |
869 | NULL, FALSE, FALSE, ofmt, report_error); | |
870 | } else | |
871 | report_error(ERR_NONFATAL, "bad syntax for EQU"); | |
872 | } | |
873 | } | |
874 | offs += assemble (current_seg, offs, sb, | |
875 | &output_ins, ofmt, report_error, &nasmlist); | |
876 | cleanup_insn (&output_ins); | |
877 | set_curr_ofs (offs); | |
878 | } | |
879 | nasm_free (line); | |
880 | ||
881 | offs = get_curr_ofs; | |
882 | eval_info (NULL, current_seg, offs); /* set $ */ | |
883 | } | |
884 | preproc->cleanup(); | |
885 | nasmlist.cleanup(); | |
886 | } | |
887 | ||
888 | static int getkw (char *buf, char **value) { | |
889 | char *p, *q; | |
890 | ||
891 | if (*buf!='[') | |
892 | return 0; | |
893 | p = buf; | |
894 | while (*p && *p != ']') p++; | |
895 | if (!*p) | |
896 | return 0; | |
897 | q = p++; | |
898 | while (*p && *p != ';') { | |
899 | if (!isspace(*p)) | |
900 | return 0; | |
901 | p++; | |
902 | } | |
903 | q[1] = '\0'; | |
904 | ||
905 | p = buf+1; | |
906 | while (*buf && *buf!=' ' && *buf!=']' && *buf!='\t') | |
907 | buf++; | |
908 | if (*buf==']') { | |
909 | *buf = '\0'; | |
910 | *value = buf; | |
911 | } else { | |
912 | *buf++ = '\0'; | |
913 | while (isspace(*buf)) buf++; /* beppu - skip leading whitespace */ | |
914 | *value = buf; | |
915 | while (*buf!=']') buf++; | |
916 | *buf++ = '\0'; | |
917 | } | |
918 | for (q=p; *q; q++) | |
919 | *q = tolower(*q); | |
920 | if (!strcmp(p, "segment") || !strcmp(p, "section")) | |
921 | return 1; | |
922 | if (!strcmp(p, "extern")) | |
923 | return 2; | |
924 | if (!strcmp(p, "bits")) | |
925 | return 3; | |
926 | if (!strcmp(p, "global")) | |
927 | return 4; | |
928 | if (!strcmp(p, "common")) | |
929 | return 5; | |
930 | if (!strcmp(p, "absolute")) | |
931 | return 6; | |
932 | return -1; | |
933 | } | |
934 | ||
935 | static void report_error (int severity, char *fmt, ...) { | |
936 | va_list ap; | |
937 | ||
938 | /* | |
939 | * See if it's a suppressed warning. | |
940 | */ | |
941 | if ((severity & ERR_MASK) == ERR_WARNING && | |
942 | (severity & ERR_WARN_MASK) != 0 && | |
943 | suppressed[ (severity & ERR_WARN_MASK) >> ERR_WARN_SHR ]) | |
944 | return; /* and bail out if so */ | |
945 | ||
946 | /* | |
947 | * See if it's a pass-one only warning and we're not in pass one. | |
948 | */ | |
949 | if ((severity & ERR_PASS1) && pass != 1) | |
950 | return; | |
951 | ||
952 | if (severity & ERR_NOFILE) | |
953 | fputs ("nasm: ", use_stdout ? stdout : stderr); | |
954 | else | |
955 | fprintf (use_stdout ? stdout : stderr, "%s:%d: ", currentfile, | |
956 | lineno + (severity & ERR_OFFBY1 ? lineinc : 0)); | |
957 | ||
958 | if ( (severity & ERR_MASK) == ERR_WARNING) | |
959 | fputs ("warning: ", use_stdout ? stdout : stderr); | |
960 | else if ( (severity & ERR_MASK) == ERR_PANIC) | |
961 | fputs ("panic: ", use_stdout ? stdout : stderr); | |
962 | ||
963 | va_start (ap, fmt); | |
964 | vfprintf (use_stdout ? stdout : stderr, fmt, ap); | |
965 | fputc ('\n', use_stdout ? stdout : stderr); | |
966 | ||
967 | if (severity & ERR_USAGE) | |
968 | want_usage = TRUE; | |
969 | ||
970 | switch (severity & ERR_MASK) { | |
971 | case ERR_WARNING: | |
972 | /* no further action, by definition */ | |
973 | break; | |
974 | case ERR_NONFATAL: | |
975 | terminate_after_phase = TRUE; | |
976 | break; | |
977 | case ERR_FATAL: | |
978 | if (ofile) { | |
979 | fclose(ofile); | |
980 | remove(outname); | |
981 | } | |
982 | if (want_usage) | |
983 | usage(); | |
984 | exit(1); /* instantly die */ | |
985 | break; /* placate silly compilers */ | |
986 | case ERR_PANIC: | |
987 | abort(); /* halt, catch fire, and dump core */ | |
988 | break; | |
989 | } | |
990 | } | |
991 | ||
992 | static void usage(void) { | |
993 | fputs("type `nasm -h' for help\n", use_stdout ? stdout : stderr); | |
994 | } | |
995 | ||
996 | static void register_output_formats(void) { | |
997 | /* Flat-form binary format */ | |
998 | #ifdef OF_BIN | |
999 | extern struct ofmt of_bin; | |
1000 | #endif | |
1001 | /* Unix formats: a.out, COFF, ELF */ | |
1002 | #ifdef OF_AOUT | |
1003 | extern struct ofmt of_aout; | |
1004 | #endif | |
1005 | #ifdef OF_AOUTB | |
1006 | extern struct ofmt of_aoutb; | |
1007 | #endif | |
1008 | #ifdef OF_COFF | |
1009 | extern struct ofmt of_coff; | |
1010 | #endif | |
1011 | #ifdef OF_ELF | |
1012 | extern struct ofmt of_elf; | |
1013 | #endif | |
1014 | /* Linux strange format: as86 */ | |
1015 | #ifdef OF_AS86 | |
1016 | extern struct ofmt of_as86; | |
1017 | #endif | |
1018 | /* DOS and DOS-ish formats: OBJ, OS/2, Win32 */ | |
1019 | #ifdef OF_OBJ | |
1020 | extern struct ofmt of_obj; | |
1021 | #endif | |
1022 | #ifdef OF_WIN32 | |
1023 | extern struct ofmt of_win32; | |
1024 | #endif | |
1025 | #ifdef OF_RDF | |
1026 | extern struct ofmt of_rdf; | |
1027 | #endif | |
1028 | #ifdef OF_DBG /* debug format must be included specifically */ | |
1029 | extern struct ofmt of_dbg; | |
1030 | #endif | |
1031 | ||
1032 | #ifdef OF_BIN | |
1033 | ofmt_register (&of_bin); | |
1034 | #endif | |
1035 | #ifdef OF_AOUT | |
1036 | ofmt_register (&of_aout); | |
1037 | #endif | |
1038 | #ifdef OF_AOUTB | |
1039 | ofmt_register (&of_aoutb); | |
1040 | #endif | |
1041 | #ifdef OF_COFF | |
1042 | ofmt_register (&of_coff); | |
1043 | #endif | |
1044 | #ifdef OF_ELF | |
1045 | ofmt_register (&of_elf); | |
1046 | #endif | |
1047 | #ifdef OF_AS86 | |
1048 | ofmt_register (&of_as86); | |
1049 | #endif | |
1050 | #ifdef OF_OBJ | |
1051 | ofmt_register (&of_obj); | |
1052 | #endif | |
1053 | #ifdef OF_WIN32 | |
1054 | ofmt_register (&of_win32); | |
1055 | #endif | |
1056 | #ifdef OF_RDF | |
1057 | ofmt_register (&of_rdf); | |
1058 | #endif | |
1059 | #ifdef OF_DBG | |
1060 | ofmt_register (&of_dbg); | |
1061 | #endif | |
1062 | /* | |
1063 | * set the default format | |
1064 | */ | |
1065 | ofmt = &OF_DEFAULT; | |
1066 | } | |
1067 | ||
1068 | #define BUF_DELTA 512 | |
1069 | ||
1070 | static FILE *no_pp_fp; | |
1071 | static efunc no_pp_err; | |
1072 | static ListGen *no_pp_list; | |
1073 | ||
1074 | static void no_pp_reset (char *file, int pass, efunc error, evalfunc eval, | |
1075 | ListGen *listgen) { | |
1076 | no_pp_err = error; | |
1077 | no_pp_fp = fopen(file, "r"); | |
1078 | if (!no_pp_fp) | |
1079 | no_pp_err (ERR_FATAL | ERR_NOFILE, | |
1080 | "unable to open input file `%s'", file); | |
1081 | no_pp_list = listgen; | |
1082 | (void) pass; /* placate compilers */ | |
1083 | (void) eval; /* placate compilers */ | |
1084 | } | |
1085 | ||
1086 | static char *no_pp_getline (void) { | |
1087 | char *buffer, *p, *q; | |
1088 | int bufsize; | |
1089 | ||
1090 | bufsize = BUF_DELTA; | |
1091 | buffer = nasm_malloc(BUF_DELTA); | |
1092 | p = buffer; | |
1093 | while (1) { | |
1094 | q = fgets(p, bufsize-(p-buffer), no_pp_fp); | |
1095 | if (!q) | |
1096 | break; | |
1097 | p += strlen(p); | |
1098 | if (p > buffer && p[-1] == '\n') | |
1099 | break; | |
1100 | if (p-buffer > bufsize-10) { | |
1101 | bufsize += BUF_DELTA; | |
1102 | buffer = nasm_realloc(buffer, bufsize); | |
1103 | } | |
1104 | } | |
1105 | ||
1106 | if (!q && p == buffer) { | |
1107 | nasm_free (buffer); | |
1108 | return NULL; | |
1109 | } | |
1110 | ||
1111 | /* | |
1112 | * Play safe: remove CRs as well as LFs, if any of either are | |
1113 | * present at the end of the line. | |
1114 | */ | |
1115 | while (p > buffer && (p[-1] == '\n' || p[-1] == '\r')) | |
1116 | *--p = '\0'; | |
1117 | ||
1118 | /* | |
1119 | * Handle spurious ^Z, which may be inserted into source files | |
1120 | * by some file transfer utilities. | |
1121 | */ | |
1122 | buffer[strcspn(buffer, "\032")] = '\0'; | |
1123 | ||
1124 | no_pp_list->line (LIST_READ, buffer); | |
1125 | ||
1126 | return buffer; | |
1127 | } | |
1128 | ||
1129 | static void no_pp_cleanup (void) { | |
1130 | fclose(no_pp_fp); | |
1131 | } |