From 31a160759d93a574c9863538d6109a23cf94873d Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 4 Sep 2025 19:41:12 -0700 Subject: [PATCH] Add %ifdirective preprocessor directive Along with C and other languages, the current trend is to be able to probe for features rather than relying on version numbers. This is motivated in part by the intent of bumping the major version number to 3. Signed-off-by: H. Peter Anvin (Intel) --- Makefile.in | 1 + asm/assemble.h | 4 +- asm/directiv.c | 109 +++++++-------------- asm/directiv.dat | 25 +++++ asm/eval.h | 6 +- asm/getbool.c | 106 +++++++++++++++++++++ asm/pptok.dat | 1 + asm/preproc.c | 104 +++++++++++++++----- doc/preproc.src | 33 ++++++- include/nasm.h | 10 ++ output/nullout.c | 4 +- output/outbin.c | 131 +++++++++++++------------- output/outcoff.c | 7 +- output/outdbg.c | 2 + output/outelf.c | 2 +- output/outieee.c | 5 +- output/outobj.c | 230 ++++++++++++++++++++++----------------------- test/directive.asm | 15 +++ x86/insns.dat | 1 + 19 files changed, 510 insertions(+), 286 deletions(-) create mode 100644 asm/getbool.c create mode 100644 test/directive.asm 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