1 | /* ----------------------------------------------------------------------- * |
2 | * |
3 | * Copyright 1996-2018 The NASM Authors - All Rights Reserved |
4 | * See the file AUTHORS included with the NASM distribution for |
5 | * the specific copyright holders. |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following |
9 | * conditions are met: |
10 | * |
11 | * * Redistributions of source code must retain the above copyright |
12 | * notice, this list of conditions and the following disclaimer. |
13 | * * Redistributions in binary form must reproduce the above |
14 | * copyright notice, this list of conditions and the following |
15 | * disclaimer in the documentation and/or other materials provided |
16 | * with the distribution. |
17 | * |
18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
19 | * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
20 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
21 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
22 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
23 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
25 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
26 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
29 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, |
30 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
31 | * |
32 | * ----------------------------------------------------------------------- */ |
33 | |
34 | /* |
35 | * Parse and handle assembler directives |
36 | */ |
37 | |
38 | #include "compiler.h" |
39 | |
40 | #include <stdlib.h> |
41 | #include <string.h> |
42 | #include <ctype.h> |
43 | #include <limits.h> |
44 | |
45 | #include "nasm.h" |
46 | #include "nasmlib.h" |
47 | #include "ilog2.h" |
48 | #include "error.h" |
49 | #include "float.h" |
50 | #include "stdscan.h" |
51 | #include "preproc.h" |
52 | #include "eval.h" |
53 | #include "assemble.h" |
54 | #include "outform.h" |
55 | #include "listing.h" |
56 | #include "labels.h" |
57 | #include "iflag.h" |
58 | |
59 | struct cpunames { |
60 | const char *name; |
61 | unsigned int level; |
62 | /* Eventually a table of features */ |
63 | }; |
64 | |
65 | static iflag_t get_cpu(const char *value) |
66 | { |
67 | iflag_t r; |
68 | const struct cpunames *cpu; |
69 | static const struct cpunames cpunames[] = { |
70 | { "8086" , IF_8086 }, |
71 | { "186" , IF_186 }, |
72 | { "286" , IF_286 }, |
73 | { "386" , IF_386 }, |
74 | { "486" , IF_486 }, |
75 | { "586" , IF_PENT }, |
76 | { "pentium" , IF_PENT }, |
77 | { "pentiummmx" , IF_PENT }, |
78 | { "686" , IF_P6 }, |
79 | { "p6" , IF_P6 }, |
80 | { "ppro" , IF_P6 }, |
81 | { "pentiumpro" , IF_P6 }, |
82 | { "p2" , IF_P6 }, /* +MMX */ |
83 | { "pentiumii" , IF_P6 }, |
84 | { "p3" , IF_KATMAI }, |
85 | { "katmai" , IF_KATMAI }, |
86 | { "p4" , IF_WILLAMETTE }, |
87 | { "willamette" , IF_WILLAMETTE }, |
88 | { "prescott" , IF_PRESCOTT }, |
89 | { "x64" , IF_X86_64 }, |
90 | { "x86-64" , IF_X86_64 }, |
91 | { "ia64" , IF_IA64 }, |
92 | { "ia-64" , IF_IA64 }, |
93 | { "itanium" , IF_IA64 }, |
94 | { "itanic" , IF_IA64 }, |
95 | { "merced" , IF_IA64 }, |
96 | { "any" , IF_PLEVEL }, |
97 | { "default" , IF_PLEVEL }, |
98 | { "all" , IF_PLEVEL }, |
99 | { NULL, IF_PLEVEL } /* Error and final default entry */ |
100 | }; |
101 | |
102 | iflag_clear_all(&r); |
103 | |
104 | for (cpu = cpunames; cpu->name; cpu++) { |
105 | if (!nasm_stricmp(value, cpu->name)) |
106 | break; |
107 | } |
108 | |
109 | if (!cpu->name) { |
110 | nasm_error(pass0 < 2 ? ERR_NONFATAL : ERR_FATAL, |
111 | "unknown 'cpu' type '%s'" , value); |
112 | } |
113 | |
114 | iflag_set_cpu(&r, cpu->level); |
115 | return r; |
116 | } |
117 | |
118 | static int get_bits(const char *value) |
119 | { |
120 | int i = atoi(value); |
121 | |
122 | switch (i) { |
123 | case 16: |
124 | break; /* Always safe */ |
125 | case 32: |
126 | if (!iflag_cpu_level_ok(&cpu, IF_386)) { |
127 | nasm_error(ERR_NONFATAL, |
128 | "cannot specify 32-bit segment on processor below a 386" ); |
129 | i = 16; |
130 | } |
131 | break; |
132 | case 64: |
133 | if (!iflag_cpu_level_ok(&cpu, IF_X86_64)) { |
134 | nasm_error(ERR_NONFATAL, |
135 | "cannot specify 64-bit segment on processor below an x86-64" ); |
136 | i = 16; |
137 | } |
138 | break; |
139 | default: |
140 | nasm_error(pass0 < 2 ? ERR_NONFATAL : ERR_FATAL, |
141 | "`%s' is not a valid segment size; must be 16, 32 or 64" , |
142 | value); |
143 | i = 16; |
144 | break; |
145 | } |
146 | return i; |
147 | } |
148 | |
149 | static enum directive parse_directive_line(char **directive, char **value) |
150 | { |
151 | char *p, *q, *buf; |
152 | |
153 | buf = nasm_skip_spaces(*directive); |
154 | |
155 | /* |
156 | * It should be enclosed in [ ]. |
157 | * XXX: we don't check there is nothing else on the remainder of the |
158 | * line, except a possible comment. |
159 | */ |
160 | if (*buf != '[') |
161 | return D_none; |
162 | q = strchr(buf, ']'); |
163 | if (!q) |
164 | return D_corrupt; |
165 | |
166 | /* |
167 | * Strip off the comments. XXX: this doesn't account for quoted |
168 | * strings inside a directive. We should really strip the |
169 | * comments in generic code, not here. While we're at it, it |
170 | * would be better to pass the backend a series of tokens instead |
171 | * of a raw string, and actually process quoted strings for it, |
172 | * like of like argv is handled in C. |
173 | */ |
174 | p = strchr(buf, ';'); |
175 | if (p) { |
176 | if (p < q) /* ouch! somewhere inside */ |
177 | return D_corrupt; |
178 | *p = '\0'; |
179 | } |
180 | |
181 | /* no brace, no trailing spaces */ |
182 | *q = '\0'; |
183 | nasm_zap_spaces_rev(--q); |
184 | |
185 | /* directive */ |
186 | p = nasm_skip_spaces(++buf); |
187 | q = nasm_skip_word(p); |
188 | if (!q) |
189 | return D_corrupt; /* sigh... no value there */ |
190 | *q = '\0'; |
191 | *directive = p; |
192 | |
193 | /* and value finally */ |
194 | p = nasm_skip_spaces(++q); |
195 | *value = p; |
196 | |
197 | return directive_find(*directive); |
198 | } |
199 | |
200 | /* |
201 | * Process a line from the assembler and try to handle it if it |
202 | * is a directive. Return true if the line was handled (including |
203 | * if it was an error), false otherwise. |
204 | */ |
205 | bool process_directives(char *directive) |
206 | { |
207 | enum directive d; |
208 | char *value, *p, *q, *special; |
209 | struct tokenval tokval; |
210 | bool bad_param = false; |
211 | int pass2 = passn > 1 ? 2 : 1; |
212 | enum label_type type; |
213 | |
214 | d = parse_directive_line(&directive, &value); |
215 | |
216 | switch (d) { |
217 | case D_none: |
218 | return D_none; /* Not a directive */ |
219 | |
220 | case D_corrupt: |
221 | nasm_error(ERR_NONFATAL, "invalid directive line" ); |
222 | break; |
223 | |
224 | default: /* It's a backend-specific directive */ |
225 | switch (ofmt->directive(d, value, pass2)) { |
226 | case DIRR_UNKNOWN: |
227 | goto unknown; |
228 | case DIRR_OK: |
229 | case DIRR_ERROR: |
230 | break; |
231 | case DIRR_BADPARAM: |
232 | bad_param = true; |
233 | break; |
234 | default: |
235 | panic(); |
236 | } |
237 | break; |
238 | |
239 | case D_unknown: |
240 | unknown: |
241 | nasm_error(pass0 < 2 ? ERR_NONFATAL : ERR_PANIC, |
242 | "unrecognised directive [%s]" , directive); |
243 | break; |
244 | |
245 | case D_SEGMENT: /* [SEGMENT n] */ |
246 | case D_SECTION: |
247 | { |
248 | int sb = globalbits; |
249 | int32_t seg = ofmt->section(value, pass2, &sb); |
250 | |
251 | if (seg == NO_SEG) { |
252 | nasm_error(pass0 < 2 ? ERR_NONFATAL : ERR_PANIC, |
253 | "segment name `%s' not recognized" , value); |
254 | } else { |
255 | globalbits = sb; |
256 | switch_segment(seg); |
257 | } |
258 | break; |
259 | } |
260 | |
261 | case D_SECTALIGN: /* [SECTALIGN n] */ |
262 | { |
263 | expr *e; |
264 | |
265 | if (*value) { |
266 | stdscan_reset(); |
267 | stdscan_set(value); |
268 | tokval.t_type = TOKEN_INVALID; |
269 | e = evaluate(stdscan, NULL, &tokval, NULL, pass2, NULL); |
270 | if (e) { |
271 | uint64_t align = e->value; |
272 | |
273 | if (!is_power2(e->value)) { |
274 | nasm_error(ERR_NONFATAL, |
275 | "segment alignment `%s' is not power of two" , |
276 | value); |
277 | } else if (align > UINT64_C(0x7fffffff)) { |
278 | /* |
279 | * FIXME: Please make some sane message here |
280 | * ofmt should have some 'check' method which |
281 | * would report segment alignment bounds. |
282 | */ |
283 | nasm_error(ERR_NONFATAL, |
284 | "absurdly large segment alignment `%s' (2^%d)" , |
285 | value, ilog2_64(align)); |
286 | } |
287 | |
288 | /* callee should be able to handle all details */ |
289 | if (location.segment != NO_SEG) |
290 | ofmt->sectalign(location.segment, align); |
291 | } |
292 | } |
293 | break; |
294 | } |
295 | |
296 | case D_BITS: /* [BITS bits] */ |
297 | globalbits = get_bits(value); |
298 | break; |
299 | |
300 | case D_GLOBAL: /* [GLOBAL|STATIC|EXTERN|COMMON symbol:special] */ |
301 | type = LBL_GLOBAL; |
302 | goto symdef; |
303 | case D_STATIC: |
304 | type = LBL_STATIC; |
305 | goto symdef; |
306 | case D_EXTERN: |
307 | type = LBL_EXTERN; |
308 | goto symdef; |
309 | case D_COMMON: |
310 | type = LBL_COMMON; |
311 | goto symdef; |
312 | |
313 | symdef: |
314 | { |
315 | bool validid = true; |
316 | int64_t size = 0; |
317 | char *sizestr; |
318 | bool rn_error; |
319 | |
320 | if (*value == '$') |
321 | value++; /* skip initial $ if present */ |
322 | |
323 | q = value; |
324 | if (!isidstart(*q)) { |
325 | validid = false; |
326 | } else { |
327 | q++; |
328 | while (*q && *q != ':' && !nasm_isspace(*q)) { |
329 | if (!isidchar(*q)) |
330 | validid = false; |
331 | q++; |
332 | } |
333 | } |
334 | if (!validid) { |
335 | nasm_error(ERR_NONFATAL, |
336 | "identifier expected after %s, got `%s'" , |
337 | directive, value); |
338 | break; |
339 | } |
340 | |
341 | if (nasm_isspace(*q)) { |
342 | *q++ = '\0'; |
343 | sizestr = q = nasm_skip_spaces(q); |
344 | q = strchr(q, ':'); |
345 | } else { |
346 | sizestr = NULL; |
347 | } |
348 | |
349 | if (q && *q == ':') { |
350 | *q++ = '\0'; |
351 | special = q; |
352 | } else { |
353 | special = NULL; |
354 | } |
355 | |
356 | if (type == LBL_COMMON) { |
357 | if (sizestr) |
358 | size = readnum(sizestr, &rn_error); |
359 | if (!sizestr || rn_error) |
360 | nasm_error(ERR_NONFATAL, |
361 | "%s size specified in common declaration" , |
362 | sizestr ? "invalid" : "no" ); |
363 | } else if (sizestr) { |
364 | nasm_error(ERR_NONFATAL, "invalid syntax in %s declaration" , |
365 | directive); |
366 | } |
367 | |
368 | if (!declare_label(value, type, special)) |
369 | break; |
370 | |
371 | if (type == LBL_COMMON || type == LBL_EXTERN) |
372 | define_label(value, 0, size, false); |
373 | |
374 | break; |
375 | } |
376 | |
377 | case D_ABSOLUTE: /* [ABSOLUTE address] */ |
378 | { |
379 | expr *e; |
380 | |
381 | stdscan_reset(); |
382 | stdscan_set(value); |
383 | tokval.t_type = TOKEN_INVALID; |
384 | e = evaluate(stdscan, NULL, &tokval, NULL, pass2, NULL); |
385 | if (e) { |
386 | if (!is_reloc(e)) |
387 | nasm_error(pass0 == |
388 | 1 ? ERR_NONFATAL : ERR_PANIC, |
389 | "cannot use non-relocatable expression as " |
390 | "ABSOLUTE address" ); |
391 | else { |
392 | absolute.segment = reloc_seg(e); |
393 | absolute.offset = reloc_value(e); |
394 | } |
395 | } else if (passn == 1) |
396 | absolute.offset = 0x100; /* don't go near zero in case of / */ |
397 | else |
398 | nasm_panic(0, "invalid ABSOLUTE address " |
399 | "in pass two" ); |
400 | in_absolute = true; |
401 | location.segment = NO_SEG; |
402 | location.offset = absolute.offset; |
403 | break; |
404 | } |
405 | |
406 | case D_DEBUG: /* [DEBUG] */ |
407 | { |
408 | bool badid, overlong; |
409 | char debugid[128]; |
410 | |
411 | p = value; |
412 | q = debugid; |
413 | badid = overlong = false; |
414 | if (!isidstart(*p)) { |
415 | badid = true; |
416 | } else { |
417 | while (*p && !nasm_isspace(*p)) { |
418 | if (q >= debugid + sizeof debugid - 1) { |
419 | overlong = true; |
420 | break; |
421 | } |
422 | if (!isidchar(*p)) |
423 | badid = true; |
424 | *q++ = *p++; |
425 | } |
426 | *q = 0; |
427 | } |
428 | if (badid) { |
429 | nasm_error(passn == 1 ? ERR_NONFATAL : ERR_PANIC, |
430 | "identifier expected after DEBUG" ); |
431 | break; |
432 | } |
433 | if (overlong) { |
434 | nasm_error(passn == 1 ? ERR_NONFATAL : ERR_PANIC, |
435 | "DEBUG identifier too long" ); |
436 | break; |
437 | } |
438 | p = nasm_skip_spaces(p); |
439 | if (pass0 == 2) |
440 | dfmt->debug_directive(debugid, p); |
441 | break; |
442 | } |
443 | |
444 | case D_WARNING: /* [WARNING {+|-|*}warn-name] */ |
445 | if (!set_warning_status(value)) { |
446 | nasm_error(ERR_WARNING|WARN_UNK_WARNING, |
447 | "unknown warning option: %s" , value); |
448 | } |
449 | break; |
450 | |
451 | case D_CPU: /* [CPU] */ |
452 | cpu = get_cpu(value); |
453 | break; |
454 | |
455 | case D_LIST: /* [LIST {+|-}] */ |
456 | value = nasm_skip_spaces(value); |
457 | if (*value == '+') { |
458 | user_nolist = false; |
459 | } else { |
460 | if (*value == '-') { |
461 | user_nolist = true; |
462 | } else { |
463 | bad_param = true; |
464 | } |
465 | } |
466 | break; |
467 | |
468 | case D_DEFAULT: /* [DEFAULT] */ |
469 | stdscan_reset(); |
470 | stdscan_set(value); |
471 | tokval.t_type = TOKEN_INVALID; |
472 | if (stdscan(NULL, &tokval) != TOKEN_INVALID) { |
473 | switch (tokval.t_integer) { |
474 | case S_REL: |
475 | globalrel = 1; |
476 | break; |
477 | case S_ABS: |
478 | globalrel = 0; |
479 | break; |
480 | case P_BND: |
481 | globalbnd = 1; |
482 | break; |
483 | case P_NOBND: |
484 | globalbnd = 0; |
485 | break; |
486 | default: |
487 | bad_param = true; |
488 | break; |
489 | } |
490 | } else { |
491 | bad_param = true; |
492 | } |
493 | break; |
494 | |
495 | case D_FLOAT: |
496 | if (float_option(value)) { |
497 | nasm_error(pass0 < 2 ? ERR_NONFATAL : ERR_PANIC, |
498 | "unknown 'float' directive: %s" , value); |
499 | } |
500 | break; |
501 | |
502 | case D_PRAGMA: |
503 | process_pragma(value); |
504 | break; |
505 | } |
506 | |
507 | |
508 | /* A common error message */ |
509 | if (bad_param) { |
510 | nasm_error(ERR_NONFATAL, "invalid parameter to [%s] directive" , |
511 | directive); |
512 | } |
513 | |
514 | return d != D_none; |
515 | } |
516 | |