0
0
mirror of https://github.com/netwide-assembler/nasm.git synced 2025-10-10 00:25:06 -04:00

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) <hpa@zytor.com>
This commit is contained in:
H. Peter Anvin
2025-09-04 19:41:12 -07:00
parent ec48f1a3f8
commit 31a160759d
19 changed files with 510 additions and 286 deletions

View File

@@ -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) \

View File

@@ -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 *);

View File

@@ -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:

View File

@@ -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

106
asm/getbool.c Normal file
View File

@@ -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;
}

View File

@@ -46,6 +46,7 @@
*def
*defalias
*difi
*directive
*empty
*env
*file

View File

@@ -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)

View File

@@ -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

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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++)

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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:

View File

@@ -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;
}

15
test/directive.asm Normal file
View File

@@ -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

View File

@@ -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