diff --git a/Makefile.in b/Makefile.in index 6c308f23..450bed53 100644 --- a/Makefile.in +++ b/Makefile.in @@ -143,6 +143,7 @@ LIBOBJ_W = \ asm/preproc.$(O) asm/quote.$(O) \ asm/listing.$(O) asm/eval.$(O) asm/exprlib.$(O) asm/exprdump.$(O) \ asm/stdscan.$(O) \ + asm/getbool.$(O) \ asm/strfunc.$(O) \ asm/segalloc.$(O) \ asm/rdstrnum.$(O) \ diff --git a/asm/assemble.h b/asm/assemble.h index 0423957b..26da8b51 100644 --- a/asm/assemble.h +++ b/asm/assemble.h @@ -1,6 +1,6 @@ /* ----------------------------------------------------------------------- * * - * Copyright 1996-2024 The NASM Authors - All Rights Reserved + * Copyright 1996-2025 The NASM Authors - All Rights Reserved * See the file AUTHORS included with the NASM distribution for * the specific copyright holders. * @@ -40,6 +40,7 @@ #include "nasm.h" #include "iflag.h" +#include "asmutil.h" extern iflag_t cpu, cmd_cpu; void set_cpu(const char *cpuspec); @@ -50,6 +51,7 @@ extern struct location absolute; int64_t increment_offset(int64_t delta); void process_insn(insn *instruction); +bool directive_valid(const char *); bool process_directives(char *); void process_pragma(char *); diff --git a/asm/directiv.c b/asm/directiv.c index c886a9bc..92f1fc16 100644 --- a/asm/directiv.c +++ b/asm/directiv.c @@ -206,66 +206,6 @@ static int get_bits(const char *value) return i; } -/* - * 1. An expression (true if nonzero 0) - * 2. The keywords true, on, yes for true - * 3. The keywords false, off, no for false - * 4. An empty line, for true - * - * This is equivalent to pp_get_boolean_option() outside of the - * preprocessor. - * - * On error, return defval (usually the previous value) - */ -static bool get_boolean_option(char **strp, bool defval) -{ - static const char * const noyes[] = { - "no", "yes", - "false", "true", - "off", "on" - }; - bool result = true; - struct tokenval tokval; - expr *evalresult; - int tt; - - tokval.t_type = TOKEN_INVALID; - tokval.t_start = *strp; - stdscan_reset(*strp); - - tt = stdscan(NULL, &tokval); - if (tt == TOKEN_EOS || tt == ']') { - result = true; - goto done; - } - - if (tt == TOKEN_ID) { - size_t i; - for (i = 0; i < ARRAY_SIZE(noyes); i++) - if (!nasm_stricmp(tokval.t_charptr, noyes[i])) { - result = i & 1; - goto done; - } - } - - evalresult = evaluate(stdscan, NULL, &tokval, NULL, true, NULL); - - if (!evalresult) - goto done; - - if (!is_really_simple(evalresult)) { - nasm_nonfatal("boolean flag expression must be a constant"); - result = defval; - goto done; - } - - result = reloc_value(evalresult) != 0; - -done: - *strp = (char *)tokval.t_start; - return result; -} - static enum directive parse_directive_line(char **directive, char **value) { char *p, *q, *buf; @@ -317,6 +257,26 @@ static enum directive parse_directive_line(char **directive, char **value) return directive_find(*directive); } +/* + * Check to see if a string matches a valid directive name (sans [], + * whitespace must be already trimmed.) + */ +bool directive_valid(const char *directive) +{ + enum directive d; + + d = directive_find(directive); + + if (d <= D_corrupt) + return false; + else if (d < D_ofmt) + return true; /* Global directive or pseudo-op */ + else if (d < D_pragma_tokens) + return ofmt->directive(d, NULL) == DIRR_OK; + else + return false; +} + /* * Process a line from the assembler and try to handle it if it * is a directive. Return true if the line was handled (including @@ -340,18 +300,21 @@ bool process_directives(char *directive) nasm_nonfatal("invalid directive line"); break; - default: /* It's a backend-specific directive */ - switch (ofmt->directive(d, value)) { - case DIRR_UNKNOWN: - goto unknown; - case DIRR_OK: - case DIRR_ERROR: - break; - case DIRR_BADPARAM: - bad_param = true; - break; - default: - panic(); + default: + if (d > D_ofmt && d < D_pragma_tokens) { + /* It's a backend-specific directive */ + switch (ofmt->directive(d, value)) { + case DIRR_UNKNOWN: + goto unknown; + case DIRR_OK: + case DIRR_ERROR: + break; + case DIRR_BADPARAM: + bad_param = true; + break; + default: + panic(); + } } break; @@ -623,7 +586,7 @@ bool process_directives(char *directive) break; case D_DOLLARHEX: - globl.dollarhex = get_boolean_option(&value, globl.dollarhex); + get_boolean_option(value, &globl.dollarhex); break; case D_PRAGMA: diff --git a/asm/directiv.dat b/asm/directiv.dat index f7653c0f..72417670 100644 --- a/asm/directiv.dat +++ b/asm/directiv.dat @@ -79,7 +79,29 @@ sectalign pragma required +; --- Pseudo-op list, for the benefit of %isdirective +#special pseudo_ops +db +dw +dd +dq +dt +do +dy +dz +resb +resw +resd +resq +rest +reso +resy +resz +incbin +equ + ; --- Format-specific directives +#special ofmt export ; outcoff, outobj group ; outobj import ; outobj @@ -91,6 +113,9 @@ osabi ; outelf safeseh ; outcoff uppercase ; outieee, outobj +; --- The following are tokens used in pragmas, not actual directives +#special pragma_tokens + ; --- Assembler pragmas prefix suffix diff --git a/asm/eval.h b/asm/eval.h index ba471a22..03476f75 100644 --- a/asm/eval.h +++ b/asm/eval.h @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- * - * + * * Copyright 1996-2009 The NASM Authors - All Rights Reserved * See the file AUTHORS included with the NASM distribution for * the specific copyright holders. @@ -14,7 +14,7 @@ * 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 @@ -31,7 +31,7 @@ * * ----------------------------------------------------------------------- */ -/* +/* * eval.h header file for eval.c */ diff --git a/asm/getbool.c b/asm/getbool.c new file mode 100644 index 00000000..af3f4d66 --- /dev/null +++ b/asm/getbool.c @@ -0,0 +1,106 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2025 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. + * + * ----------------------------------------------------------------------- */ + +#include "compiler.h" +#include "nasm.h" +#include "asmutil.h" +#include "stdscan.h" +#include "eval.h" + +/* + * 1. An expression (true if nonzero 0) + * 2. The keywords true, on, yes for true + * 3. The keywords false, off, no for false + * 4. An empty line, for true + * + * This is equivalent to pp_get_boolean_option() outside of the + * preprocessor. + * + * On error, return defval (usually the previous value) + * + * If str is NULL, return NULL without changing *val. + */ +char *get_boolean_option(const char *str, bool *val) +{ + static const char * const noyes[] = { + "no", "yes", + "false", "true", + "off", "on" + }; + struct tokenval tokval; + expr *evalresult; + char *p; + int tt; + + if (!str) + return NULL; + + str = nasm_skip_spaces(str); + p = nasm_strdup(str); + + tokval.t_type = TOKEN_INVALID; + tokval.t_start = str; + stdscan_reset(p); + + tt = stdscan(NULL, &tokval); + if (tt == TOKEN_EOS || tt == ']' || tt == ',') { + *val = true; + goto done; + } + + if (tt == TOKEN_ID) { + size_t i; + for (i = 0; i < ARRAY_SIZE(noyes); i++) + if (!nasm_stricmp(tokval.t_charptr, noyes[i])) { + *val = i & 1; + goto done; + } + } + + evalresult = evaluate(stdscan, NULL, &tokval, NULL, true, NULL); + + if (!evalresult) + goto done; + + if (!is_really_simple(evalresult)) { + nasm_nonfatal("boolean flag expression must be a constant"); + goto done; + } + + *val = reloc_value(evalresult) != 0; + +done: + str += nasm_skip_spaces(tokval.t_start) - p; + nasm_free(p); + return (char *)str; +} diff --git a/asm/pptok.dat b/asm/pptok.dat index d05c3030..ebb524fe 100644 --- a/asm/pptok.dat +++ b/asm/pptok.dat @@ -46,6 +46,7 @@ *def *defalias *difi +*directive *empty *env *file diff --git a/asm/preproc.c b/asm/preproc.c index 6bdc0bc4..035db74e 100644 --- a/asm/preproc.c +++ b/asm/preproc.c @@ -66,6 +66,7 @@ #include "nasm.h" #include "nasmlib.h" +#include "assemble.h" #include "error.h" #include "preproc.h" #include "hashtbl.h" @@ -2906,6 +2907,69 @@ static Token **count_mmac_params(Token *tline, int *nparamp, Token ***paramsp) return comma; } +/* Check to see if a string is a valid preprocessor directive */ + +/* This requires that the caller has checked dname[0] == '%' */ +static inline enum preproc_token pp_get_nasm_directive(const char *dname) +{ + /* + * For it to be a directive, the second character has to be an + * ASCII letter; this is a very quick and dirty test for that; + * all other cases will get rejected by the token hash. + */ + if (likely((uint8_t)((dname[1] & ~0x20) - 'A') <= 'Z')) + return pp_token_hash(dname); + + return PP_invalid; +} + +static inline enum preproc_token pp_get_tasm_directive(const char *dname) +{ + if (likely(!(ppopt & PP_TASM))) + return PP_invalid; + + /* + * Directive in TASM mode. Again, must begin with a letter. + */ + if ((uint8_t)((dname[0] & ~0x20) - 'A') <= 'Z') + return pp_tasm_token_hash(dname); + + return PP_invalid; +} + +static enum preproc_token pp_get_directive(const char *dname) +{ + if (dname[0] == '%') + return pp_get_nasm_directive(dname); + else + return pp_get_tasm_directive(dname); +} + +static bool is_directive(const char *dname) +{ + char *p; + const char *q; + bool j; + + dname = nasm_skip_spaces(dname); + + if (*dname == '[') { + dname = nasm_skip_spaces(dname+1); + } else { + if (*dname == '%') + return pp_get_nasm_directive(dname) != PP_invalid; + + if (pp_get_tasm_directive(dname) != PP_invalid) + return true; + } + + q = nasm_skip_word(dname); + p = nasm_strndup(dname, q-dname); + j = directive_valid(p); + nasm_free(p); + return j; +} + /* * Determine whether one of the various `if' conditions is true or * not. @@ -2982,6 +3046,13 @@ if_condition(Token * tline, enum preproc_token ct, const char *dname) j = false; goto fail; + case PP_IFDIRECTIVE: + tline = skip_white(expand_smacro(tline)); + j = false; + if (tline) + j = is_directive(unquote_token(tline)); + break; + case PP_IFENV: tline = expand_smacro(tline); j = false; /* have we matched yet? */ @@ -4356,30 +4427,20 @@ static int do_directive(Token *tline, Token **output, bool suppressed) if (!tline) return NO_DIRECTIVE_FOUND; + dname = tok_text(tline); + switch (tline->type) { case TOKEN_PREPROC_ID: - dname = tok_text(tline); - /* - * For it to be a directive, the second character has to be an - * ASCII letter; this is a very quick and dirty test for that; - * all other cases will get rejected by the token hash. - */ - if ((uint8_t)(dname[1] - 'A') > (uint8_t)('z' - 'A')) - return NO_DIRECTIVE_FOUND; - - op = pp_token_hash(dname); + op = pp_get_nasm_directive(dname); break; case TOKEN_ID: - if (likely(!(ppopt & PP_TASM))) - return NO_DIRECTIVE_FOUND; - - dname = tok_text(tline); - op = pp_tasm_token_hash(dname); + op = pp_get_tasm_directive(tok_text(tline)); break; default: - return NO_DIRECTIVE_FOUND; + op = PP_invalid; + break; } switch (op) { @@ -4398,6 +4459,9 @@ static int do_directive(Token *tline, Token **output, bool suppressed) break; } + if (op == PP_invalid) + return NO_DIRECTIVE_FOUND; + if (unlikely(ppopt & PP_TRIVIAL)) goto done; @@ -7994,11 +8058,9 @@ stdmac_user_error(const SMacro *s, Token **params, int nparam) */ static SMacro *define_magic(const char *mname, bool casesense, SMacro *tmpl) { - if (mname[0] == '%') { - enum preproc_token op = pp_token_hash(mname); - if (op != PP_invalid) - pp_op_may_be_function[op] = true; - } + enum preproc_token op = pp_get_directive(mname); + if (op != PP_invalid) + pp_op_may_be_function[op] = true; /* Magic functions can be recursive */ if (tmpl && tmpl->nparam && tmpl->expand) diff --git a/doc/preproc.src b/doc/preproc.src index 12cc243e..c0ff1896 100644 --- a/doc/preproc.src +++ b/doc/preproc.src @@ -1812,6 +1812,34 @@ any tokens at all, whitespace excepted. The usual \i\c{%elifempty}, \i\c\{%ifnempty}, and \i\c{%elifnempty} variants are also provided. +\S{ifdirective} \i\c{%ifdirective}: Test If a Directive Is Supported + +The conditional assembly construct \c{%ifdirective} assembles the +subsequent code if and only if followed by a token that corresponds to +a preprocessor directive, assembler directive (see \k{directive}) or a +pseudo-instruction (see \k{pseudop}) supported in the current version +of NASM. + +The argument can be a quoted string to prevent macro expansion, in +which case it is unquoted before the test, that is, these two lines do +the same thing: + +\c %ifdirective %ifndef +\c %ifdirective "%ifndef" + +Preprocessor directives must be specified with a leading \c{%} sign +(except for certain directives in TASM mode); assembler directives +\e{may} be specified with surrounding brackets \c{[]}, but those are +not required. + +Some assembly directives can be supported in some contexts and not +others, for example, most output formats do not support the \c{ORG} +directive, therefore the result of \c{%ifdirective} may depend on more +than just the current version of NASM. + +The usual \i\c{%elifdirective}, \i\c\{%ifndirective}, and \i\c{%elifndirective} +variants are also provided. + \S{ifenv} \i\c{%ifenv}: Test If Environment Variable Exists The conditional assembly construct \c{%ifenv} assembles the @@ -2356,7 +2384,7 @@ The \c{%$localsize} variable is used internally by the current context before the \c{%local} directive may be used. Failure to do so will result in one expression syntax error for each \c{%local} variable declared. It then may be used in -the construction of an appropriately sized ENTER instruction +the construction of an appropriately sized \c{ENTER} instruction as shown in the example. @@ -2471,7 +2499,8 @@ output is either \c{win32} or \c{win64}: \S{pragma-preproc} Preprocessor Pragmas -The only preprocessor \c{%pragma} defined in NASM 2.15 is: +The only preprocessor \c{%pragma} defined as the current version of +NASM is: \b \c{%pragma preproc sane_empty_expansion}: disables legacy compatibility handling of braceless empty arguments to multi-line diff --git a/include/nasm.h b/include/nasm.h index ea0979a5..84b10210 100644 --- a/include/nasm.h +++ b/include/nasm.h @@ -1164,6 +1164,16 @@ struct ofmt { * 2 - DIRR_ERROR - backend printed its own error message * 3 - DIRR_BADPARAM - print the generic message * "invalid parameter to [*] directive" + * + * When called with "value" as NULL (as opposed to the empty + * string!), it should perform no action and not print any + * messages, but return DIRR_UNKNOWN if this directive is + * unsupported by the backend, DIRR_OK if it is, and either + * DIRR_ERROR or DIRR_BADPARAM if it *would* be supported by the + * backend if configured otherwise, but it would be invalid in the + * current context, regardless of the parameter(s). + * + * This is used by %ifdirective. */ enum directive_result (*directive)(enum directive directive, char *value); diff --git a/output/nullout.c b/output/nullout.c index 121fe70b..e9907cbd 100644 --- a/output/nullout.c +++ b/output/nullout.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- * - * + * * Copyright 1996-2018 The NASM Authors - All Rights Reserved * See the file AUTHORS included with the NASM distribution for * the specific copyright holders. @@ -14,7 +14,7 @@ * 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 diff --git a/output/outbin.c b/output/outbin.c index 874b2896..21297791 100644 --- a/output/outbin.c +++ b/output/outbin.c @@ -1265,80 +1265,83 @@ bin_directive(enum directive directive, char *args) { switch (directive) { case D_ORG: - { - struct tokenval tokval; - uint64_t value; - expr *e; + if (args) { + struct tokenval tokval; + uint64_t value; + expr *e; - stdscan_reset(args); - tokval.t_type = TOKEN_INVALID; - e = evaluate(stdscan, NULL, &tokval, NULL, 1, NULL); - if (e) { - if (!is_really_simple(e)) - nasm_nonfatal("org value must be a critical" - " expression"); - else { + stdscan_reset(args); + tokval.t_type = TOKEN_INVALID; + e = evaluate(stdscan, NULL, &tokval, NULL, 1, NULL); + if (!e) { + nasm_nonfatal("No or invalid offset specified" + " in ORG directive."); + return DIRR_ERROR; + } else if (!is_really_simple(e)) { + nasm_nonfatal("ORG value must be a critical" + " expression"); + return DIRR_ERROR; + } else { value = reloc_value(e); /* Check for ORG redefinition. */ - if (origin_defined && (value != origin)) + if (origin_defined && (value != origin)) { nasm_nonfatal("program origin redefined"); - else { + return DIRR_ERROR; + } else { origin = value; origin_defined = 1; } } - } else - nasm_nonfatal("No or invalid offset specified" - " in ORG directive."); - return DIRR_OK; - } - case D_MAP: - { - /* The 'map' directive allows the user to generate section - * and symbol information to stdout, stderr, or to a file. */ - char *p; - - if (!pass_first()) - return DIRR_OK; - args += strspn(args, " \t"); - while (*args) { - p = args; - args += strcspn(args, " \t"); - if (*args != '\0') - *(args++) = '\0'; - if (!nasm_stricmp(p, "all")) - map_control |= - MAP_ORIGIN | MAP_SUMMARY | MAP_SECTIONS | MAP_SYMBOLS; - else if (!nasm_stricmp(p, "brief")) - map_control |= MAP_ORIGIN | MAP_SUMMARY; - else if (!nasm_stricmp(p, "sections")) - map_control |= MAP_ORIGIN | MAP_SUMMARY | MAP_SECTIONS; - else if (!nasm_stricmp(p, "segments")) - map_control |= MAP_ORIGIN | MAP_SUMMARY | MAP_SECTIONS; - else if (!nasm_stricmp(p, "symbols")) - map_control |= MAP_SYMBOLS; - else if (!rf) { - if (!nasm_stricmp(p, "stdout")) - rf = stdout; - else if (!nasm_stricmp(p, "stderr")) - rf = stderr; - else { /* Must be a filename. */ - rf = nasm_open_write(p, NF_TEXT); - if (!rf) { - nasm_warn(WARN_OTHER, "unable to open map file `%s'", p); - map_control = 0; - return DIRR_OK; - } - } - } else - nasm_warn(WARN_OTHER, "map file already specified"); } - if (map_control == 0) - map_control |= MAP_ORIGIN | MAP_SUMMARY; - if (!rf) - rf = stdout; return DIRR_OK; - } + + case D_MAP: + /* The 'map' directive allows the user to generate section + * and symbol information to stdout, stderr, or to a file. */ + if (args && pass_first()) { + char *p; + + args += strspn(args, " \t"); + while (*args) { + p = args; + args += strcspn(args, " \t"); + if (*args != '\0') + *(args++) = '\0'; + if (!nasm_stricmp(p, "all")) + map_control |= + MAP_ORIGIN | MAP_SUMMARY | MAP_SECTIONS | MAP_SYMBOLS; + else if (!nasm_stricmp(p, "brief")) + map_control |= MAP_ORIGIN | MAP_SUMMARY; + else if (!nasm_stricmp(p, "sections")) + map_control |= MAP_ORIGIN | MAP_SUMMARY | MAP_SECTIONS; + else if (!nasm_stricmp(p, "segments")) + map_control |= MAP_ORIGIN | MAP_SUMMARY | MAP_SECTIONS; + else if (!nasm_stricmp(p, "symbols")) + map_control |= MAP_SYMBOLS; + else if (!rf) { + if (!nasm_stricmp(p, "stdout")) + rf = stdout; + else if (!nasm_stricmp(p, "stderr")) + rf = stderr; + else { /* Must be a filename. */ + rf = nasm_open_write(p, NF_TEXT); + if (!rf) { + nasm_warn(WARN_OTHER, "unable to open map file `%s'", p); + map_control = 0; + return DIRR_OK; + } + } + } else { + nasm_warn(WARN_OTHER, "map file already specified"); + } + } + if (map_control == 0) + map_control |= MAP_ORIGIN | MAP_SUMMARY; + if (!rf) + rf = stdout; + } + return DIRR_OK; + default: return DIRR_UNKNOWN; } diff --git a/output/outcoff.c b/output/outcoff.c index 1c0bb587..5d63d072 100644 --- a/output/outcoff.c +++ b/output/outcoff.c @@ -904,7 +904,7 @@ coff_directives(enum directive directive, char *value) * is probably to mark a label as an export in the label * structure, in case the label doesn't actually exist. */ - if (!pass_first()) + if (!value || !pass_first()) return DIRR_OK; /* ignore in pass two */ name = q = value; while (*q && !nasm_isspace(*q)) @@ -932,7 +932,10 @@ coff_directives(enum directive directive, char *value) int i; if (!win32) /* Only applicable for -f win32 */ - return 0; + return DIRR_UNKNOWN; + + if (!value) + return DIRR_OK; if (sxseg == -1) { for (i = 0; i < coff_nsects; i++) diff --git a/output/outdbg.c b/output/outdbg.c index 5fbfa032..218dd8a6 100644 --- a/output/outdbg.c +++ b/output/outdbg.c @@ -360,6 +360,8 @@ dbg_directive(enum directive directive, char *value) fprintf(ofile, "directive [%s] value [%s] pass %"PRId64" (%s)\n", directive_dname(directive), value, pass_count(), pass_type_name()); + + /* The debug output format accepts all known directives */ return DIRR_OK; } diff --git a/output/outelf.c b/output/outelf.c index 20c4439c..33dfd37e 100644 --- a/output/outelf.c +++ b/output/outelf.c @@ -438,7 +438,7 @@ elf_directive(enum directive directive, char *value) switch (directive) { case D_OSABI: - if (!pass_first()) /* XXX: Why? */ + if (!value || !pass_first()) return DIRR_OK; n = readnum(value, &err); diff --git a/output/outieee.c b/output/outieee.c index 71415bf4..53a7eb53 100644 --- a/output/outieee.c +++ b/output/outieee.c @@ -73,6 +73,7 @@ #include "nasm.h" #include "nasmlib.h" +#include "asmutil.h" #include "error.h" #include "ver.h" @@ -84,7 +85,7 @@ #define ARRAY_BOT 0x1 static char ieee_infile[FILENAME_MAX]; -static int ieee_uppercase; +static bool ieee_uppercase; static bool any_segs; static int arrindex; @@ -829,7 +830,7 @@ ieee_directive(enum directive directive, char *value) switch (directive) { case D_UPPERCASE: - ieee_uppercase = true; + get_boolean_option(value, &ieee_uppercase); return DIRR_OK; default: diff --git a/output/outobj.c b/output/outobj.c index 76866293..2279462c 100644 --- a/output/outobj.c +++ b/output/outobj.c @@ -43,6 +43,7 @@ #include "nasm.h" #include "nasmlib.h" +#include "asmutil.h" #include "error.h" #include "stdscan.h" #include "eval.h" @@ -1575,9 +1576,9 @@ obj_directive(enum directive directive, char *value) { switch (directive) { case D_GROUP: - { - char *p, *q, *v; - if (pass_first()) { /* XXX */ + /* Is pass_first() really correct? */ + if (value && pass_first()) { + char *p, *q, *v; struct Group *grp; struct Segment *seg; struct External **extp; @@ -1682,95 +1683,18 @@ obj_directive(enum directive directive, char *value) } } return DIRR_OK; - } + case D_UPPERCASE: - obj_uppercase = true; + if (value) + get_boolean_option(value, &obj_uppercase); return DIRR_OK; case D_IMPORT: - { - char *q, *extname, *libname, *impname; + /* Is pass_first() really correct? */ + if (value && pass_first()) { + char *q, *extname, *libname, *impname; - if (!pass_first()) /* XXX */ - return DIRR_OK; - extname = q = value; - while (*q && !nasm_isspace(*q)) - q++; - if (nasm_isspace(*q)) { - *q++ = '\0'; - while (*q && nasm_isspace(*q)) - q++; - } - - libname = q; - while (*q && !nasm_isspace(*q)) - q++; - if (nasm_isspace(*q)) { - *q++ = '\0'; - while (*q && nasm_isspace(*q)) - q++; - } - - impname = q; - - if (!*extname || !*libname) - nasm_nonfatal("`import' directive requires symbol name" - " and library name"); - else { - struct ImpDef *imp; - bool err = false; - - imp = *imptail = nasm_malloc(sizeof(struct ImpDef)); - imptail = &imp->next; - imp->next = NULL; - imp->extname = nasm_strdup(extname); - imp->libname = nasm_strdup(libname); - imp->impindex = readnum(impname, &err); - if (!*impname || err) - imp->impname = nasm_strdup(impname); - else - imp->impname = NULL; - } - - return DIRR_OK; - } - case D_EXPORT: - { - char *q, *extname, *intname, *v; - struct ExpDef *export; - int flags = 0; - unsigned int ordinal = 0; - - if (!pass_first()) - return DIRR_OK; /* ignore in pass two */ - intname = q = value; - while (*q && !nasm_isspace(*q)) - q++; - if (nasm_isspace(*q)) { - *q++ = '\0'; - while (*q && nasm_isspace(*q)) - q++; - } - - extname = q; - while (*q && !nasm_isspace(*q)) - q++; - if (nasm_isspace(*q)) { - *q++ = '\0'; - while (*q && nasm_isspace(*q)) - q++; - } - - if (!*intname) { - nasm_nonfatal("`export' directive requires export name"); - return DIRR_OK; - } - if (!*extname) { - extname = intname; - intname = ""; - } - while (*q) { - v = q; + extname = q = value; while (*q && !nasm_isspace(*q)) q++; if (nasm_isspace(*q)) { @@ -1778,38 +1702,114 @@ obj_directive(enum directive directive, char *value) while (*q && nasm_isspace(*q)) q++; } - if (!nasm_stricmp(v, "resident")) - flags |= EXPDEF_FLAG_RESIDENT; - else if (!nasm_stricmp(v, "nodata")) - flags |= EXPDEF_FLAG_NODATA; - else if (!nasm_strnicmp(v, "parm=", 5)) { + + libname = q; + while (*q && !nasm_isspace(*q)) + q++; + if (nasm_isspace(*q)) { + *q++ = '\0'; + while (*q && nasm_isspace(*q)) + q++; + } + + impname = q; + + if (!*extname || !*libname) + nasm_nonfatal("`import' directive requires symbol name" + " and library name"); + else { + struct ImpDef *imp; bool err = false; - flags |= EXPDEF_MASK_PARMCNT & readnum(v + 5, &err); - if (err) { - nasm_nonfatal("value `%s' for `parm' is non-numeric", v + 5); - return DIRR_ERROR; - } - } else { - bool err = false; - ordinal = readnum(v, &err); - if (err) { - nasm_nonfatal("unrecognised export qualifier `%s'", v); - return DIRR_ERROR; - } - flags |= EXPDEF_FLAG_ORDINAL; + + imp = *imptail = nasm_malloc(sizeof(struct ImpDef)); + imptail = &imp->next; + imp->next = NULL; + imp->extname = nasm_strdup(extname); + imp->libname = nasm_strdup(libname); + imp->impindex = readnum(impname, &err); + if (!*impname || err) + imp->impname = nasm_strdup(impname); + else + imp->impname = NULL; } } - - export = *exptail = nasm_malloc(sizeof(struct ExpDef)); - exptail = &export->next; - export->next = NULL; - export->extname = nasm_strdup(extname); - export->intname = nasm_strdup(intname); - export->ordinal = ordinal; - export->flags = flags; - return DIRR_OK; - } + + case D_EXPORT: + /* Is pass_first() really correct? */ + if (value && pass_first()) { + char *q, *extname, *intname, *v; + struct ExpDef *export; + int flags = 0; + unsigned int ordinal = 0; + + intname = q = value; + while (*q && !nasm_isspace(*q)) + q++; + if (nasm_isspace(*q)) { + *q++ = '\0'; + while (*q && nasm_isspace(*q)) + q++; + } + + extname = q; + while (*q && !nasm_isspace(*q)) + q++; + if (nasm_isspace(*q)) { + *q++ = '\0'; + while (*q && nasm_isspace(*q)) + q++; + } + + if (!*intname) { + nasm_nonfatal("`export' directive requires export name"); + return DIRR_OK; + } + if (!*extname) { + extname = intname; + intname = ""; + } + while (*q) { + v = q; + while (*q && !nasm_isspace(*q)) + q++; + if (nasm_isspace(*q)) { + *q++ = '\0'; + while (*q && nasm_isspace(*q)) + q++; + } + if (!nasm_stricmp(v, "resident")) + flags |= EXPDEF_FLAG_RESIDENT; + else if (!nasm_stricmp(v, "nodata")) + flags |= EXPDEF_FLAG_NODATA; + else if (!nasm_strnicmp(v, "parm=", 5)) { + bool err = false; + flags |= EXPDEF_MASK_PARMCNT & readnum(v + 5, &err); + if (err) { + nasm_nonfatal("value `%s' for `parm' is non-numeric", v + 5); + return DIRR_ERROR; + } + } else { + bool err = false; + ordinal = readnum(v, &err); + if (err) { + nasm_nonfatal("unrecognised export qualifier `%s'", v); + return DIRR_ERROR; + } + flags |= EXPDEF_FLAG_ORDINAL; + } + } + + export = *exptail = nasm_malloc(sizeof(struct ExpDef)); + exptail = &export->next; + export->next = NULL; + export->extname = nasm_strdup(extname); + export->intname = nasm_strdup(intname); + export->ordinal = ordinal; + export->flags = flags; + } + return DIRR_OK; + default: return DIRR_UNKNOWN; } diff --git a/test/directive.asm b/test/directive.asm new file mode 100644 index 00000000..96c158cd --- /dev/null +++ b/test/directive.asm @@ -0,0 +1,15 @@ +%macro dir 1 + %assign y %isdirective(%1) + %xdefine c %cond(y,YES,NO) + db "Directive ", %str(%1), %cond(y,""," not"), ` valid\r\n` +%endmacro + + dir db + dir %iffile + dir iffile + dir [%iffile] + dir [extern] + dir extern + dir %extern + dir org + dir uppercase diff --git a/x86/insns.dat b/x86/insns.dat index a8297cf7..eedacd84 100644 --- a/x86/insns.dat +++ b/x86/insns.dat @@ -50,6 +50,7 @@ ;# Special instructions (pseudo-ops) ; These MUST be first in this file and must maintain the pattern of ; Dx by size, RESx by size, INCBIN, and EQU in that order. +; These are also listed in directiv.dat. DB ignore ignore PSEUDO DW ignore ignore PSEUDO DD ignore ignore PSEUDO