0
0
mirror of https://github.com/netwide-assembler/nasm.git synced 2025-10-10 00:25:06 -04:00
Files
nasm/asm/pragma.c
Fabian Giesen 0ee113ce68 Don't assume pragma directives are a single word
pragma->tail is described as "anything after the operation", but
existing parsing passed just the first whitespace-delimited
word. Change the parsing to just strip leading and trailing
white space off the rest of the line, but keep interior spaces
if there are any.

This is preparation for a build_version pragma for Mach-O matching
the llvm-as .build_version syntax.

Signed-off-by: Fabian Giesen <fabian.giesen@epicgames.com>
2025-10-03 10:47:25 -07:00

349 lines
10 KiB
C

/* SPDX-License-Identifier: BSD-2-Clause */
/* Copyright 1996-2019 The NASM Authors - All Rights Reserved */
/*
* Parse and handle [pragma] directives. The preprocessor handles
* %pragma preproc directives separately, all other namespaces are
* simply converted to [pragma].
*/
#include "compiler.h"
#include "nctype.h"
#include "nasm.h"
#include "nasmlib.h"
#include "assemble.h"
#include "error.h"
#include "listing.h"
static enum directive_result ignore_pragma(const struct pragma *pragma);
static enum directive_result output_pragma(const struct pragma *pragma);
static enum directive_result debug_pragma(const struct pragma *pragma);
static enum directive_result limit_pragma(const struct pragma *pragma);
/*
* Handle [pragma] directives. [pragma] is generally produced by
* the %pragma preprocessor directive, which simply passes on any
* string that it finds *except* %pragma preproc. The idea is
* that pragmas are of the form:
*
* %pragma <facility> <opname> [<options>...]
*
* ... where "facility" can be either a generic facility or a backend
* name.
*
* The following names are currently reserved for global facilities;
* so far none of these have any defined pragmas at all:
*
* preproc - preprocessor
* limit - limit setting
* asm - assembler
* list - listing generator
* file - generic file handling
* input - input file handling
* output - backend-independent output handling
* debug - backend-independent debug handling
* ignore - dummy pragma (can be used to "comment out")
*
* This function should generally not error out if it doesn't understand
* what a pragma is for, for unknown arguments, etc; the whole point of
* a pragma is that future releases might add new ones that should be
* ignored rather than be an error. Erroring out is acceptable for
* known pragmas suffering from parsing errors and so on.
*
* Adding default-suppressed warnings would, however, be a good idea
* at some point.
*/
static struct pragma_facility global_pragmas[] =
{
{ "asm", NULL },
{ "limit", limit_pragma },
{ "list", list_pragma },
{ "file", NULL },
{ "input", NULL },
{ "output", output_pragma },
{ "debug", debug_pragma },
{ "ignore", ignore_pragma },
/* This will never actually get this far... */
{ "preproc", NULL }, /* Handled in the preprocessor by necessity */
{ NULL, NULL }
};
/*
* Invoke a pragma handler
*/
static enum directive_result
call_pragma(const struct pragma_facility *pf, struct pragma *pragma)
{
if (!pf || !pf->handler)
return DIRR_UNKNOWN;
pragma->facility = pf;
return pf->handler(pragma);
}
/*
* Search a pragma list for a known pragma facility and if so, invoke
* the handler. Return true if processing is complete. The "default
* name", *or def->name*, if set, matches the final NULL entry (used
* for backends, so multiple backends can share the same list under
* some circumstances, and the backends can implement common operations.)
*/
static enum directive_result
search_pragma_list(const struct pragma_facility *list,
const char *defaultname,
const struct pragma_facility *def,
const struct pragma *cpragma)
{
const struct pragma_facility *pf = NULL;
enum directive_result rv;
bool facility_match, is_default;
struct pragma pragma = *cpragma;
const char *facname = pragma.facility_name;
/* Is there a default facility and we match its name? */
is_default = def && def->name && !nasm_stricmp(facname, def->name);
facility_match = is_default;
/*
* Promote def->name to defaultname if both are set. This handles
* e.g. output -> elf32 so that we can handle elf32-specific
* directives in that handler.
*/
if (defaultname) {
if (is_default)
facname = defaultname;
else
facility_match = !nasm_stricmp(facname, defaultname);
}
if (facname && list) {
for (pf = list; pf->name; pf++) {
if (!nasm_stricmp(facname, pf->name)) {
facility_match = true;
rv = call_pragma(pf, &pragma);
if (rv != DIRR_UNKNOWN)
goto found_it;
}
}
if (facility_match) {
/*
* Facility name match but no matching directive; handler in NULL
* entry at end of list?
*/
rv = call_pragma(pf, &pragma);
if (rv != DIRR_UNKNOWN)
goto found_it;
}
}
if (facility_match) {
/*
* Facility match but still nothing: def->handler if it exists
*/
rv = call_pragma(def, &pragma);
} else {
/*
* No facility matched
*/
return DIRR_UNKNOWN;
}
/*
* Otherwise we found the facility but not any supported directive,
* fall through...
*/
found_it:
switch (rv) {
case DIRR_UNKNOWN:
switch (pragma.opcode) {
case D_none:
/*!
*!pragma-bad [off] malformed \c{%pragma}
*!=bad-pragma
*! warns about a malformed or otherwise unparsable
*! \c{%pragma} directive.
*/
nasm_warn(ERR_PASS2|WARN_PRAGMA_BAD,
"empty %%pragma %s", pragma.facility_name);
break;
default:
/*!
*!pragma-unknown [off] unknown \c{%pragma} facility or directive
*!=unknown-pragma
*! warns about an unknown \c{%pragma} directive.
*! This is not yet implemented for most cases.
*/
nasm_warn(ERR_PASS2|WARN_PRAGMA_UNKNOWN,
"unknown %%pragma %s %s",
pragma.facility_name, pragma.opname);
break;
}
rv = DIRR_ERROR; /* Already printed an error message */
break;
case DIRR_OK:
case DIRR_ERROR:
break; /* Nothing to do */
case DIRR_BADPARAM:
/*
* This one is an error. Don't use it if forward compatibility
* would be compromised, as opposed to an inherent error.
*/
nasm_error(ERR_NONFATAL, "bad argument to %%pragma %s %s",
pragma.facility_name, pragma.opname);
break;
default:
panic();
}
return rv;
}
/* This warning message is intended for future use */
/*!
*!pragma-na [off] \c{%pragma} not applicable to this compilation
*!=not-my-pragma
*! warns about a \c{%pragma} directive which is not applicable to
*! this particular assembly session. This is not yet implemented.
*/
/* Naked %pragma */
/*!
*!pragma-empty [off] empty \c{%pragma} directive
*! warns about a \c{%pragma} directive containing nothing.
*! This is treated identically to \c{%pragma ignore} except
*! for this optional warning.
*/
void process_pragma(char *str)
{
const struct pragma_facility *pf;
struct pragma pragma;
char *p;
nasm_zero(pragma);
pragma.facility_name = nasm_get_word(str, &p);
if (!pragma.facility_name) {
/* Empty %pragma */
nasm_warn(ERR_PASS2|WARN_PRAGMA_EMPTY,
"empty %%pragma directive, ignored");
return;
}
pragma.opname = nasm_get_word(p, &p);
if (!pragma.opname)
pragma.opcode = D_none;
else
pragma.opcode = directive_find(pragma.opname);
/*
* This used to use nasm_trim_spaces, but that assumes a single word.
* Instead, strip spaces at either end of the directive, but allow
* interior spaces.
*/
p = nasm_zap_spaces_fwd(p);
pragma.tail = p;
p += strlen(p);
while (p > pragma.tail && nasm_isspace(p[-1]))
*--p = 0;
/*
* Search the global pragma namespaces. This is done
* as a loop rather than letting search_pragma_list()
* just run, because we don't want to keep searching if
* we have a facility match, thus we want to call
* search_pragma_list() individually for each namespace.
*/
for (pf = global_pragmas; pf->name; pf++) {
if (search_pragma_list(NULL, NULL, pf, &pragma) != DIRR_UNKNOWN)
return;
}
/* Is it an output pragma? */
if (output_pragma(&pragma) != DIRR_UNKNOWN)
return;
/* Is it a debug pragma */
if (debug_pragma(&pragma) != DIRR_UNKNOWN)
return;
/*
* Note: it would be nice to warn for an unknown namespace,
* but in order to do so we need to walk *ALL* the backends
* in order to make sure we aren't dealing with a pragma that
* is for another backend. On the other hand, that could
* also be a warning with a separate warning flag.
*
* Leave this for the future, however, the warning classes are
* already defined for future compatibility.
*/
}
/* %pragma ignore */
static enum directive_result ignore_pragma(const struct pragma *pragma)
{
(void)pragma;
return DIRR_OK; /* Even for D_none! */
}
/*
* Process output and debug pragmas, by either list name or generic
* name. Note that the output/debug format list can hook the default
* names if they so choose.
*/
static enum directive_result output_pragma_common(const struct pragma *);
static enum directive_result output_pragma(const struct pragma *pragma)
{
static const struct pragma_facility
output_pragma_def = { "output", output_pragma_common };
return search_pragma_list(ofmt->pragmas, ofmt->shortname,
&output_pragma_def, pragma);
}
/* Generic pragmas that apply to all output backends */
static enum directive_result output_pragma_common(const struct pragma *pragma)
{
switch (pragma->opcode) {
case D_PREFIX:
case D_GPREFIX:
set_label_mangle(LM_GPREFIX, pragma->tail);
return DIRR_OK;
case D_SUFFIX:
case D_GSUFFIX:
set_label_mangle(LM_GSUFFIX, pragma->tail);
return DIRR_OK;
case D_LPREFIX:
set_label_mangle(LM_LPREFIX, pragma->tail);
return DIRR_OK;
case D_LSUFFIX:
set_label_mangle(LM_LSUFFIX, pragma->tail);
return DIRR_OK;
default:
return DIRR_UNKNOWN;
}
}
static enum directive_result debug_pragma(const struct pragma *pragma)
{
static const struct pragma_facility
debug_pragma_def = { "debug", NULL };
return search_pragma_list(dfmt->pragmas, dfmt->shortname,
&debug_pragma_def, pragma);
}
/*
* %pragma limit to set resource limits
*/
static enum directive_result limit_pragma(const struct pragma *pragma)
{
return nasm_set_limit(pragma->opname, pragma->tail);
}