/* ----------------------------------------------------------------------- * * * Copyright 1996-2018 The NASM Authors - All Rights Reserved * See the file AUTHORS included with the NASM distribution for * the specific copyright holders. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following * conditions are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * ----------------------------------------------------------------------- */ /* * Parse and handle assembler directives */ #include "compiler.h" #include #include #include #include #include "nasm.h" #include "nasmlib.h" #include "ilog2.h" #include "error.h" #include "float.h" #include "stdscan.h" #include "preproc.h" #include "eval.h" #include "assemble.h" #include "outform.h" #include "listing.h" #include "labels.h" #include "iflag.h" struct cpunames { const char *name; unsigned int level; /* Eventually a table of features */ }; static iflag_t get_cpu(const char *value) { iflag_t r; const struct cpunames *cpu; static const struct cpunames cpunames[] = { { "8086", IF_8086 }, { "186", IF_186 }, { "286", IF_286 }, { "386", IF_386 }, { "486", IF_486 }, { "586", IF_PENT }, { "pentium", IF_PENT }, { "pentiummmx", IF_PENT }, { "686", IF_P6 }, { "p6", IF_P6 }, { "ppro", IF_P6 }, { "pentiumpro", IF_P6 }, { "p2", IF_P6 }, /* +MMX */ { "pentiumii", IF_P6 }, { "p3", IF_KATMAI }, { "katmai", IF_KATMAI }, { "p4", IF_WILLAMETTE }, { "willamette", IF_WILLAMETTE }, { "prescott", IF_PRESCOTT }, { "x64", IF_X86_64 }, { "x86-64", IF_X86_64 }, { "ia64", IF_IA64 }, { "ia-64", IF_IA64 }, { "itanium", IF_IA64 }, { "itanic", IF_IA64 }, { "merced", IF_IA64 }, { "any", IF_PLEVEL }, { "default", IF_PLEVEL }, { "all", IF_PLEVEL }, { NULL, IF_PLEVEL } /* Error and final default entry */ }; iflag_clear_all(&r); for (cpu = cpunames; cpu->name; cpu++) { if (!strcmp(value, cpu->name)) break; } if (!cpu->name) { nasm_error(pass0 < 2 ? ERR_NONFATAL : ERR_FATAL, "unknown 'cpu' type '%s'", value); } iflag_set_cpu(&r, cpu->level); return r; } static int get_bits(const char *value) { int i = atoi(value); switch (i) { case 16: break; /* Always safe */ case 32: if (!iflag_cpu_level_ok(&cpu, IF_386)) { nasm_error(ERR_NONFATAL, "cannot specify 32-bit segment on processor below a 386"); i = 16; } break; case 64: if (!iflag_cpu_level_ok(&cpu, IF_X86_64)) { nasm_error(ERR_NONFATAL, "cannot specify 64-bit segment on processor below an x86-64"); i = 16; } break; default: nasm_error(pass0 < 2 ? ERR_NONFATAL : ERR_FATAL, "`%s' is not a valid segment size; must be 16, 32 or 64", value); i = 16; break; } return i; } static enum directive parse_directive_line(char **directive, char **value) { char *p, *q, *buf; buf = nasm_skip_spaces(*directive); /* * It should be enclosed in [ ]. * XXX: we don't check there is nothing else on the remainder of the * line, except a possible comment. */ if (*buf != '[') return D_none; q = strchr(buf, ']'); if (!q) return D_corrupt; /* * Strip off the comments. XXX: this doesn't account for quoted * strings inside a directive. We should really strip the * comments in generic code, not here. While we're at it, it * would be better to pass the backend a series of tokens instead * of a raw string, and actually process quoted strings for it, * like of like argv is handled in C. */ p = strchr(buf, ';'); if (p) { if (p < q) /* ouch! somewhere inside */ return D_corrupt; *p = '\0'; } /* no brace, no trailing spaces */ *q = '\0'; nasm_zap_spaces_rev(--q); /* directive */ p = nasm_skip_spaces(++buf); q = nasm_skip_word(p); if (!q) return D_corrupt; /* sigh... no value there */ *q = '\0'; *directive = p; /* and value finally */ p = nasm_skip_spaces(++q); *value = p; return directive_find(*directive); } /* * Process a line from the assembler and try to handle it if it * is a directive. Return true if the line was handled (including * if it was an error), false otherwise. */ bool process_directives(char *directive) { enum directive d; char *value, *p, *q, *special; struct tokenval tokval; bool bad_param = false; int pass2 = passn > 1 ? 2 : 1; d = parse_directive_line(&directive, &value); switch (d) { case D_none: return D_none; /* Not a directive */ case D_corrupt: nasm_error(ERR_NONFATAL, "invalid directive line"); break; default: /* It's a backend-specific directive */ switch (ofmt->directive(d, value, pass2)) { case DIRR_UNKNOWN: goto unknown; case DIRR_OK: case DIRR_ERROR: break; case DIRR_BADPARAM: bad_param = true; break; default: panic(); } break; case D_unknown: unknown: nasm_error(pass0 < 2 ? ERR_NONFATAL : ERR_PANIC, "unrecognised directive [%s]", directive); break; case D_SEGMENT: /* [SEGMENT n] */ case D_SECTION: { int sb = globalbits; int32_t seg = ofmt->section(value, pass2, &sb); if (seg == NO_SEG) { nasm_error(pass0 < 2 ? ERR_NONFATAL : ERR_PANIC, "segment name `%s' not recognized", value); } else { in_absolute = false; location.segment = seg; globalbits = sb; } break; } case D_SECTALIGN: /* [SECTALIGN n] */ { expr *e; if (*value) { stdscan_reset(); stdscan_set(value); tokval.t_type = TOKEN_INVALID; e = evaluate(stdscan, NULL, &tokval, NULL, pass2, NULL); if (e) { uint64_t align = e->value; if (!is_power2(e->value)) { nasm_error(ERR_NONFATAL, "segment alignment `%s' is not power of two", value); } else if (align > UINT64_C(0x7fffffff)) { /* * FIXME: Please make some sane message here * ofmt should have some 'check' method which * would report segment alignment bounds. */ nasm_error(ERR_NONFATAL, "absurdly large segment alignment `%s' (2^%d)", value, ilog2_64(align)); } /* callee should be able to handle all details */ if (location.segment != NO_SEG) ofmt->sectalign(location.segment, align); } } break; } case D_EXTERN: /* [EXTERN label:special] */ if (*value == '$') value++; /* skip initial $ if present */ if (pass0 == 2) { q = value; while (*q && *q != ':') q++; if (*q == ':') { *q++ = '\0'; ofmt->symdef(value, 0L, 0L, 3, q); } } else if (passn == 1) { bool validid = true; q = value; if (!isidstart(*q)) validid = false; while (*q && *q != ':') { if (!isidchar(*q)) validid = false; q++; } if (!validid) { nasm_error(ERR_NONFATAL, "identifier expected after EXTERN"); break; } if (*q == ':') { *q++ = '\0'; special = q; } else special = NULL; if (!is_extern(value)) { /* allow re-EXTERN to be ignored */ int temp = pass0; pass0 = 1; /* fake pass 1 in labels.c */ declare_as_global(value, special); define_label(value, seg_alloc(), 0L, NULL, false, true); pass0 = temp; } } /* else pass0 == 1 */ break; case D_BITS: /* [BITS bits] */ globalbits = get_bits(value); break; case D_GLOBAL: /* [GLOBAL symbol:special] */ if (*value == '$') value++; /* skip initial $ if present */ if (pass0 == 2) { /* pass 2 */ q = value; while (*q && *q != ':') q++; if (*q == ':') { *q++ = '\0'; ofmt->symdef(value, 0L, 0L, 3, q); } } else if (pass2 == 1) { /* pass == 1 */ bool validid = true; q = value; if (!isidstart(*q)) validid = false; while (*q && *q != ':') { if (!isidchar(*q)) validid = false; q++; } if (!validid) { nasm_error(ERR_NONFATAL, "identifier expected after GLOBAL"); break; } if (*q == ':') { *q++ = '\0'; special = q; } else special = NULL; declare_as_global(value, special); } /* pass == 1 */ break; case D_COMMON: /* [COMMON symbol size:special] */ { int64_t size; bool rn_error; bool validid; if (*value == '$') value++; /* skip initial $ if present */ p = value; validid = true; if (!isidstart(*p)) validid = false; while (*p && !nasm_isspace(*p)) { if (!isidchar(*p)) validid = false; p++; } if (!validid) { nasm_error(ERR_NONFATAL, "identifier expected after COMMON"); break; } if (*p) { p = nasm_zap_spaces_fwd(p); q = p; while (*q && *q != ':') q++; if (*q == ':') { *q++ = '\0'; special = q; } else { special = NULL; } size = readnum(p, &rn_error); if (rn_error) { nasm_error(ERR_NONFATAL, "invalid size specified" " in COMMON declaration"); break; } } else { nasm_error(ERR_NONFATAL, "no size specified in" " COMMON declaration"); break; } if (pass0 < 2) { define_common(value, seg_alloc(), size, special); } else if (pass0 == 2) { if (special) ofmt->symdef(value, 0L, 0L, 3, special); } break; } case D_ABSOLUTE: /* [ABSOLUTE address] */ { expr *e; stdscan_reset(); stdscan_set(value); tokval.t_type = TOKEN_INVALID; e = evaluate(stdscan, NULL, &tokval, NULL, pass2, NULL); if (e) { if (!is_reloc(e)) nasm_error(pass0 == 1 ? ERR_NONFATAL : ERR_PANIC, "cannot use non-relocatable expression as " "ABSOLUTE address"); else { absolute.segment = reloc_seg(e); absolute.offset = reloc_value(e); } } else if (passn == 1) absolute.offset = 0x100; /* don't go near zero in case of / */ else nasm_panic(0, "invalid ABSOLUTE address " "in pass two"); in_absolute = true; location.segment = NO_SEG; break; } case D_DEBUG: /* [DEBUG] */ { bool badid, overlong; char debugid[128]; p = value; q = debugid; badid = overlong = false; if (!isidstart(*p)) { badid = true; } else { while (*p && !nasm_isspace(*p)) { if (q >= debugid + sizeof debugid - 1) { overlong = true; break; } if (!isidchar(*p)) badid = true; *q++ = *p++; } *q = 0; } if (badid) { nasm_error(passn == 1 ? ERR_NONFATAL : ERR_PANIC, "identifier expected after DEBUG"); break; } if (overlong) { nasm_error(passn == 1 ? ERR_NONFATAL : ERR_PANIC, "DEBUG identifier too long"); break; } p = nasm_skip_spaces(p); if (pass0 == 2) dfmt->debug_directive(debugid, p); break; } case D_WARNING: /* [WARNING {+|-|*}warn-name] */ if (!set_warning_status(value)) { nasm_error(ERR_WARNING|ERR_WARN_UNK_WARNING, "unknown warning option: %s", value); } break; case D_CPU: /* [CPU] */ cpu = get_cpu(value); break; case D_LIST: /* [LIST {+|-}] */ value = nasm_skip_spaces(value); if (*value == '+') { user_nolist = false; } else { if (*value == '-') { user_nolist = true; } else { bad_param = true; } } break; case D_DEFAULT: /* [DEFAULT] */ stdscan_reset(); stdscan_set(value); tokval.t_type = TOKEN_INVALID; if (stdscan(NULL, &tokval) != TOKEN_INVALID) { switch (tokval.t_integer) { case S_REL: globalrel = 1; break; case S_ABS: globalrel = 0; break; case P_BND: globalbnd = 1; break; case P_NOBND: globalbnd = 0; break; default: bad_param = true; break; } } else { bad_param = true; } break; case D_FLOAT: if (float_option(value)) { nasm_error(pass0 < 2 ? ERR_NONFATAL : ERR_PANIC, "unknown 'float' directive: %s", value); } break; case D_PRAGMA: process_pragma(value); break; } /* A common error message */ if (bad_param) { nasm_error(ERR_NONFATAL, "invalid parameter to [%s] directive", directive); } return d != D_none; }