nasm/asm/nasm.c
H. Peter Anvin (Intel) 18c778c590 files: formalize storing "mapped" file names in the filenames system
Formalize the mechanism for storing mapped (non-filesystem) filenames
in the filenames array. These are not compared against input or output
files for overwrite checks.

Add the infrastructure for comparing more than one input filename
(future proofing.)

Move the Makefile dependency target name to the filenames system.

Signed-off-by: H. Peter Anvin (Intel) <hpa@zytor.com>
2026-07-06 17:08:01 -07:00

2287 lines
66 KiB
C

/* 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"
#include "files.h"
#include "error.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;
};
struct debug_prefix_list {
struct debug_prefix_list *next;
char *base;
char *dest;
};
const char *_progname;
static void open_and_process_respfile(char *, int);
static void parse_cmdline(int, char **, int);
static void assemble_file(const char *, char const *, struct strlist *);
static void help(FILE *out, const char *what);
static bool using_debug_info;
static const char *debug_format;
static bool debug_format_owned;
#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;
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;
errflags errflags_never = 0; /* Error flags to unconditionally suppress */
FILE *ofile = NULL;
enum optimization optimizing = OPTIM_DEFAULT;
static int cmd_sb = 16; /* by default */
static struct debug_prefix_list *debug_prefixes = NULL;
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;
struct strlist *depend_list;
static inline bool terminate_after_phase(void)
{
return erropt.worst >= ERR_NONFATAL;
}
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.
*/
int64_t nasm_limit[LIMIT_MAX];
static int64_t nasm_limit_from_cmdline[LIMIT_MAX];
struct limit_info {
const char *name;
const char *help;
int64_t default_val; /* Default, or 0 for maximum */
int64_t max_val; /* Absolute maximum, or 0 for LIMIT_MAX_VAL */
};
/* The order here must match enum nasm_limit in nasm.h */
static const struct limit_info limit_info[LIMIT_MAX] = {
{ "passes", "total number of passes", 0, 0 },
{ "stalled-passes", "number of passes without forward progress", 1000, 0 },
{ "macro-levels", "levels of macro expansion", 10000, 0 },
{ "macro-tokens", "tokens processed during single-line macro expansion",
10000000, 0 },
{ "mmacros", "multi-line macros before final return",
100000, 0 },
{ "rep", "%rep count", 1000000, 0 },
{ "eval", "expression evaluation descent", 8192, 0 },
{ "lines", "total source lines processed", 2000000000, 0 },
{ "params", "multi-line macro parameter count", 16383, INT_MAX >> 1 }
};
static int64_t limit_default(enum nasm_limit limit)
{
const struct limit_info *l;
int64_t v, m;
if (limit >= LIMIT_MAX)
return 0;
l = &limit_info[limit];
m = l->max_val;
if (!m || m > LIMIT_MAX_VAL)
m = LIMIT_MAX_VAL;
v = l->default_val;
return (!v || v > m) ? m : v;
}
static int64_t limit_max(enum nasm_limit limit)
{
const struct limit_info *l;
int64_t m;
if (limit >= LIMIT_MAX)
return 0;
l = &limit_info[limit];
m = l->max_val;
if (!m || m > LIMIT_MAX_VAL)
m = LIMIT_MAX_VAL;
return m;
}
static const char *limit_str(int64_t limit_val)
{
static char buf[3*sizeof(limit_val)+3]; /* Excessive but safe */
if (limit_val < LIMIT_MAX_VAL) {
snprintf(buf, sizeof buf, "%"PRId64, limit_val);
return buf;
} else {
return "unlimited";
}
}
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_default(i);
/*
* 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, init_val, default_val, max_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;
}
max_val = limit_max(i);
default_val = limit_default(i);
init_val = nasm_limit_from_cmdline[i];
if (!init_val)
init_val = default_val;
if (!strcmp(valstr, "*") ||
!nasm_stricmp(valstr, "reset") ||
!nasm_stricmp(valstr, "init")) {
val = init_val;
} else if (!nasm_stricmp(valstr, "default")) {
val = default_val;
} else if (!nasm_stricmp(valstr, "unlimited") ||
!nasm_stricmp(valstr, "maximum") ||
!nasm_stricmp(valstr, "max")) {
val = max_val;
} else {
if (not_started())
errlevel = ERR_WARNING|WARN_OTHER|ERR_USAGE;
else
errlevel = ERR_WARNING|WARN_PRAGMA_BAD;
val = readnum(valstr, &rn_error);
if (rn_error || val < 0) {
nasm_error(errlevel, "invalid limit value: `%s %s'",
limit, valstr);
return DIRR_ERROR;
}
if (!val)
val = init_val;
if (val > max_val) {
val = max_val;
if (max_val < LIMIT_MAX_VAL) {
nasm_error(errlevel,
"excessive limit value `%s %s' capped to %"PRId64,
limit, valstr, max_val);
/* ... but proceed after the warning ... */
}
}
}
nasm_limit[i] = val;
return DIRR_OK;
}
int64_t nasm_get_limit(const char *limit, enum get_limit_which which)
{
int i;
for (i = 0; i < LIMIT_MAX; i++) {
if (!nasm_stricmp(limit, limit_info[i].name)) {
switch (which) {
case GET_LIMIT_CURRENT:
return nasm_limit[i];
case GET_LIMIT_INIT:
return nasm_limit_from_cmdline[i];
case GET_LIMIT_DEFAULT:
return limit_default(i);
case GET_LIMIT_MAX:
return limit_max(i);
default:
return 0;
}
}
}
return 0;
}
const char *nasm_limit_name(enum nasm_limit i)
{
if (i >= LIMIT_MAX)
return NULL;
return limit_info[i].name;
}
static void reset_limits_before_pass(void)
{
memcpy(nasm_limit, nasm_limit_from_cmdline, sizeof nasm_limit);
}
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;
const char * const depend_file = get_filename(FN_DEPENDFILE);
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 :", get_filename(FN_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;
}
}
static const char *fix_outfile_name(void)
{
const char * const outname = get_filename(FN_OUTFILE);
const char *inname;
char *newname;
if (outname)
return outname; /* Output file already set */
if (operating_mode & OP_PREPROCESS)
return NULL; /* Preprocessing to stdout */
/*
* No filename specified, and not in preprocessing mode, create
* a default output file name.
*/
inname = get_filename(FN_INFILE);
newname = filename_set_extension(inname, ofmt->extension);
if (!strcmp(newname, inname)) {
nasm_strdupto(&newname, "nasm.out");
nasm_warn(WARN_OTHER,
"default output file same as input, using `%s' for output\n",
newname);
}
return set_filename(FN_OUTFILE, newname);
}
static const char *fix_dependfile_name(void)
{
const char * const depend_file = get_filename(FN_DEPENDFILE);
const char *outname, *inname;
char *newname;
if (depend_file)
return depend_file;
if (!(operating_mode & ~OP_DEPEND))
return NULL; /* ONLY doing dependencies -> stdout */
outname = get_filename(FN_OUTFILE);
if (outname) {
newname = nasm_strcat(outname, ".d");
} else {
inname = get_filename(FN_INFILE);
if (inname)
newname = filename_set_extension(inname, ".d");
else
newname = nasm_strdup("nasm.d");
}
return set_filename(FN_DEPENDFILE, newname);
}
int main(int argc, char **argv)
{
/* Do these as early as possible */
erropt.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);
reset_global_defaults(0);
_pass_type = PASS_INIT;
_passn = 0;
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())
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())
return 1;
/* Save away the default states of warnings and limits */
error_init();
memcpy(nasm_limit_from_cmdline, nasm_limit, sizeof nasm_limit);
/*
* Set output and depend file names to defaults as needed.
* Call fix_dependfile_name() first - its defaults depend on
* whether or not the outfile name is a generated default.
*/
fix_dependfile_name();
fix_outfile_name();
if (operating_mode & OP_DEPEND) {
depend_list = strlist_alloc(true);
if (!get_filename(FN_DEPEND_TARGET))
set_filename(FN_DEPEND_TARGET,
quote_for_make(get_filename(FN_OUTFILE)));
} else {
depend_list = NULL;
}
reset_global_defaults(cmd_sb);
if (!(operating_mode & (OP_PREPROCESS|OP_NORMAL))) {
char *line;
error_pass_start(true);
if (depend_missing_ok)
pp_include_path(NULL); /* "assume generated" */
pp_reset(get_filename(FN_INFILE), get_filename(FN_MAPPED_INFILE), PP_DEPS, depend_list);
ofile = NULL;
while ((line = pp_getline()))
nasm_free(line);
pp_cleanup_pass();
error_pass_end();
} 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;
const char *outname = get_filename(FN_OUTFILE);
if (outname) {
ofile = nasm_open_write(outname, NF_TEXT);
if (!ofile)
nasm_fatalf(ERR_PERROR,
"unable to open output file `%s'", outname);
} else {
ofile = stdout;
}
location.known = false;
_pass_type = PASS_PREPROC;
pp_reset(get_filename(FN_INFILE), get_filename(FN_MAPPED_INFILE), PP_PREPROC, depend_list);
error_pass_start(true);
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_strto(&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', ofile);
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 (*line || linnum != -1) {
if (linnum != where.lineno) {
fprintf(ofile, "%%line %"PRId32"%+"PRId32" %s\n",
where.lineno, lineinc, quoted_file_name);
}
linnum = where.lineno + lineinc;
fputs(line, ofile);
fputc('\n', ofile);
}
nasm_free(line);
}
nasm_free(quoted_file_name);
pp_cleanup_pass();
error_pass_end();
close_output(terminate_after_phase());
}
if (operating_mode & OP_NORMAL) {
const char *outname = get_filename(FN_OUTFILE);
ofile = nasm_open_write(outname, (ofmt->flags & OFMT_TEXT) ? NF_TEXT : NF_BINARY);
if (!ofile)
nasm_fatalf(ERR_PERROR, "unable to open output file `%s'", outname);
ofmt->init();
dfmt->init();
assemble_file(get_filename(FN_INFILE), get_filename(FN_MAPPED_INFILE), depend_list);
if (!terminate_after_phase()) {
ofmt->cleanup();
cleanup_labels();
fflush(ofile);
if (ferror(ofile))
nasm_nonfatalf(ERR_PERROR,
"write error on output file `%s'", outname);
}
close_output(terminate_after_phase());
}
pp_cleanup_session();
if (depend_list && !terminate_after_phase())
emit_dependencies(depend_list);
raa_free(offsets);
saa_free(forwrefs);
eval_cleanup();
stdscan_cleanup();
src_free();
strlist_free(&include_path);
cleanup_filenames();
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;
}
/*
* Apply debug prefix mappings to a path buffer
*/
static char *debug_prefix_remap(char const* path)
{
struct debug_prefix_list *d;
size_t n;
for (d = debug_prefixes; d != NULL; d = d->next) {
n = strlen(d->base);
if (strncmp(path, d->base, n) == 0) {
size_t prefix_len = strlen(d->dest);
size_t suffix_len = strlen(path) - n;
size_t len = prefix_len + suffix_len + 1;
char* out = nasm_malloc(len);
strlcpy(out, d->dest, len);
strlcpy(&out[prefix_len], &path[n], len - prefix_len);
return out;
}
}
return nasm_strdup(path);
}
/*
* Convert a string to a POSIX make-safe form; returns a newly allocated
* string.
*/
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; returns a newly allocated
* string.
*/
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,
OPT_DEBUG_PREFIX_MAP
};
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},
{"debug-prefix-map", OPT_DEBUG_PREFIX_MAP, 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)
erropt.file = stdout;
break;
case 'o': /* output file */
if (pass == 2)
copy_filename(FN_OUTFILE, param);
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, &param, 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++;
erropt.verbose_info++;
break;
case 'x':
param++;
optimizing = OPTIM_ALL_ENABLED;
break;
default:
nasm_fatalf(ERR_USAGE,
"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(FN_LISTFILE, param);
break;
case 'L': /* listing options */
if (pass == 2)
list_update_options(param, true);
break;
case 'Z': /* error messages file */
if (pass == 1)
copy_filename(FN_ERRFILE, param);
break;
case 'F': /* specify debug format */
if (pass == 1) {
using_debug_info = true;
if (debug_format_owned && debug_format)
nasm_free((void *)debug_format);
debug_format = nasm_strdup(param);
debug_format_owned = true;
}
break;
case 'X': /* specify error reporting format */
if (pass == 1) {
if (set_error_format(param)) {
nasm_fatalf(ERR_USAGE,
"unrecognized error reporting format `%s'",
param);
}
}
break;
case 'g':
if (pass == 1) {
using_debug_info = true;
if (p[2]) {
const char *s = nasm_skip_spaces(p + 2);
if (debug_format_owned && debug_format)
nasm_free((void *)debug_format);
debug_format = nasm_strdup(s);
debug_format_owned = true;
}
}
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')) {
copy_filename(FN_DEPENDFILE, q);
advance = true;
}
break;
case 'F':
copy_filename(FN_DEPENDFILE, q);
advance = true;
break;
case 'T':
copy_filename(FN_DEPEND_TARGET, q);
advance = true;
break;
case 'Q':
set_filename(FN_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)
erropt.debug_nasm = param ?
strtoul(param, NULL, 10) : erropt.debug_nasm+1;
break;
case OPT_INFO:
if (pass == 1)
erropt.verbose_info = param ?
strtoul(param, NULL, 10) : erropt.verbose_info+1;
break;
case OPT_REPRODUCIBLE:
reproducible = true;
break;
case OPT_DEBUG_PREFIX_MAP: {
struct debug_prefix_list *d;
char *c;
c = strchr(param, '=');
if (!c) {
nasm_error(ERR_NONFATAL | ERR_NOFILE | ERR_USAGE,
"option `--%s' must be of the form `BASE=DEST'", p);
break;
}
*c = '\0';
d = nasm_malloc(sizeof(*d));
d->next = debug_prefixes;
d->base = nasm_strdup(param);
d->dest = nasm_strdup(c + 1);
debug_prefixes = d;
*c = '=';
}
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, but that
* would require making this a list, and probably would require
* some other more complicated changes.
*/
copy_filename(FN_INFILE, p);
}
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_fatalf(ERR_PERROR, "unable to open response file `%s'", respfile);
}
}
static void open_errfile(const char *errfile)
{
if (!errfile || !*errfile)
return;
if (errfile[0] == '-' && !errfile[1]) {
erropt.file = stdout;
return;
}
erropt.file = nasm_open_write(errfile, NF_TEXT);
if (!erropt.file) {
nasm_fatalf(ERR_PERROR, "cannot open file `%s' for error messages",
errfile);
}
}
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 (!get_filename(FN_INFILE))
nasm_fatalf(ERR_USAGE, "no input file specified");
set_filename(FN_MAPPED_INFILE, debug_prefix_remap(get_filename(FN_INFILE)));
check_overwrite_files();
open_errfile(get_filename(FN_ERRFILE));
}
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;
}
}
}
void print_final_report(bool failure)
{
/* This test is here to reduce the likelihood of a recursive failure */
if (unlikely(erropt.verbose_info >= 1)) {
enum pass_type t = pass_type();
if (t >= PASS_FIRST) {
unsigned int end_passes = t > PASS_OPT ? t - PASS_OPT : 0;
nasm_info(1, "assembly %s after 1+%"PRId64"+%u passes",
failure ? "failed" : "completed",
pass_count()-1-end_passes, end_passes);
}
}
}
static void assemble_file(const char *fname, char const* mapped_fname, struct strlist *depend_list)
{
static int64_t stall_count = 0; /* Make sure we make forward progress... */
char *line;
insn output_ins;
uint64_t prev_offset_changed;
reset_limits_before_pass();
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;
/* Remove the list file in case we die before the output pass */
if (!keep_all)
nasm_remove(get_filename(FN_LISTFILE));
while (!terminate_after_phase() && !pass_final()) {
const char *listname;
_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;
}
error_pass_start(pass_final());
global_offset_changed = 0;
/* Suppress ERR_PASS2 unless we are actually in the final pass */
erropt.never = 0;
if (!pass_final())
erropt.never |= ERR_PASS2;
reset_global_defaults(cmd_sb);
cpu = cmd_cpu;
listname = get_filename(FN_LISTFILE);
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)
nasm_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, mapped_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();
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;
}
}
error_pass_end();
}
print_final_report(terminate_after_phase());
lfmt->cleanup();
}
void close_output(bool error)
{
if (!ofile)
return;
if (ofile == stdout || ofile == stdin) {
fflush(ofile);
} else {
fclose(ofile);
if (error && !keep_all) {
nasm_remove(get_filename(FN_OUTFILE));
nasm_remove(get_filename(FN_MAPFILE));
}
}
ofile = NULL;
}
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 SEEx(x,y) (with == HW_OPT ? " (see -h " x ")" : (y))
#define SEE(x) SEEx(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(
" -L flags for general usage:\n"
" -Lb show builtin macro packages (standard and %use)\n"
" -Lc list the data contents of INCBIN files\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"
" -Lt list the full output from TIMES and ALIGN directives\n"
" -L+ equivalent to -LbdefFmps\n"
" -L++ equivalent to -L+ -Lct\n"
" -L flags intended for debugging NASM:\n"
" -Lw flush the output after every line (very slow!)\n"
" -LX list instruction pattern line numbers from insns.xda\n"
" -L! enable ALL listing options including debugging\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"
" --debug-prefix-map base=dest\n"
" remap paths starting with 'base' to 'dest' in output files\n"
, out);
}
if (help_is(with, HW_LIMIT)) {
fprintf(out, " --limit-* val set execution limit:\n");
for (i = 0; i < LIMIT_MAX; i++) {
fprintf(out,
" %-20s %s\n"
" %-20s [default %s",
limit_info[i].name, limit_info[i].help,
"", limit_str(limit_default(i)));
fprintf(out, ", max %s]\n",
limit_str(limit_max(i)));
}
} else if (help_optor(with, HW_LIMIT)) {
fprintf(out, " --limit-* val set execution limit%s\n",
SEE("--limit"));
}
#undef SEE
}