From fcedfddf00b3f994e4f4e40332ac7fc192c63244 Mon Sep 17 00:00:00 2001 From: polwex Date: Sun, 5 Oct 2025 21:56:51 +0700 Subject: claude is gud --- vere/ext/nasm/asm/nasm.c | 2352 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2352 insertions(+) create mode 100644 vere/ext/nasm/asm/nasm.c (limited to 'vere/ext/nasm/asm/nasm.c') diff --git a/vere/ext/nasm/asm/nasm.c b/vere/ext/nasm/asm/nasm.c new file mode 100644 index 0000000..76c70f6 --- /dev/null +++ b/vere/ext/nasm/asm/nasm.c @@ -0,0 +1,2352 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1996-2022 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. + * + * ----------------------------------------------------------------------- */ + +/* + * The Netwide Assembler main program module + */ + +#include "compiler.h" + + +#include "nasm.h" +#include "nasmlib.h" +#include "nctype.h" +#include "error.h" +#include "saa.h" +#include "raa.h" +#include "floats.h" +#include "stdscan.h" +#include "insns.h" +#include "preproc.h" +#include "parser.h" +#include "eval.h" +#include "assemble.h" +#include "labels.h" +#include "outform.h" +#include "listing.h" +#include "iflag.h" +#include "quote.h" +#include "ver.h" + +/* + * This is the maximum number of optimization passes to do. If we ever + * find a case where the optimizer doesn't naturally converge, we might + * have to drop this value so the assembler doesn't appear to just hang. + */ +#define MAX_OPTIMIZE (INT_MAX >> 1) + +struct forwrefinfo { /* info held on forward refs. */ + int lineno; + int operand; +}; + +const char *_progname; + +static void parse_cmdline(int, char **, int); +static void assemble_file(const char *, struct strlist *); +static bool skip_this_pass(errflags severity); +static void usage(void); +static void help(FILE *); + +struct error_format { + const char *beforeline; /* Before line number, if present */ + const char *afterline; /* After line number, if present */ + const char *beforemsg; /* Before actual message */ +}; + +static const struct error_format errfmt_gnu = { ":", "", ": " }; +static const struct error_format errfmt_msvc = { "(", ")", " : " }; +static const struct error_format *errfmt = &errfmt_gnu; +static struct strlist *warn_list; +static struct nasm_errhold *errhold_stack; + +unsigned int debug_nasm; /* Debugging messages? */ + +static bool using_debug_info, opt_verbose_info; +static const char *debug_format; + +#ifndef ABORT_ON_PANIC +# define ABORT_ON_PANIC 0 +#endif +static bool abort_on_panic = ABORT_ON_PANIC; +static bool keep_all; + +bool tasm_compatible_mode = false; +enum pass_type _pass_type; +const char * const _pass_types[] = +{ + "init", "preproc-only", "first", "optimize", "stabilize", "final" +}; +int64_t _passn; +int globalrel = 0; +int globalbnd = 0; + +struct compile_time official_compile_time; + +const char *inname; +const char *outname; +static const char *listname; +static const char *errname; + +static int64_t globallineno; /* for forward-reference tracking */ + +const struct ofmt *ofmt = &OF_DEFAULT; +const struct ofmt_alias *ofmt_alias = NULL; +const struct dfmt *dfmt; + +FILE *error_file; /* Where to write error messages */ + +FILE *ofile = NULL; +struct optimization optimizing = + { MAX_OPTIMIZE, OPTIM_ALL_ENABLED }; /* number of optimization passes to take */ +static int cmd_sb = 16; /* by default */ + +iflag_t cpu, cmd_cpu; + +struct location location; +bool in_absolute; /* Flag we are in ABSOLUTE seg */ +struct location absolute; /* Segment/offset inside ABSOLUTE */ + +static struct RAA *offsets; + +static struct SAA *forwrefs; /* keep track of forward references */ +static const struct forwrefinfo *forwref; + +static struct strlist *include_path; +static enum preproc_opt ppopt; + +#define OP_NORMAL (1U << 0) +#define OP_PREPROCESS (1U << 1) +#define OP_DEPEND (1U << 2) + +static unsigned int operating_mode; + +/* Dependency flags */ +static bool depend_emit_phony = false; +static bool depend_missing_ok = false; +static const char *depend_target = NULL; +static const char *depend_file = NULL; +struct strlist *depend_list; + +static bool want_usage; +static bool terminate_after_phase; +bool user_nolist = false; + +static char *quote_for_pmake(const char *str); +static char *quote_for_wmake(const char *str); +static char *(*quote_for_make)(const char *) = quote_for_pmake; + +/* + * Execution limits that can be set via a command-line option or %pragma + */ + +/* + * This is really unlimited; it would take far longer than the + * current age of the universe for this limit to be reached even on + * much faster CPUs than currently exist. +*/ +#define LIMIT_MAX_VAL (INT64_MAX >> 1) + +int64_t nasm_limit[LIMIT_MAX+1]; + +struct limit_info { + const char *name; + const char *help; + int64_t default_val; +}; +/* The order here must match enum nasm_limit in nasm.h */ +static const struct limit_info limit_info[LIMIT_MAX+1] = { + { "passes", "total number of passes", LIMIT_MAX_VAL }, + { "stalled-passes", "number of passes without forward progress", 1000 }, + { "macro-levels", "levels of macro expansion", 10000 }, + { "macro-tokens", "tokens processed during single-lime macro expansion", 10000000 }, + { "mmacros", "multi-line macros before final return", 100000 }, + { "rep", "%rep count", 1000000 }, + { "eval", "expression evaluation descent", 8192 }, + { "lines", "total source lines processed", 2000000000 } +}; + +static void set_default_limits(void) +{ + int i; + size_t rl; + int64_t new_limit; + + for (i = 0; i <= LIMIT_MAX; i++) + nasm_limit[i] = limit_info[i].default_val; + + /* + * Try to set a sensible default value for the eval depth based + * on the limit of the stack size, if knowable... + */ + rl = nasm_get_stack_size_limit(); + new_limit = rl / (128 * sizeof(void *)); /* Sensible heuristic */ + if (new_limit < nasm_limit[LIMIT_EVAL]) + nasm_limit[LIMIT_EVAL] = new_limit; +} + +enum directive_result +nasm_set_limit(const char *limit, const char *valstr) +{ + int i; + int64_t val; + bool rn_error; + int errlevel; + + if (!limit) + limit = ""; + if (!valstr) + valstr = ""; + + for (i = 0; i <= LIMIT_MAX; i++) { + if (!nasm_stricmp(limit, limit_info[i].name)) + break; + } + if (i > LIMIT_MAX) { + if (not_started()) + errlevel = ERR_WARNING|WARN_OTHER|ERR_USAGE; + else + errlevel = ERR_WARNING|WARN_PRAGMA_UNKNOWN; + nasm_error(errlevel, "unknown limit: `%s'", limit); + return DIRR_ERROR; + } + + if (!nasm_stricmp(valstr, "unlimited")) { + val = LIMIT_MAX_VAL; + } else { + val = readnum(valstr, &rn_error); + if (rn_error || val < 0) { + if (not_started()) + errlevel = ERR_WARNING|WARN_OTHER|ERR_USAGE; + else + errlevel = ERR_WARNING|WARN_PRAGMA_BAD; + nasm_error(errlevel, "invalid limit value: `%s'", valstr); + return DIRR_ERROR; + } + if (val > LIMIT_MAX_VAL) + val = LIMIT_MAX_VAL; + } + + nasm_limit[i] = val; + return DIRR_OK; +} + +int64_t switch_segment(int32_t segment) +{ + location.segment = segment; + if (segment == NO_SEG) { + location.offset = absolute.offset; + in_absolute = true; + } else { + location.offset = raa_read(offsets, segment); + in_absolute = false; + } + return location.offset; +} + +static void set_curr_offs(int64_t l_off) +{ + if (in_absolute) + absolute.offset = l_off; + else + offsets = raa_write(offsets, location.segment, l_off); +} + +static void increment_offset(int64_t delta) +{ + if (unlikely(delta == 0)) + return; + + location.offset += delta; + set_curr_offs(location.offset); +} + +/* + * Define system-defined macros that are not part of + * macros/standard.mac. + */ +static void define_macros(void) +{ + const struct compile_time * const oct = &official_compile_time; + char temp[128]; + + if (oct->have_local) { + strftime(temp, sizeof temp, "__?DATE?__=\"%Y-%m-%d\"", &oct->local); + pp_pre_define(temp); + strftime(temp, sizeof temp, "__?DATE_NUM?__=%Y%m%d", &oct->local); + pp_pre_define(temp); + strftime(temp, sizeof temp, "__?TIME?__=\"%H:%M:%S\"", &oct->local); + pp_pre_define(temp); + strftime(temp, sizeof temp, "__?TIME_NUM?__=%H%M%S", &oct->local); + pp_pre_define(temp); + } + + if (oct->have_gm) { + strftime(temp, sizeof temp, "__?UTC_DATE?__=\"%Y-%m-%d\"", &oct->gm); + pp_pre_define(temp); + strftime(temp, sizeof temp, "__?UTC_DATE_NUM?__=%Y%m%d", &oct->gm); + pp_pre_define(temp); + strftime(temp, sizeof temp, "__?UTC_TIME?__=\"%H:%M:%S\"", &oct->gm); + pp_pre_define(temp); + strftime(temp, sizeof temp, "__?UTC_TIME_NUM?__=%H%M%S", &oct->gm); + pp_pre_define(temp); + } + + if (oct->have_posix) { + snprintf(temp, sizeof temp, "__?POSIX_TIME?__=%"PRId64, oct->posix); + pp_pre_define(temp); + } + + /* + * In case if output format is defined by alias + * we have to put shortname of the alias itself here + * otherwise ABI backward compatibility gets broken. + */ + snprintf(temp, sizeof(temp), "__?OUTPUT_FORMAT?__=%s", + ofmt_alias ? ofmt_alias->shortname : ofmt->shortname); + pp_pre_define(temp); + + /* + * Output-format specific macros. + */ + if (ofmt->stdmac) + pp_extra_stdmac(ofmt->stdmac); + + /* + * Debug format, if any + */ + if (dfmt != &null_debug_form) { + snprintf(temp, sizeof(temp), "__?DEBUG_FORMAT?__=%s", dfmt->shortname); + pp_pre_define(temp); + } +} + +/* + * Initialize the preprocessor, set up the include path, and define + * the system-included macros. This is called between passes 1 and 2 + * of parsing the command options; ofmt and dfmt are defined at this + * point. + * + * Command-line specified preprocessor directives (-p, -d, -u, + * --pragma, --before) are processed after this function. + */ +static void preproc_init(struct strlist *ipath) +{ + pp_init(ppopt); + define_macros(); + pp_include_path(ipath); +} + +static void emit_dependencies(struct strlist *list) +{ + FILE *deps; + int linepos, len; + bool wmake = (quote_for_make == quote_for_wmake); + const char *wrapstr, *nulltarget; + const struct strlist_entry *l; + + if (!list) + return; + + wrapstr = wmake ? " &\n " : " \\\n "; + nulltarget = wmake ? "\t%null\n" : ""; + + if (depend_file && strcmp(depend_file, "-")) { + deps = nasm_open_write(depend_file, NF_TEXT); + if (!deps) { + nasm_nonfatal("unable to write dependency file `%s'", depend_file); + return; + } + } else { + deps = stdout; + } + + linepos = fprintf(deps, "%s :", depend_target); + strlist_for_each(l, list) { + char *file = quote_for_make(l->str); + len = strlen(file); + if (linepos + len > 62 && linepos > 1) { + fputs(wrapstr, deps); + linepos = 1; + } + fprintf(deps, " %s", file); + linepos += len+1; + nasm_free(file); + } + fputs("\n\n", deps); + + strlist_for_each(l, list) { + if (depend_emit_phony) { + char *file = quote_for_make(l->str); + fprintf(deps, "%s :\n%s\n", file, nulltarget); + nasm_free(file); + } + } + + strlist_free(&list); + + if (deps != stdout) + fclose(deps); +} + +/* Convert a struct tm to a POSIX-style time constant */ +static int64_t make_posix_time(const struct tm *tm) +{ + int64_t t; + int64_t y = tm->tm_year; + + /* See IEEE 1003.1:2004, section 4.14 */ + + t = (y-70)*365 + (y-69)/4 - (y-1)/100 + (y+299)/400; + t += tm->tm_yday; + t *= 24; + t += tm->tm_hour; + t *= 60; + t += tm->tm_min; + t *= 60; + t += tm->tm_sec; + + return t; +} + +/* + * Quote a filename string if and only if it is necessary. + * It is considered necessary if any one of these is true: + * 1. The filename contains control characters; + * 2. The filename starts or ends with a space or quote mark; + * 3. The filename contains more than one space in a row; + * 4. The filename is empty. + * + * The filename is returned in a newly allocated buffer. + */ +static char *nasm_quote_filename(const char *fn) +{ + const unsigned char *p = + (const unsigned char *)fn; + size_t len; + + if (!p || !*p) + return nasm_strdup("\"\""); + + if (*p <= ' ' || nasm_isquote(*p)) { + goto quote; + } else { + unsigned char cutoff = ' '; + + while (*p) { + if (*p < cutoff) + goto quote; + cutoff = ' ' + (*p == ' '); + p++; + } + if (p[-1] <= ' ' || nasm_isquote(p[-1])) + goto quote; + } + + /* Quoting not necessary */ + return nasm_strdup(fn); + +quote: + len = strlen(fn); + return nasm_quote(fn, &len); +} + +static void timestamp(void) +{ + struct compile_time * const oct = &official_compile_time; + const struct tm *tp, *best_gm; + + time(&oct->t); + + best_gm = NULL; + + tp = localtime(&oct->t); + if (tp) { + oct->local = *tp; + best_gm = &oct->local; + oct->have_local = true; + } + + tp = gmtime(&oct->t); + if (tp) { + oct->gm = *tp; + best_gm = &oct->gm; + oct->have_gm = true; + if (!oct->have_local) + oct->local = oct->gm; + } else { + oct->gm = oct->local; + } + + if (best_gm) { + oct->posix = make_posix_time(best_gm); + oct->have_posix = true; + } +} + +int main(int argc, char **argv) +{ + /* Do these as early as possible */ + error_file = stderr; + _progname = argv[0]; + if (!_progname || !_progname[0]) + _progname = "nasm"; + + timestamp(); + + set_cpu(NULL); + cmd_cpu = cpu; + + set_default_limits(); + + include_path = strlist_alloc(true); + + _pass_type = PASS_INIT; + _passn = 0; + + want_usage = terminate_after_phase = false; + + nasm_ctype_init(); + src_init(); + + /* + * We must call init_labels() before the command line parsing, + * because we may be setting prefixes/suffixes from the command + * line. + */ + init_labels(); + + offsets = raa_init(); + forwrefs = saa_init((int32_t)sizeof(struct forwrefinfo)); + + operating_mode = OP_NORMAL; + + parse_cmdline(argc, argv, 1); + if (terminate_after_phase) { + if (want_usage) + usage(); + return 1; + } + + /* At this point we have ofmt and the name of the desired debug format */ + if (!using_debug_info) { + /* No debug info, redirect to the null backend (empty stubs) */ + dfmt = &null_debug_form; + } else if (!debug_format) { + /* Default debug format for this backend */ + dfmt = ofmt->default_dfmt; + } else { + dfmt = dfmt_find(ofmt, debug_format); + if (!dfmt) { + nasm_fatalf(ERR_USAGE, "unrecognized debug format `%s' for output format `%s'", + debug_format, ofmt->shortname); + } + } + + /* Have we enabled TASM mode? */ + if (tasm_compatible_mode) { + ppopt |= PP_TASM; + nasm_ctype_tasm_mode(); + } + preproc_init(include_path); + + parse_cmdline(argc, argv, 2); + if (terminate_after_phase) { + if (want_usage) + usage(); + return 1; + } + + /* Save away the default state of warnings */ + init_warnings(); + + /* Dependency filename if we are also doing other things */ + if (!depend_file && (operating_mode & ~OP_DEPEND)) { + if (outname) + depend_file = nasm_strcat(outname, ".d"); + else + depend_file = filename_set_extension(inname, ".d"); + } + + /* + * If no output file name provided and this + * is preprocess mode, we're perfectly + * fine to output into stdout. + */ + if (!outname && !(operating_mode & OP_PREPROCESS)) { + outname = filename_set_extension(inname, ofmt->extension); + if (!strcmp(outname, inname)) { + outname = "nasm.out"; + nasm_warn(WARN_OTHER, "default output file same as input, using `%s' for output\n", outname); + } + } + + depend_list = (operating_mode & OP_DEPEND) ? strlist_alloc(true) : NULL; + + if (!depend_target) + depend_target = quote_for_make(outname); + + if (!(operating_mode & (OP_PREPROCESS|OP_NORMAL))) { + char *line; + + if (depend_missing_ok) + pp_include_path(NULL); /* "assume generated" */ + + pp_reset(inname, PP_DEPS, depend_list); + ofile = NULL; + while ((line = pp_getline())) + nasm_free(line); + pp_cleanup_pass(); + reset_warnings(); + } else if (operating_mode & OP_PREPROCESS) { + char *line; + const char *file_name = NULL; + char *quoted_file_name = nasm_quote_filename(file_name); + int32_t linnum = 0; + int32_t lineinc = 0; + FILE *out; + + if (outname) { + ofile = nasm_open_write(outname, NF_TEXT); + if (!ofile) + nasm_fatal("unable to open output file `%s'", outname); + out = ofile; + } else { + ofile = NULL; + out = stdout; + } + + location.known = false; + + _pass_type = PASS_PREPROC; + pp_reset(inname, PP_PREPROC, depend_list); + + while ((line = pp_getline())) { + /* + * We generate %line directives if needed for later programs + */ + struct src_location where = src_where(); + if (file_name != where.filename) { + file_name = where.filename; + linnum = -1; /* Force a new %line statement */ + lineinc = file_name ? 1 : 0; + nasm_free(quoted_file_name); + quoted_file_name = nasm_quote_filename(file_name); + } else if (lineinc) { + if (linnum + lineinc == where.lineno) { + /* Add one blank line to account for increment */ + fputc('\n', out); + linnum += lineinc; + } else if (linnum - lineinc == where.lineno) { + /* + * Standing still, probably a macro. Set increment + * to zero. + */ + lineinc = 0; + } + } else { + /* lineinc == 0 */ + if (linnum + 1 == where.lineno) + lineinc = 1; + } + + /* Skip blank lines if we will need a %line anyway */ + if (linnum == -1 && !line[0]) + continue; + + if (linnum != where.lineno) { + fprintf(out, "%%line %"PRId32"%+"PRId32" %s\n", + where.lineno, lineinc, quoted_file_name); + } + linnum = where.lineno + lineinc; + + fputs(line, out); + fputc('\n', out); + } + + nasm_free(quoted_file_name); + + pp_cleanup_pass(); + reset_warnings(); + if (ofile) + fclose(ofile); + if (ofile && terminate_after_phase && !keep_all) + remove(outname); + ofile = NULL; + } + + if (operating_mode & OP_NORMAL) { + ofile = nasm_open_write(outname, (ofmt->flags & OFMT_TEXT) ? NF_TEXT : NF_BINARY); + if (!ofile) + nasm_fatal("unable to open output file `%s'", outname); + + ofmt->init(); + dfmt->init(); + + assemble_file(inname, depend_list); + + if (!terminate_after_phase) { + ofmt->cleanup(); + cleanup_labels(); + fflush(ofile); + if (ferror(ofile)) + nasm_nonfatal("write error on output file `%s'", outname); + } + + if (ofile) { + fclose(ofile); + if (terminate_after_phase && !keep_all) + remove(outname); + ofile = NULL; + } + } + + pp_cleanup_session(); + + if (depend_list && !terminate_after_phase) + emit_dependencies(depend_list); + + if (want_usage) + usage(); + + raa_free(offsets); + saa_free(forwrefs); + eval_cleanup(); + stdscan_cleanup(); + src_free(); + strlist_free(&include_path); + + return terminate_after_phase; +} + +/* + * Get a parameter for a command line option. + * First arg must be in the form of e.g. -f... + */ +static char *get_param(char *p, char *q, bool *advance) +{ + *advance = false; + if (p[2]) /* the parameter's in the option */ + return nasm_skip_spaces(p + 2); + if (q && q[0]) { + *advance = true; + return q; + } + nasm_nonfatalf(ERR_USAGE, "option `-%c' requires an argument", p[1]); + return NULL; +} + +/* + * Copy a filename + */ +static void copy_filename(const char **dst, const char *src, const char *what) +{ + if (*dst) + nasm_fatal("more than one %s file specified: %s\n", what, src); + + *dst = nasm_strdup(src); +} + +/* + * Convert a string to a POSIX make-safe form + */ +static char *quote_for_pmake(const char *str) +{ + const char *p; + char *os, *q; + + size_t n = 1; /* Terminating zero */ + size_t nbs = 0; + + if (!str) + return NULL; + + for (p = str; *p; p++) { + switch (*p) { + case ' ': + case '\t': + /* Convert N backslashes + ws -> 2N+1 backslashes + ws */ + n += nbs + 2; + nbs = 0; + break; + case '$': + case '#': + nbs = 0; + n += 2; + break; + case '\\': + nbs++; + n++; + break; + default: + nbs = 0; + n++; + break; + } + } + + /* Convert N backslashes at the end of filename to 2N backslashes */ + n += nbs; + + os = q = nasm_malloc(n); + + nbs = 0; + for (p = str; *p; p++) { + switch (*p) { + case ' ': + case '\t': + q = mempset(q, '\\', nbs); + *q++ = '\\'; + *q++ = *p; + nbs = 0; + break; + case '$': + *q++ = *p; + *q++ = *p; + nbs = 0; + break; + case '#': + *q++ = '\\'; + *q++ = *p; + nbs = 0; + break; + case '\\': + *q++ = *p; + nbs++; + break; + default: + *q++ = *p; + nbs = 0; + break; + } + } + + q = mempset(q, '\\', nbs); + *q = '\0'; + + return os; +} + +/* + * Convert a string to a Watcom make-safe form + */ +static char *quote_for_wmake(const char *str) +{ + const char *p; + char *os, *q; + bool quote = false; + + size_t n = 1; /* Terminating zero */ + + if (!str) + return NULL; + + for (p = str; *p; p++) { + switch (*p) { + case ' ': + case '\t': + case '&': + quote = true; + n++; + break; + case '\"': + quote = true; + n += 2; + break; + case '$': + case '#': + n += 2; + break; + default: + n++; + break; + } + } + + if (quote) + n += 2; + + os = q = nasm_malloc(n); + + if (quote) + *q++ = '\"'; + + for (p = str; *p; p++) { + switch (*p) { + case '$': + case '#': + *q++ = '$'; + *q++ = *p; + break; + case '\"': + *q++ = *p; + *q++ = *p; + break; + default: + *q++ = *p; + break; + } + } + + if (quote) + *q++ = '\"'; + + *q = '\0'; + + return os; +} + +enum text_options { + OPT_BOGUS, + OPT_VERSION, + OPT_HELP, + OPT_ABORT_ON_PANIC, + OPT_MANGLE, + OPT_INCLUDE, + OPT_PRAGMA, + OPT_BEFORE, + OPT_LIMIT, + OPT_KEEP_ALL, + OPT_NO_LINE, + OPT_DEBUG, + OPT_REPRODUCIBLE +}; +enum need_arg { + ARG_NO, + ARG_YES, + ARG_MAYBE +}; + +struct textargs { + const char *label; + enum text_options opt; + enum need_arg need_arg; + int pvt; +}; +static const struct textargs textopts[] = { + {"v", OPT_VERSION, ARG_NO, 0}, + {"version", OPT_VERSION, ARG_NO, 0}, + {"help", OPT_HELP, ARG_NO, 0}, + {"abort-on-panic", OPT_ABORT_ON_PANIC, ARG_NO, 0}, + {"prefix", OPT_MANGLE, ARG_YES, LM_GPREFIX}, + {"postfix", OPT_MANGLE, ARG_YES, LM_GSUFFIX}, + {"gprefix", OPT_MANGLE, ARG_YES, LM_GPREFIX}, + {"gpostfix", OPT_MANGLE, ARG_YES, LM_GSUFFIX}, + {"lprefix", OPT_MANGLE, ARG_YES, LM_LPREFIX}, + {"lpostfix", OPT_MANGLE, ARG_YES, LM_LSUFFIX}, + {"include", OPT_INCLUDE, ARG_YES, 0}, + {"pragma", OPT_PRAGMA, ARG_YES, 0}, + {"before", OPT_BEFORE, ARG_YES, 0}, + {"limit-", OPT_LIMIT, ARG_YES, 0}, + {"keep-all", OPT_KEEP_ALL, ARG_NO, 0}, + {"no-line", OPT_NO_LINE, ARG_NO, 0}, + {"debug", OPT_DEBUG, ARG_MAYBE, 0}, + {"reproducible", OPT_REPRODUCIBLE, ARG_NO, 0}, + {NULL, OPT_BOGUS, ARG_NO, 0} +}; + +static void show_version(void) +{ + printf("NASM version %s compiled on %s%s\n", + nasm_version, nasm_date, nasm_compile_options); + exit(0); +} + +static bool stopoptions = false; +static bool process_arg(char *p, char *q, int pass) +{ + char *param; + bool advance = false; + + if (!p || !p[0]) + return false; + + if (p[0] == '-' && !stopoptions) { + if (strchr("oOfpPdDiIlLFXuUZwW", p[1])) { + /* These parameters take values */ + if (!(param = get_param(p, q, &advance))) + return advance; + } + + switch (p[1]) { + case 's': + if (pass == 1) + error_file = stdout; + break; + + case 'o': /* output file */ + if (pass == 2) + copy_filename(&outname, param, "output"); + break; + + case 'f': /* output format */ + if (pass == 1) { + ofmt = ofmt_find(param, &ofmt_alias); + if (!ofmt) { + nasm_fatalf(ERR_USAGE, "unrecognised output format `%s' - use -hf for a list", param); + } + } + break; + + case 'O': /* Optimization level */ + if (pass == 1) { + int opt; + + if (!*param) { + /* Naked -O == -Ox */ + optimizing.level = MAX_OPTIMIZE; + } else { + while (*param) { + switch (*param) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + opt = strtoul(param, ¶m, 10); + + /* -O0 -> optimizing.level == -1, 0.98 behaviour */ + /* -O1 -> optimizing.level == 0, 0.98.09 behaviour */ + if (opt < 2) + optimizing.level = opt - 1; + else + optimizing.level = opt; + break; + + case 'v': + case '+': + param++; + opt_verbose_info = true; + break; + + case 'x': + param++; + optimizing.level = MAX_OPTIMIZE; + break; + + default: + nasm_fatal("unknown optimization option -O%c\n", + *param); + break; + } + } + if (optimizing.level > MAX_OPTIMIZE) + optimizing.level = MAX_OPTIMIZE; + } + } + break; + + case 'p': /* pre-include */ + case 'P': + if (pass == 2) + pp_pre_include(param); + break; + + case 'd': /* pre-define */ + case 'D': + if (pass == 2) + pp_pre_define(param); + break; + + case 'u': /* un-define */ + case 'U': + if (pass == 2) + pp_pre_undefine(param); + break; + + case 'i': /* include search path */ + case 'I': + if (pass == 1) + strlist_add(include_path, param); + break; + + case 'l': /* listing file */ + if (pass == 2) + copy_filename(&listname, param, "listing"); + break; + + case 'L': /* listing options */ + if (pass == 2) { + while (*param) + list_options |= list_option_mask(*param++); + } + break; + + case 'Z': /* error messages file */ + if (pass == 1) + copy_filename(&errname, param, "error"); + break; + + case 'F': /* specify debug format */ + if (pass == 1) { + using_debug_info = true; + debug_format = param; + } + break; + + case 'X': /* specify error reporting format */ + if (pass == 1) { + if (!nasm_stricmp("vc", param) || !nasm_stricmp("msvc", param) || !nasm_stricmp("ms", param)) + errfmt = &errfmt_msvc; + else if (!nasm_stricmp("gnu", param) || !nasm_stricmp("gcc", param)) + errfmt = &errfmt_gnu; + else + nasm_fatalf(ERR_USAGE, "unrecognized error reporting format `%s'", param); + } + break; + + case 'g': + if (pass == 1) { + using_debug_info = true; + if (p[2]) + debug_format = nasm_skip_spaces(p + 2); + } + break; + + case 'h': + help(stdout); + exit(0); /* never need usage message here */ + break; + + case 'y': + /* legacy option */ + dfmt_list(stdout); + exit(0); + break; + + case 't': + if (pass == 1) + tasm_compatible_mode = true; + break; + + case 'v': + show_version(); + break; + + case 'e': /* preprocess only */ + case 'E': + if (pass == 1) + operating_mode = OP_PREPROCESS; + break; + + case 'a': /* assemble only - don't preprocess */ + if (pass == 1) + ppopt |= PP_TRIVIAL; + break; + + case 'w': + case 'W': + if (pass == 2) + set_warning_status(param); + break; + + case 'M': + if (pass == 1) { + switch (p[2]) { + case 'W': + quote_for_make = quote_for_wmake; + break; + case 'D': + case 'F': + case 'T': + case 'Q': + advance = true; + break; + default: + break; + } + } else { + switch (p[2]) { + case 0: + operating_mode = OP_DEPEND; + break; + case 'G': + operating_mode = OP_DEPEND; + depend_missing_ok = true; + break; + case 'P': + depend_emit_phony = true; + break; + case 'D': + operating_mode |= OP_DEPEND; + if (q && (q[0] != '-' || q[1] == '\0')) { + depend_file = q; + advance = true; + } + break; + case 'F': + depend_file = q; + advance = true; + break; + case 'T': + depend_target = q; + advance = true; + break; + case 'Q': + depend_target = quote_for_make(q); + advance = true; + break; + case 'W': + /* handled in pass 1 */ + break; + default: + nasm_nonfatalf(ERR_USAGE, "unknown dependency option `-M%c'", p[2]); + break; + } + } + if (advance && (!q || !q[0])) { + nasm_nonfatalf(ERR_USAGE, "option `-M%c' requires a parameter", p[2]); + break; + } + break; + + case '-': + { + const struct textargs *tx; + size_t olen, plen; + char *eqsave; + enum text_options opt; + + p += 2; + + if (!*p) { /* -- => stop processing options */ + stopoptions = true; + break; + } + + olen = 0; /* Placate gcc at lower optimization levels */ + plen = strlen(p); + for (tx = textopts; tx->label; tx++) { + olen = strlen(tx->label); + + if (olen > plen) + continue; + + if (nasm_memicmp(p, tx->label, olen)) + continue; + + if (tx->label[olen-1] == '-') + break; /* Incomplete option */ + + if (!p[olen] || p[olen] == '=') + break; /* Complete option */ + } + + if (!tx->label) { + nasm_nonfatalf(ERR_USAGE, "unrecognized option `--%s'", p); + } + + opt = tx->opt; + + eqsave = param = strchr(p+olen, '='); + if (param) + *param++ = '\0'; + + switch (tx->need_arg) { + case ARG_YES: /* Argument required, and may be standalone */ + if (!param) { + param = q; + advance = true; + } + + /* Note: a null string is a valid parameter */ + if (!param) { + nasm_nonfatalf(ERR_USAGE, "option `--%s' requires an argument", p); + opt = OPT_BOGUS; + } + break; + + case ARG_NO: /* Argument prohibited */ + if (param) { + nasm_nonfatalf(ERR_USAGE, "option `--%s' does not take an argument", p); + opt = OPT_BOGUS; + } + break; + + case ARG_MAYBE: /* Argument permitted, but must be attached with = */ + break; + } + + switch (opt) { + case OPT_BOGUS: + break; /* We have already errored out */ + case OPT_VERSION: + show_version(); + break; + case OPT_ABORT_ON_PANIC: + abort_on_panic = true; + break; + case OPT_MANGLE: + if (pass == 2) + set_label_mangle(tx->pvt, param); + break; + case OPT_INCLUDE: + if (pass == 2) + pp_pre_include(q); + break; + case OPT_PRAGMA: + if (pass == 2) + pp_pre_command("%pragma", param); + break; + case OPT_BEFORE: + if (pass == 2) + pp_pre_command(NULL, param); + break; + case OPT_LIMIT: + if (pass == 1) + nasm_set_limit(p+olen, param); + break; + case OPT_KEEP_ALL: + keep_all = true; + break; + case OPT_NO_LINE: + ppopt |= PP_NOLINE; + break; + case OPT_DEBUG: + debug_nasm = param ? strtoul(param, NULL, 10) : debug_nasm+1; + break; + case OPT_REPRODUCIBLE: + reproducible = true; + break; + case OPT_HELP: + help(stdout); + exit(0); + default: + panic(); + } + + if (eqsave) + *eqsave = '='; /* Restore = argument separator */ + + break; + } + + default: + nasm_nonfatalf(ERR_USAGE, "unrecognised option `-%c'", p[1]); + break; + } + } else if (pass == 2) { + /* In theory we could allow multiple input files... */ + copy_filename(&inname, p, "input"); + } + + return advance; +} + +#define ARG_BUF_DELTA 128 + +static void process_respfile(FILE * rfile, int pass) +{ + char *buffer, *p, *q, *prevarg; + int bufsize, prevargsize; + + bufsize = prevargsize = ARG_BUF_DELTA; + buffer = nasm_malloc(ARG_BUF_DELTA); + prevarg = nasm_malloc(ARG_BUF_DELTA); + prevarg[0] = '\0'; + + while (1) { /* Loop to handle all lines in file */ + p = buffer; + while (1) { /* Loop to handle long lines */ + q = fgets(p, bufsize - (p - buffer), rfile); + if (!q) + break; + p += strlen(p); + if (p > buffer && p[-1] == '\n') + break; + if (p - buffer > bufsize - 10) { + int offset; + offset = p - buffer; + bufsize += ARG_BUF_DELTA; + buffer = nasm_realloc(buffer, bufsize); + p = buffer + offset; + } + } + + if (!q && p == buffer) { + if (prevarg[0]) + process_arg(prevarg, NULL, pass); + nasm_free(buffer); + nasm_free(prevarg); + return; + } + + /* + * Play safe: remove CRs, LFs and any spurious ^Zs, if any of + * them are present at the end of the line. + */ + *(p = &buffer[strcspn(buffer, "\r\n\032")]) = '\0'; + + while (p > buffer && nasm_isspace(p[-1])) + *--p = '\0'; + + p = nasm_skip_spaces(buffer); + + if (process_arg(prevarg, p, pass)) + *p = '\0'; + + if ((int) strlen(p) > prevargsize - 10) { + prevargsize += ARG_BUF_DELTA; + prevarg = nasm_realloc(prevarg, prevargsize); + } + strncpy(prevarg, p, prevargsize); + } +} + +/* Function to process args from a string of args, rather than the + * argv array. Used by the environment variable and response file + * processing. + */ +static void process_args(char *args, int pass) +{ + char *p, *q, *arg, *prevarg; + char separator = ' '; + + p = args; + if (*p && *p != '-') + separator = *p++; + arg = NULL; + while (*p) { + q = p; + while (*p && *p != separator) + p++; + while (*p == separator) + *p++ = '\0'; + prevarg = arg; + arg = q; + if (process_arg(prevarg, arg, pass)) + arg = NULL; + } + if (arg) + process_arg(arg, NULL, pass); +} + +static void process_response_file(const char *file, int pass) +{ + char str[2048]; + FILE *f = nasm_open_read(file, NF_TEXT); + if (!f) { + perror(file); + exit(-1); + } + while (fgets(str, sizeof str, f)) { + process_args(str, pass); + } + fclose(f); +} + +static void parse_cmdline(int argc, char **argv, int pass) +{ + FILE *rfile; + char *envreal, *envcopy = NULL, *p; + + /* + * Initialize all the warnings to their default state, including + * warning index 0 used for "always on". + */ + memcpy(warning_state, warning_default, sizeof warning_state); + + /* + * First, process the NASMENV environment variable. + */ + envreal = getenv("NASMENV"); + if (envreal) { + envcopy = nasm_strdup(envreal); + process_args(envcopy, pass); + nasm_free(envcopy); + } + + /* + * Now process the actual command line. + */ + while (--argc) { + bool advance; + argv++; + if (argv[0][0] == '@') { + /* + * We have a response file, so process this as a set of + * arguments like the environment variable. This allows us + * to have multiple arguments on a single line, which is + * different to the -@resp file processing below for regular + * NASM. + */ + process_response_file(argv[0]+1, pass); + argc--; + argv++; + } + if (!stopoptions && argv[0][0] == '-' && argv[0][1] == '@') { + p = get_param(argv[0], argc > 1 ? argv[1] : NULL, &advance); + if (p) { + rfile = nasm_open_read(p, NF_TEXT); + if (rfile) { + process_respfile(rfile, pass); + fclose(rfile); + } else { + nasm_nonfatalf(ERR_USAGE, "unable to open response file `%s'", p); + } + } + } else + advance = process_arg(argv[0], argc > 1 ? argv[1] : NULL, pass); + argv += advance, argc -= advance; + } + + /* + * Look for basic command line typos. This definitely doesn't + * catch all errors, but it might help cases of fumbled fingers. + */ + if (pass != 2) + return; + + if (!inname) + nasm_fatalf(ERR_USAGE, "no input file specified"); + else if ((errname && !strcmp(inname, errname)) || + (outname && !strcmp(inname, outname)) || + (listname && !strcmp(inname, listname)) || + (depend_file && !strcmp(inname, depend_file))) + nasm_fatalf(ERR_USAGE, "will not overwrite input file"); + + if (errname) { + error_file = nasm_open_write(errname, NF_TEXT); + if (!error_file) { + error_file = stderr; /* Revert to default! */ + nasm_fatalf(ERR_USAGE, "cannot open file `%s' for error messages", errname); + } + } +} + +static void forward_refs(insn *instruction) +{ + int i; + struct forwrefinfo *fwinf; + + instruction->forw_ref = false; + + if (!optimizing.level) + return; /* For -O0 don't bother */ + + if (!forwref) + return; + + if (forwref->lineno != globallineno) + return; + + instruction->forw_ref = true; + do { + instruction->oprs[forwref->operand].opflags |= OPFLAG_FORWARD; + forwref = saa_rstruct(forwrefs); + } while (forwref && forwref->lineno == globallineno); + + if (!pass_first()) + return; + + for (i = 0; i < instruction->operands; i++) { + if (instruction->oprs[i].opflags & OPFLAG_FORWARD) { + fwinf = saa_wstruct(forwrefs); + fwinf->lineno = globallineno; + fwinf->operand = i; + } + } +} + +static void process_insn(insn *instruction) +{ + int32_t n; + int64_t l; + + if (!instruction->times) + return; /* Nothing to do... */ + + nasm_assert(instruction->times > 0); + + /* + * NOTE: insn_size() can change instruction->times + * (usually to 1) when called. + */ + if (!pass_final()) { + int64_t start = location.offset; + for (n = 1; n <= instruction->times; n++) { + l = insn_size(location.segment, location.offset, + globalbits, instruction); + /* l == -1 -> invalid instruction */ + if (l != -1) + increment_offset(l); + } + if (list_option('p')) { + struct out_data dummy; + memset(&dummy, 0, sizeof dummy); + dummy.type = OUT_RAWDATA; /* Handled specially with .data NULL */ + dummy.offset = start; + dummy.size = location.offset - start; + lfmt->output(&dummy); + } + } else { + l = assemble(location.segment, location.offset, + globalbits, instruction); + /* We can't get an invalid instruction here */ + increment_offset(l); + + if (instruction->times > 1) { + lfmt->uplevel(LIST_TIMES, instruction->times); + for (n = 2; n <= instruction->times; n++) { + l = assemble(location.segment, location.offset, + globalbits, instruction); + increment_offset(l); + } + lfmt->downlevel(LIST_TIMES); + } + } +} + +static void assemble_file(const char *fname, struct strlist *depend_list) +{ + char *line; + insn output_ins; + uint64_t prev_offset_changed; + int64_t stall_count = 0; /* Make sure we make forward progress... */ + + switch (cmd_sb) { + case 16: + break; + case 32: + if (!iflag_cpu_level_ok(&cmd_cpu, IF_386)) + nasm_fatal("command line: 32-bit segment size requires a higher cpu"); + break; + case 64: + if (!iflag_cpu_level_ok(&cmd_cpu, IF_X86_64)) + nasm_fatal("command line: 64-bit segment size requires a higher cpu"); + break; + default: + panic(); + break; + } + + prev_offset_changed = INT64_MAX; + + if (listname && !keep_all) { + /* Remove the list file in case we die before the output pass */ + remove(listname); + } + + while (!terminate_after_phase && !pass_final()) { + _passn++; + switch (pass_type()) { + case PASS_INIT: + _pass_type = PASS_FIRST; + break; + case PASS_OPT: + if (global_offset_changed) + break; /* One more optimization pass */ + /* fall through */ + default: + _pass_type++; + break; + } + + global_offset_changed = 0; + + /* + * Create a warning buffer list unless we are in + * pass 2 (everything will be emitted immediately in pass 2.) + */ + if (warn_list) { + if (warn_list->nstr || pass_final()) + strlist_free(&warn_list); + } + + if (!pass_final() && !warn_list) + warn_list = strlist_alloc(false); + + globalbits = cmd_sb; /* set 'bits' to command line default */ + cpu = cmd_cpu; + if (listname) { + if (pass_final() || list_on_every_pass()) { + lfmt->init(listname); + } else if (list_active()) { + /* + * Looks like we used the list engine on a previous pass, + * but now it is turned off, presumably via %pragma -p + */ + lfmt->cleanup(); + if (!keep_all) + remove(listname); + } + } + + in_absolute = false; + if (!pass_first()) { + saa_rewind(forwrefs); + forwref = saa_rstruct(forwrefs); + raa_free(offsets); + offsets = raa_init(); + } + location.segment = NO_SEG; + location.offset = 0; + if (pass_first()) + location.known = true; + ofmt->reset(); + switch_segment(ofmt->section(NULL, &globalbits)); + pp_reset(fname, PP_NORMAL, pass_final() ? depend_list : NULL); + + globallineno = 0; + + while ((line = pp_getline())) { + if (++globallineno > nasm_limit[LIMIT_LINES]) + nasm_fatal("overall line count exceeds the maximum %"PRId64"\n", + nasm_limit[LIMIT_LINES]); + + /* + * Here we parse our directives; this is not handled by the + * main parser. + */ + if (process_directives(line)) + goto end_of_line; /* Just do final cleanup */ + + /* Not a directive, or even something that starts with [ */ + parse_line(line, &output_ins); + forward_refs(&output_ins); + process_insn(&output_ins); + cleanup_insn(&output_ins); + + end_of_line: + nasm_free(line); + } /* end while (line = pp_getline... */ + + pp_cleanup_pass(); + + /* We better not be having an error hold still... */ + nasm_assert(!errhold_stack); + + if (global_offset_changed) { + switch (pass_type()) { + case PASS_OPT: + /* + * This is the only pass type that can be executed more + * than once, and therefore has the ability to stall. + */ + if (global_offset_changed < prev_offset_changed) { + prev_offset_changed = global_offset_changed; + stall_count = 0; + } else { + stall_count++; + } + + if (stall_count > nasm_limit[LIMIT_STALLED] || + pass_count() >= nasm_limit[LIMIT_PASSES]) { + /* No convergence, almost certainly dead */ + nasm_nonfatalf(ERR_UNDEAD, + "unable to find valid values for all labels " + "after %"PRId64" passes; " + "stalled for %"PRId64", giving up.", + pass_count(), stall_count); + nasm_nonfatalf(ERR_UNDEAD, + "Possible causes: recursive EQUs, macro abuse."); + } + break; + + case PASS_STAB: + /*! + *!phase [off] phase error during stabilization + *! warns about symbols having changed values during + *! the second-to-last assembly pass. This is not + *! inherently fatal, but may be a source of bugs. + */ + nasm_warn(WARN_PHASE|ERR_UNDEAD, + "phase error during stabilization " + "pass, hoping for the best"); + break; + + case PASS_FINAL: + nasm_nonfatalf(ERR_UNDEAD, + "phase error during code generation pass"); + break; + + default: + /* This is normal, we'll keep going... */ + break; + } + } + + reset_warnings(); + } + + if (opt_verbose_info && pass_final()) { + /* -On and -Ov switches */ + nasm_info("assembly required 1+%"PRId64"+2 passes\n", pass_count()-3); + } + + lfmt->cleanup(); + strlist_free(&warn_list); +} + +/** + * get warning index; 0 if this is non-suppressible. + */ +static size_t warn_index(errflags severity) +{ + size_t index; + + if ((severity & ERR_MASK) >= ERR_FATAL) + return 0; /* Fatal errors are never suppressible */ + + /* Warnings MUST HAVE a warning category specifier! */ + nasm_assert((severity & (ERR_MASK|WARN_MASK)) != ERR_WARNING); + + index = WARN_IDX(severity); + nasm_assert(index < WARN_IDX_ALL); + + return index; +} + +static bool skip_this_pass(errflags severity) +{ + errflags type = severity & ERR_MASK; + + /* + * See if it's a pass-specific error or warning which should be skipped. + * We can never skip fatal errors as by definition they cannot be + * resumed from. + */ + if (type >= ERR_FATAL) + return false; + + /* + * ERR_LISTMSG messages are always skipped; the list file + * receives them anyway as this function is not consulted + * for sending to the list file. + */ + if (type == ERR_LISTMSG) + return true; + + /* + * This message not applicable unless it is the last pass we are going + * to execute; this can be either the final code-generation pass or + * the single pass executed in preproc-only mode. + */ + return (severity & ERR_PASS2) && !pass_final_or_preproc(); +} + +/** + * check for suppressed message (usually warnings or notes) + * + * @param severity the severity of the warning or error + * @return true if we should abort error/warning printing + */ +static bool is_suppressed(errflags severity) +{ + /* Fatal errors must never be suppressed */ + if ((severity & ERR_MASK) >= ERR_FATAL) + return false; + + /* This error/warning is pointless if we are dead anyway */ + if ((severity & ERR_UNDEAD) && terminate_after_phase) + return true; + + if (!(warning_state[warn_index(severity)] & WARN_ST_ENABLED)) + return true; + + if (!(severity & ERR_PP_LISTMACRO)) + return pp_suppress_error(severity); + + return false; +} + +/** + * Return the true error type (the ERR_MASK part) of the given + * severity, accounting for warnings that may need to be promoted to + * error. + * + * @param severity the severity of the warning or error + * @return true if we should error out + */ +static errflags true_error_type(errflags severity) +{ + const uint8_t warn_is_err = WARN_ST_ENABLED|WARN_ST_ERROR; + int type; + + type = severity & ERR_MASK; + + /* Promote warning to error? */ + if (type == ERR_WARNING) { + uint8_t state = warning_state[warn_index(severity)]; + if ((state & warn_is_err) == warn_is_err) + type = ERR_NONFATAL; + } + + return type; +} + +/* + * The various error type prefixes + */ +static const char * const error_pfx_table[ERR_MASK+1] = { + ";;; ", "debug: ", "info: ", "warning: ", + "error: ", "fatal: ", "critical: ", "panic: " +}; +static const char no_file_name[] = "nasm"; /* What to print if no file name */ + +/* + * For fatal/critical/panic errors, kill this process. + */ +static fatal_func die_hard(errflags true_type, errflags severity) +{ + fflush(NULL); + + if (true_type == ERR_PANIC && abort_on_panic) + abort(); + + if (ofile) { + fclose(ofile); + if (!keep_all) + remove(outname); + ofile = NULL; + } + + if (severity & ERR_USAGE) + usage(); + + /* Terminate immediately */ + exit(true_type - ERR_FATAL + 1); +} + +/* + * Returns the struct src_location appropriate for use, after some + * potential filename mangling. + */ +static struct src_location error_where(errflags severity) +{ + struct src_location where; + + if (severity & ERR_NOFILE) { + where.filename = NULL; + where.lineno = 0; + } else { + where = src_where_error(); + + if (!where.filename) { + where.filename = + inname && inname[0] ? inname : + outname && outname[0] ? outname : + NULL; + where.lineno = 0; + } + } + + return where; +} + +/* + * error reporting for critical and panic errors: minimize + * the amount of system dependencies for getting a message out, + * and in particular try to avoid memory allocations. + */ +fatal_func nasm_verror_critical(errflags severity, const char *fmt, va_list args) +{ + struct src_location where; + errflags true_type = severity & ERR_MASK; + static bool been_here = false; + + if (unlikely(been_here)) + abort(); /* Recursive error... just die */ + + been_here = true; + + where = error_where(severity); + if (!where.filename) + where.filename = no_file_name; + + fputs(error_pfx_table[severity], error_file); + fputs(where.filename, error_file); + if (where.lineno) { + fprintf(error_file, "%s%"PRId32"%s", + errfmt->beforeline, where.lineno, errfmt->afterline); + } + fputs(errfmt->beforemsg, error_file); + vfprintf(error_file, fmt, args); + fputc('\n', error_file); + + die_hard(true_type, severity); +} + +/** + * Stack of tentative error hold lists. + */ +struct nasm_errtext { + struct nasm_errtext *next; + char *msg; /* Owned by this structure */ + struct src_location where; /* Owned by the srcfile system */ + errflags severity; + errflags true_type; +}; +struct nasm_errhold { + struct nasm_errhold *up; + struct nasm_errtext *head, **tail; +}; + +static void nasm_free_error(struct nasm_errtext *et) +{ + nasm_free(et->msg); + nasm_free(et); +} + +static void nasm_issue_error(struct nasm_errtext *et); + +struct nasm_errhold *nasm_error_hold_push(void) +{ + struct nasm_errhold *eh; + + nasm_new(eh); + eh->up = errhold_stack; + eh->tail = &eh->head; + errhold_stack = eh; + + return eh; +} + +void nasm_error_hold_pop(struct nasm_errhold *eh, bool issue) +{ + struct nasm_errtext *et, *etmp; + + /* Allow calling with a null argument saying no hold in the first place */ + if (!eh) + return; + + /* This *must* be the current top of the errhold stack */ + nasm_assert(eh == errhold_stack); + + if (eh->head) { + if (issue) { + if (eh->up) { + /* Commit the current hold list to the previous level */ + *eh->up->tail = eh->head; + eh->up->tail = eh->tail; + } else { + /* Issue errors */ + list_for_each_safe(et, etmp, eh->head) + nasm_issue_error(et); + } + } else { + /* Free the list, drop errors */ + list_for_each_safe(et, etmp, eh->head) + nasm_free_error(et); + } + } + + errhold_stack = eh->up; + nasm_free(eh); +} + +/** + * common error reporting + * This is the common back end of the error reporting schemes currently + * implemented. It prints the nature of the warning and then the + * specific error message to error_file and may or may not return. It + * doesn't return if the error severity is a "panic" or "debug" type. + * + * @param severity the severity of the warning or error + * @param fmt the printf style format string + */ +void nasm_verror(errflags severity, const char *fmt, va_list args) +{ + struct nasm_errtext *et; + errflags true_type = true_error_type(severity); + + if (true_type >= ERR_CRITICAL) + nasm_verror_critical(severity, fmt, args); + + if (is_suppressed(severity)) + return; + + nasm_new(et); + et->severity = severity; + et->true_type = true_type; + et->msg = nasm_vasprintf(fmt, args); + et->where = error_where(severity); + + if (errhold_stack && true_type <= ERR_NONFATAL) { + /* It is a tentative error */ + *errhold_stack->tail = et; + errhold_stack->tail = &et->next; + } else { + nasm_issue_error(et); + } + + /* + * Don't do this before then, if we do, we lose messages in the list + * file, as the list file is only generated in the last pass. + */ + if (skip_this_pass(severity)) + return; + + if (!(severity & (ERR_HERE|ERR_PP_LISTMACRO))) + pp_error_list_macros(severity); +} + +/* + * Actually print, list and take action on an error + */ +static void nasm_issue_error(struct nasm_errtext *et) +{ + const char *pfx; + char warnsuf[64]; /* Warning suffix */ + char linestr[64]; /* Formatted line number if applicable */ + const errflags severity = et->severity; + const errflags true_type = et->true_type; + const struct src_location where = et->where; + + if (severity & ERR_NO_SEVERITY) + pfx = ""; + else + pfx = error_pfx_table[true_type]; + + *warnsuf = 0; + if ((severity & (ERR_MASK|ERR_HERE|ERR_PP_LISTMACRO)) == ERR_WARNING) { + /* + * It's a warning without ERR_HERE defined, and we are not already + * unwinding the macros that led us here. + */ + snprintf(warnsuf, sizeof warnsuf, " [-w+%s%s]", + (true_type >= ERR_NONFATAL) ? "error=" : "", + warning_name[warn_index(severity)]); + } + + *linestr = 0; + if (where.lineno) { + snprintf(linestr, sizeof linestr, "%s%"PRId32"%s", + errfmt->beforeline, where.lineno, errfmt->afterline); + } + + if (!skip_this_pass(severity)) { + const char *file = where.filename ? where.filename : no_file_name; + const char *here = ""; + + if (severity & ERR_HERE) { + here = where.filename ? " here" : " in an unknown location"; + } + + if (warn_list && true_type < ERR_NONFATAL) { + /* + * Buffer up warnings until we either get an error + * or we are on the code-generation pass. + */ + strlist_printf(warn_list, "%s%s%s%s%s%s%s", + file, linestr, errfmt->beforemsg, + pfx, et->msg, here, warnsuf); + } else { + /* + * Actually output an error. If we have buffered + * warnings, and this is a non-warning, output them now. + */ + if (true_type >= ERR_NONFATAL && warn_list) { + strlist_write(warn_list, "\n", error_file); + strlist_free(&warn_list); + } + + fprintf(error_file, "%s%s%s%s%s%s%s\n", + file, linestr, errfmt->beforemsg, + pfx, et->msg, here, warnsuf); + } + } + + /* Are we recursing from error_list_macros? */ + if (severity & ERR_PP_LISTMACRO) + goto done; + + /* + * Don't suppress this with skip_this_pass(), or we don't get + * pass1 or preprocessor warnings in the list file + */ + if (severity & ERR_HERE) { + if (where.lineno) + lfmt->error(severity, "%s%s at %s:%"PRId32"%s", + pfx, et->msg, where.filename, where.lineno, warnsuf); + else if (where.filename) + lfmt->error(severity, "%s%s in file %s%s", + pfx, et->msg, where.filename, warnsuf); + else + lfmt->error(severity, "%s%s in an unknown location%s", + pfx, et->msg, warnsuf); + } else { + lfmt->error(severity, "%s%s%s", pfx, et->msg, warnsuf); + } + + if (skip_this_pass(severity)) + goto done; + + if (true_type >= ERR_FATAL) + die_hard(true_type, severity); + else if (true_type >= ERR_NONFATAL) + terminate_after_phase = true; + +done: + nasm_free_error(et); +} + +static void usage(void) +{ + fprintf(error_file, "Type %s -h for help.\n", _progname); +} + +static void help(FILE *out) +{ + int i; + + fprintf(out, + "Usage: %s [-@ response_file] [options...] [--] filename\n" + " %s -v (or --v)\n", + _progname, _progname); + fputs( + "\n" + "Options (values in brackets indicate defaults):\n" + "\n" + " -h show this text and exit (also --help)\n" + " -v (or --v) print the NASM version number and exit\n" + " -@ file response file; one command line option per line\n" + "\n" + " -o outfile write output to outfile\n" + " --keep-all output files will not be removed even if an error happens\n" + "\n" + " -Xformat specify error reporting format (gnu or vc)\n" + " -s redirect error messages to stdout\n" + " -Zfile redirect error messages to file\n" + "\n" + " -M generate Makefile dependencies on stdout\n" + " -MG d:o, missing files assumed generated\n" + " -MF file set Makefile dependency file\n" + " -MD file assemble and generate dependencies\n" + " -MT file dependency target name\n" + " -MQ file dependency target name (quoted)\n" + " -MP emit phony targets\n" + "\n" + " -f format select output file format\n" + , out); + ofmt_list(ofmt, out); + fputs( + "\n" + " -g generate debugging information\n" + " -F format select a debugging format (output format dependent)\n" + " -gformat same as -g -F format\n" + , out); + dfmt_list(out); + fputs( + "\n" + " -l listfile write listing to a list file\n" + " -Lflags... add optional information to the list file\n" + " -Lb show builtin macro packages (standard and %use)\n" + " -Ld show byte and repeat counts in decimal, not hex\n" + " -Le show the preprocessed output\n" + " -Lf ignore .nolist (force output)\n" + " -Lm show multi-line macro calls with expanded parameters\n" + " -Lp output a list file every pass, in case of errors\n" + " -Ls show all single-line macro definitions\n" + " -Lw flush the output after every line (very slow!)\n" + " -L+ enable all listing options except -Lw (very verbose!)\n" + "\n" + " -Oflags... optimize opcodes, immediates and branch offsets\n" + " -O0 no optimization\n" + " -O1 minimal optimization\n" + " -Ox multipass optimization (default)\n" + " -Ov display the number of passes executed at the end\n" + " -t assemble in limited SciTech TASM compatible mode\n" + "\n" + " -E (or -e) preprocess only (writes output to stdout by default)\n" + " -a don't preprocess (assemble only)\n" + " -Ipath add a pathname to the include file path\n" + " -Pfile pre-include a file (also --include)\n" + " -Dmacro[=str] pre-define a macro\n" + " -Umacro undefine a macro\n" + " --pragma str pre-executes a specific %%pragma\n" + " --before str add line (usually a preprocessor statement) before the input\n" + " --no-line ignore %line directives in input\n" + "\n" + " --prefix str prepend the given string to the names of all extern,\n" + " common and global symbols (also --gprefix)\n" + " --suffix str append the given string to the names of all extern,\n" + " common and global symbols (also --gprefix)\n" + " --lprefix str prepend the given string to local symbols\n" + " --lpostfix str append the given string to local symbols\n" + "\n" + " --reproducible attempt to produce run-to-run identical output\n" + "\n" + " -w+x enable warning x (also -Wx)\n" + " -w-x disable warning x (also -Wno-x)\n" + " -w[+-]error promote all warnings to errors (also -Werror)\n" + " -w[+-]error=x promote warning x to errors (also -Werror=x)\n" + , out); + + fprintf(out, " %-20s %s\n", + warning_name[WARN_IDX_ALL], warning_help[WARN_IDX_ALL]); + + for (i = 1; i < WARN_IDX_ALL; i++) { + const char *me = warning_name[i]; + const char *prev = warning_name[i-1]; + const char *next = warning_name[i+1]; + + if (prev) { + int prev_len = strlen(prev); + const char *dash = me; + + while ((dash = strchr(dash+1, '-'))) { + int prefix_len = dash - me; /* Not including final dash */ + if (strncmp(next, me, prefix_len+1)) { + /* Only one or last option with this prefix */ + break; + } + if (prefix_len >= prev_len || + strncmp(prev, me, prefix_len) || + (prev[prefix_len] != '-' && prev[prefix_len] != '\0')) { + /* This prefix is different from the previous option */ + fprintf(out, " %-20.*s all warnings prefixed with \"%.*s\"\n", + prefix_len, me, prefix_len+1, me); + } + } + } + + fprintf(out, " %-20s %s%s\n", + warning_name[i], warning_help[i], + (warning_default[i] & WARN_ST_ERROR) ? " [error]" : + (warning_default[i] & WARN_ST_ENABLED) ? " [on]" : " [off]"); + } + + fputs( + "\n" + " --limit-X val set execution limit X\n" + , out); + + + for (i = 0; i <= LIMIT_MAX; i++) { + fprintf(out, " %-20s %s [", + limit_info[i].name, limit_info[i].help); + if (nasm_limit[i] < LIMIT_MAX_VAL) { + fprintf(out, "%"PRId64"]\n", nasm_limit[i]); + } else { + fputs("unlimited]\n", out); + } + } +} -- cgit v1.2.3