| /* SPDX-License-Identifier: BSD-2-Clause */ |
| /* Copyright 1996-2025 The NASM Authors - All Rights Reserved */ |
| |
| /* |
| * 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 open_and_process_respfile(char *, int); |
| 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 *out, const char *what); |
| |
| 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; |
| |
| static bool using_debug_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; |
| |
| 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; |
| |
| static FILE *error_file; /* Where to write error messages */ |
| errflags errflags_never = 0; /* Error flags to unconditionally suppress */ |
| |
| FILE *ofile = NULL; |
| enum optimization optimizing = OPTIM_DEFAULT; |
| 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; |
| |
| 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-line 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 int64_t set_curr_offs(int64_t l_off) |
| { |
| location.offset = l_off; |
| if (in_absolute) { |
| absolute.offset = l_off; |
| } else { |
| offsets = raa_write(offsets, location.segment, l_off); |
| } |
| return l_off; |
| } |
| |
| int64_t increment_offset(int64_t delta) |
| { |
| int64_t newoffs = location.offset + delta; |
| |
| if (unlikely(!delta)) |
| return newoffs; |
| |
| return set_curr_offs(newoffs); |
| } |
| |
| /* |
| * 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); |
| |
| /* |
| * 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... |
| * |
| * get_param() errors on a missing argument; get_opt_param() does not. |
| */ |
| static char *get_opt_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; |
| } |
| return NULL; |
| } |
| static char *get_param(char *p, char *q, bool *advance) |
| { |
| char *r = get_opt_param(p, q, advance); |
| if (!r) |
| nasm_nonfatalf(ERR_USAGE, "option `-%c' requires an argument", p[1]); |
| return r; |
| } |
| |
| /* |
| * 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_INFO, |
| OPT_REPRODUCIBLE, |
| OPT_BITS |
| }; |
| 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_MAYBE, 0}, |
| {"abort-on-panic", OPT_ABORT_ON_PANIC, ARG_NO, 0}, |
| {"prefix", OPT_MANGLE, ARG_YES, D_PREFIX}, |
| {"postfix", OPT_MANGLE, ARG_YES, D_POSTFIX}, |
| {"suffix", OPT_MANGLE, ARG_YES, D_SUFFIX}, |
| {"gprefix", OPT_MANGLE, ARG_YES, D_GPREFIX}, |
| {"gpostfix", OPT_MANGLE, ARG_YES, D_GPOSTFIX}, |
| {"gsuffix", OPT_MANGLE, ARG_YES, D_GSUFFIX}, |
| {"lprefix", OPT_MANGLE, ARG_YES, D_LPREFIX}, |
| {"lpostfix", OPT_MANGLE, ARG_YES, D_LPOSTFIX}, |
| {"lsuffix", OPT_MANGLE, ARG_YES, D_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}, |
| {"info", OPT_INFO , ARG_MAYBE, 0}, |
| {"verbose", OPT_INFO , ARG_MAYBE, 0}, |
| {"debug", OPT_DEBUG, ARG_MAYBE, 0}, |
| {"reproducible", OPT_REPRODUCIBLE, ARG_NO, 0}, |
| {"bits", OPT_BITS, ARG_YES, 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 '@': |
| open_and_process_respfile(param, pass); |
| break; |
| 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 = OPTIM_ALL_ENABLED; |
| } 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); |
| switch (opt) { |
| case 0: |
| optimizing = OPTIM_LEVEL_0; |
| break; |
| case 1: |
| optimizing = OPTIM_LEVEL_1; |
| break; |
| default: |
| optimizing = OPTIM_ALL_ENABLED; |
| break; |
| } |
| break; |
| |
| case 'v': |
| case '+': |
| param++; |
| opt_verbose_info++; |
| break; |
| |
| case 'x': |
| param++; |
| optimizing = OPTIM_ALL_ENABLED; |
| break; |
| |
| default: |
| nasm_fatal("unknown optimization option -O%c\n", |
| *param); |
| break; |
| } |
| } |
| } |
| } |
| 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': |
| case '?': |
| help(stdout, get_opt_param(p, q, &advance)); |
| exit(0); /* never need usage message here */ |
| break; |
| |
| case 'y': |
| /* legacy option */ |
| help(stdout, "-F"); |
| 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_BITS: |
| if (pass == 2) |
| pp_pre_command("BITS", 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: |
| if (pass == 1) |
| debug_nasm = param ? |
| strtoul(param, NULL, 10) : debug_nasm+1; |
| break; |
| case OPT_INFO: |
| if (pass == 1) |
| opt_verbose_info = param ? |
| strtoul(param, NULL, 10) : opt_verbose_info+1; |
| break; |
| case OPT_REPRODUCIBLE: |
| reproducible = true; |
| break; |
| case OPT_HELP: |
| /* Allow --help topic without *requiring* topic */ |
| if (!param) |
| param = q; |
| help(stdout, param); |
| 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 open_and_process_respfile(char *respfile, int pass) |
| { |
| FILE *rfile; |
| |
| rfile = nasm_open_read(respfile, NF_TEXT); |
| if (rfile) { |
| process_respfile(rfile, pass); |
| fclose(rfile); |
| } else { |
| nasm_nonfatalf(ERR_USAGE, "unable to open response file `%s'", respfile); |
| } |
| } |
| |
| static void parse_cmdline(int argc, char **argv, int pass) |
| { |
| char *envreal, *envcopy = NULL; |
| |
| /* |
| * 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++; |
| } |
| |
| 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; |
| |
| /* Don't bother for -O1 */ |
| if (instruction->opt & OPTIM_DISABLE_FWREF) |
| return; |
| |
| if (!forwref) |
| return; |
| |
| if (forwref->lineno != globallineno) |
| return; |
| |
| 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 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); |
| |
| /* Suppress ERR_PASS2 unless we are actually in the final pass */ |
| errflags_never = 0; |
| if (!pass_final()) |
| errflags_never |= ERR_PASS2; |
| |
| globl.bits = cmd_sb; /* set 'bits' to command line default */ |
| globl.bnd = false; |
| globl.rel = 0; |
| globl.reldef = EAF_FS|EAF_GS; /* For now, don't warn on fs:/gs: absolute */ |
| globl.dollarhex = true; |
| |
| cpu = cmd_cpu; |
| if (listname) { |
| if (list_on_this_pass()) { |
| /* |
| * Generating a list file on this 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, &globl.bits)); |
| pp_reset(fname, PP_NORMAL, depend_list); |
| |
| 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, globl.bits); |
| 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: |
| 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 (pass_final()) { |
| /* -On and -Ov switches */ |
| nasm_info(1, "assembly required 1+%"PRId64"+2 passes\n", pass_count()-3); |
| } |
| |
| lfmt->cleanup(); |
| strlist_free(&warn_list); |
| } |
| |
| 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 and ERR_NOTE 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_NOTE) |
| return true; |
| |
| /* |
| * This message is 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 flags) |
| { |
| const errflags severity = flags & ERR_MASK; |
| const errflags level = WARN_IDX(flags); |
| |
| if (severity >= ERR_FATAL) { |
| /* Fatal errors or higher can never be suppressed */ |
| return false; |
| } |
| |
| if (flags & errflags_never) |
| return true; |
| |
| switch (severity) { |
| case ERR_WARNING: |
| if (!(warning_state[level] & WARN_ST_ENABLED)) |
| return true; |
| break; |
| |
| case ERR_INFO: |
| if (!info_level(level)) |
| return true; |
| break; |
| |
| case ERR_DEBUG: |
| if (!debug_level(level)) |
| return true; |
| break; |
| |
| default: |
| break; |
| } |
| |
| /* Suppressed by the preprocessor? */ |
| if (!(flags & ERR_PP_LISTMACRO)) |
| return pp_suppress_error(flags); |
| |
| 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 pure_func 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_IDX(severity)]; |
| if ((state & warn_is_err) == warn_is_err) |
| type = ERR_NONFATAL; |
| } |
| |
| return type; |
| } |
| |
| 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(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; |
| } |
| |
| /* Pop an error hold. Returns the highest severity issued or dropped. */ |
| errflags nasm_error_hold_pop(struct nasm_errhold *eh, bool issue) |
| { |
| struct nasm_errtext *et, *etmp; |
| errflags worst = 0; |
| |
| /* |
| * Allow calling with a null argument saying no hold in the first place. |
| */ |
| if (!eh) |
| return worst; |
| |
| /* 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) { |
| if (et->true_type > worst) |
| worst = et->true_type; |
| nasm_issue_error(et); |
| } |
| } |
| } else { |
| /* Free the list, drop errors */ |
| list_for_each_safe(et, etmp, eh->head) { |
| if (et->true_type > worst) |
| worst = et->true_type; |
| nasm_free_error(et); |
| } |
| } |
| } |
| |
| errhold_stack = eh->up; |
| nasm_free(eh); |
| return worst; |
| } |
| |
| /** |
| * 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); |
| abort(); |
| } |
| |
| 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(true_type); |
| |
| *warnsuf = 0; |
| if (!(severity & (ERR_HERE|ERR_PP_LISTMACRO))) { |
| const unsigned int level = WARN_IDX(severity); |
| |
| switch (severity & ERR_MASK) { |
| case ERR_WARNING: |
| snprintf(warnsuf, sizeof warnsuf, " [-w+%s%s]", |
| (true_type >= ERR_NONFATAL) ? "error=" : "", |
| warning_name[level]); |
| break; |
| case ERR_DEBUG: |
| snprintf(warnsuf, sizeof warnsuf, " [--debug=%u]", debug_nasm); |
| break; |
| case ERR_INFO: |
| snprintf(warnsuf, sizeof warnsuf, " [--info=%u]", opt_verbose_info); |
| break; |
| default: |
| /* Not WARNING, DEBUG or INFO, not suppressible */ |
| break; |
| } |
| } |
| |
| *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; |
| errflags_never |= ERR_UNDEAD; |
| } |
| |
| done: |
| nasm_free_error(et); |
| } |
| |
| static void usage(void) |
| { |
| fprintf(error_file, |
| "Usage: %s [-@ response_file] [options...] [--] filename\n" |
| " For additional help:\n" |
| " %s -h [run|topics|all|-option]\n", |
| _progname, _progname); |
| } |
| |
| enum help_with { |
| HW_ALL = 0, |
| HW_OPT = 1, |
| HW_ERR = 2, |
| HW_LIMIT = 3 |
| }; |
| |
| static inline bool help_is(int with, int h) { |
| return with == HW_ALL || with == h; |
| } |
| static inline bool help_opt(int with) |
| { |
| return help_is(with, HW_OPT); |
| } |
| static inline bool help_optor(int with, int h) |
| { |
| return help_opt(with) || with == h; |
| } |
| |
| static void help(FILE *out, const char *what) |
| { |
| int i; |
| int with; |
| |
| #define SEE(x) (with == HW_OPT ? " (see -h " x ")" : "") |
| |
| with = HW_ERR; |
| |
| if (!what || !*what || !strcmp(what, "-") || !strcmp(what, "--") || |
| !nasm_stricmp(what, "opt") || |
| !nasm_stricmp(what, "run") || |
| !nasm_stricmp(what, "cmd")) { |
| with = HW_OPT; |
| } else if (!nasm_stricmp(what, "all")) { |
| with = HW_ALL; |
| } else if (!nasm_strnicmp(what, "--limit", 7) || |
| !nasm_strnicmp(what, "limit", 5)) { |
| with = HW_LIMIT; |
| } else if (!nasm_stricmp(what, "--help") || |
| !nasm_stricmp(what, "help") || |
| !nasm_stricmp(what, "list") || |
| !nasm_stricmp(what, "topics") || |
| !nasm_stricmp(what, "index")) { |
| with = 'h'; |
| } else if ((what[0] == '-' && what[1] && !what[2]) || |
| (what[0] && !what[1])) { |
| with = what[0] == '-' ? what[1] : what[0]; |
| switch (with) { |
| case 'h': |
| case 'X': |
| case 'L': |
| case 'f': |
| case 'F': |
| case 'O': |
| case 'w': |
| case 'M': |
| break; |
| case 'g': |
| with = 'F'; |
| break; |
| case 'W': |
| with = 'w'; |
| break; |
| case '?': |
| with = 'h'; |
| break; |
| case '*': |
| with = HW_ALL; |
| break; |
| default: |
| with = HW_ERR; |
| break; |
| } |
| } |
| |
| if (with == HW_ERR) { |
| fprintf(out, "Sorry, no help available for `%s'\n", what); |
| return; |
| } |
| |
| if (help_opt(with)) { |
| fprintf(out, |
| "Usage: %s [-@ response_file] [options...] [--] filename\n" |
| " Options:\n" |
| " -v (or --v) print the NASM version number and exit\n" |
| " -@ file response file; one command line option per line\n", |
| _progname); |
| } |
| if (help_optor(with, 'h')) { |
| fputs( |
| " -h list command line options and exit (also --help)\n" |
| , out); |
| if (with == HW_OPT) { |
| fputs( |
| " -h -opt show additional help for option \"-opt\"\n" |
| , out); |
| } |
| fputs( |
| " -h all show all available command line help\n" |
| " -h topics show list of help topics (also -h list)\n" |
| , out); |
| } |
| if (help_is(with, 'h')) { |
| fputs( |
| " -h -X show list of error message formats\n" |
| " -h -M show list of dependency generation options\n" |
| " -h -f show list of object code output formats\n" |
| " -h -F show list of debug information formats\n" |
| " -h -L show list of list file option flags\n" |
| " -h -O show list of optimization flags\n" |
| " -h -w show list of warning classes and defaults\n" |
| " -h --limit show list of resource limits and defaults\n" |
| , out); |
| } |
| if (help_opt(with)) { |
| fputs( |
| " -o outfile write output to outfile\n" |
| " --keep-all output files will not be removed even if an error happens\n" |
| , out); |
| } |
| if (help_optor(with, 'X')) { |
| fprintf(out, |
| " -Xformat specify error reporting format%s\n", |
| SEE("-X")); |
| } |
| if (help_is(with, 'X')) { |
| fputs( |
| " -Xgnu report errors in GNU format (default)\n" |
| " -Xgcc alias for -Xgnu\n" |
| " -Xvc report errors in Visual Studio format\n" |
| " -Xmsvc alias for -Xvc\n" |
| " -Xms alias for -Xvc\n" |
| , out); |
| } |
| if (help_opt(with)) { |
| fputs( |
| " -s redirect messages to stdout\n" |
| " -Zfile redirect messages to file\n" |
| " --info[=lvl] display optional informational messages\n" |
| " --debug[=lvl] display NASM internal debugging messages\n" |
| , out); |
| } |
| if (help_optor(with, 'M')) { |
| fprintf(out, |
| " -M... generate Makefile dependencies%s\n", |
| with == HW_OPT |
| ? " (see -h -M)" |
| : " (multiple options permitted):"); |
| } |
| if (help_is(with, 'M')) { |
| fputs( |
| " -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" |
| " -MW generate output in Watcom wmake syntax\n" |
| , out); |
| } |
| if (help_optor(with, 'f')) { |
| fprintf(out, |
| " -f format select output file format%s\n", |
| SEE("-f")); |
| } |
| if (help_is(with, 'f')) { |
| ofmt_list(ofmt, out); |
| } |
| if (help_optor(with, 'F')) { |
| fprintf(out, |
| " -g generate debugging information\n" |
| " -F format select a debugging format%s\n" |
| " -gformat same as -g -F format\n", |
| SEE("-F")); |
| } |
| if (help_is(with, 'F')) { |
| dfmt_list(out); |
| } |
| if (help_optor(with, 'L')) { |
| fprintf(out, |
| " -l listfile write listing to a list file\n" |
| " -Lflags... add information to the list file%s\n", |
| SEE("-L")); |
| } |
| if (help_is(with, 'L')) { |
| fputs( |
| " -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" |
| " -LF ignore [LIST -] directives (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" |
| , out); |
| } |
| if (help_optor(with, 'O')) { |
| fprintf(out, |
| " -Oflags... select optimization%s\n", SEE("-O")); |
| } |
| if (help_is(with, 'O')) { |
| fputs( |
| " -O0 no optimization\n" |
| " -O1 minimal optimization\n" |
| " -Ox multipass optimization (default, recommended)\n" |
| " -Ov display the number of passes executed at the end\n" |
| , out); |
| } |
| if (help_opt(with)) { |
| fputs( |
| " -t assemble in limited SciTech TASM compatible mode\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" |
| , out); |
| } |
| if (help_optor(with, 'w')) { |
| fprintf(out, |
| " -w+x enable warning x %s(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", |
| SEE("-w")); |
| } |
| if (help_is(with, 'w')) { |
| fputs(" Defaults in brackets:\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++) { |
| static const char nl_indent[] = |
| "\n "; |
| 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%sall warnings prefixed with \"%.*s\"\n", |
| prefix_len, me, |
| prefix_len > 19 ? nl_indent : " ", |
| prefix_len+1, me); |
| } |
| } |
| } |
| |
| fprintf(out, " %-20s%s%s%s\n", |
| warning_name[i], |
| strlen(warning_name[i]) > 19 ? nl_indent : " ", |
| warning_help[i], |
| (warning_default[i] & WARN_ST_ERROR) ? " [error]" : |
| (warning_default[i] & WARN_ST_ENABLED) ? " [on]" : " [off]"); |
| } |
| } |
| if (help_opt(with)) { |
| fputs( |
| " --pragma str pre-executes a specific %pragma\n" |
| " --before str add line (usually a preprocessor statement) before the input\n" |
| " --bits nn set bits to nn (equivalent to --before \"BITS nn\")\n" |
| " --no-line ignore %line directives in input\n" |
| " --gprefix str prepend the given string to the names of all extern,\n" |
| " common and global symbols (also --prefix)\n" |
| " --gpostfix str append the given string to the names of all extern,\n" |
| " common and global symbols (also --postfix)\n" |
| " --lprefix str prepend the given string to local symbols\n" |
| " --lpostfix str append the given string to local symbols\n" |
| " --reproducible attempt to produce run-to-run identical output\n" |
| , out); |
| } |
| if (help_optor(with, HW_LIMIT)) { |
| fprintf(out, " --limit-X val set execution limit X%s\n", |
| SEE("--limit")); |
| } |
| if (help_is(with, HW_LIMIT)) { |
| fputs(" Defaults in brackets:\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); |
| } |
| } |
| } |
| #undef SEE |
| } |