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