diff --git a/asm/error.c b/asm/error.c index 70ecfc4e..09c86129 100644 --- a/asm/error.c +++ b/asm/error.c @@ -1,6 +1,6 @@ /* ----------------------------------------------------------------------- * * - * Copyright 1996-2024 The NASM Authors - All Rights Reserved + * Copyright 1996-2025 The NASM Authors - All Rights Reserved * See the file AUTHORS included with the NASM distribution for * the specific copyright holders. * @@ -36,27 +36,34 @@ */ #include "compiler.h" - - #include "nasmlib.h" #include "error.h" +unsigned int debug_nasm; /* Debugging messages? */ +unsigned int opt_verbose_info; /* Informational messages? */ + /* Common function body */ #define nasm_do_error(_sev,_flags) \ - va_list ap; \ - va_start(ap, fmt); \ - if ((_sev) >= ERR_CRITICAL) \ - nasm_verror_critical((_sev)|(_flags), fmt, ap); \ - else \ - nasm_verror((_sev)|(_flags), fmt, ap); \ - va_end(ap); \ - if ((_sev) >= ERR_FATAL) \ - abort(); + do { \ + va_list ap; \ + va_start(ap, fmt); \ + if ((_sev) >= ERR_CRITICAL) \ + nasm_verror_critical((_sev)|(_flags), fmt, ap); \ + else \ + nasm_verror((_sev)|(_flags), fmt, ap); \ + va_end(ap); \ + if ((_sev) >= ERR_FATAL) \ + abort(); \ + } while (0) - -void nasm_error(errflags severity, const char *fmt, ...) +/* + * This is the generic function to use when the error type is not + * known a priori. For ERR_DEBUG and ERR_INFO the level can be + * included by + */ +void nasm_error(errflags flags, const char *fmt, ...) { - nasm_do_error(severity & ERR_MASK, severity & ~ERR_MASK); + nasm_do_error(flags & ERR_MASK, flags); } #define nasm_err_helpers(_type, _name, _sev) \ @@ -71,8 +78,6 @@ _type nasm_ ## _name (const char *fmt, ...) \ nasm_err_helpers(void, listmsg, ERR_LISTMSG) nasm_err_helpers(void, note, ERR_NOTE) -nasm_err_helpers(void, debug, ERR_DEBUG) -nasm_err_helpers(void, info, ERR_INFO) nasm_err_helpers(void, nonfatal, ERR_NONFATAL) nasm_err_helpers(fatal_func, fatal, ERR_FATAL) nasm_err_helpers(fatal_func, critical, ERR_CRITICAL) @@ -88,7 +93,22 @@ nasm_err_helpers(fatal_func, panic, ERR_PANIC) */ void nasm_warn_(errflags flags, const char *fmt, ...) { - nasm_do_error(ERR_WARNING, flags); + nasm_do_error(ERR_WARNING, flags); +} + +/* + * nasm_info() and nasm_debug() takes mandatory enabling levels. + */ +void nasm_info_(unsigned int level, const char *fmt, ...) +{ + if (info_level(level)) + nasm_do_error(ERR_INFO, LEVEL(level)); +} + +void nasm_debug_(unsigned int level, const char *fmt, ...) +{ + if (debug_level(level)) + nasm_do_error(ERR_DEBUG, LEVEL(level)); } fatal_func nasm_panic_from_macro(const char *func, const char *file, int line) @@ -297,3 +317,32 @@ bool set_warning_status(const char *value) return ok; } + +/* + * The various error type prefixes + */ +const char *error_pfx(errflags severity) +{ + switch (severity & ERR_MASK) { + case ERR_LISTMSG: + return ";;; "; + case ERR_NOTE: + return "note: "; + case ERR_DEBUG: + return "debug: "; + case ERR_INFO: + return "info: "; + case ERR_WARNING: + return "warning: "; + case ERR_NONFATAL: + return "error: "; + case ERR_FATAL: + return "fatal: "; + case ERR_CRITICAL: + return "critical: "; + case ERR_PANIC: + return "panic: "; + default: + return "internal error: "; + } +} diff --git a/asm/nasm.c b/asm/nasm.c index 5039dc33..718b9298 100644 --- a/asm/nasm.c +++ b/asm/nasm.c @@ -90,9 +90,7 @@ static const struct error_format *errfmt = &errfmt_gnu; static struct strlist *warn_list; static struct nasm_errhold *errhold_stack; -unsigned int debug_nasm; /* Debugging messages? */ - -static bool using_debug_info, opt_verbose_info; +static bool using_debug_info; static const char *debug_format; #ifndef ABORT_ON_PANIC @@ -122,7 +120,8 @@ const struct ofmt *ofmt = &OF_DEFAULT; const struct ofmt_alias *ofmt_alias = NULL; const struct dfmt *dfmt; -FILE *error_file; /* Where to write error messages */ +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; @@ -942,6 +941,7 @@ enum text_options { OPT_KEEP_ALL, OPT_NO_LINE, OPT_DEBUG, + OPT_INFO, OPT_REPRODUCIBLE }; enum need_arg { @@ -973,6 +973,8 @@ static const struct textargs textopts[] = { {"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}, {NULL, OPT_BOGUS, ARG_NO, 0} @@ -1050,7 +1052,7 @@ static bool process_arg(char *p, char *q, int pass) case 'v': case '+': param++; - opt_verbose_info = true; + opt_verbose_info++; break; case 'x': @@ -1341,8 +1343,14 @@ static bool process_arg(char *p, char *q, int pass) ppopt |= PP_NOLINE; break; case OPT_DEBUG: - debug_nasm = param ? - strtoul(param, NULL, 10) : debug_nasm+1; + 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; @@ -1647,6 +1655,11 @@ static void assemble_file(const char *fname, struct strlist *depend_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 = false; @@ -1767,34 +1780,15 @@ static void assemble_file(const char *fname, struct strlist *depend_list) reset_warnings(); } - if (opt_verbose_info && pass_final()) { + if (pass_final()) { /* -On and -Ov switches */ - nasm_info("assembly required 1+%"PRId64"+2 passes\n", pass_count()-3); + nasm_info(1, "assembly required 1+%"PRId64"+2 passes\n", pass_count()-3); } lfmt->cleanup(); strlist_free(&warn_list); } -/** - * get warning index; 0 if this is non-suppressible. - */ -static size_t pure_func warn_index(errflags severity) -{ - size_t index; - - if ((severity & ERR_MASK) >= ERR_FATAL) - return 0; /* Fatal errors are never suppressible */ - - /* Warnings MUST HAVE a warning category specifier! */ - nasm_assert((severity & (ERR_MASK|WARN_MASK)) != ERR_WARNING); - - index = WARN_IDX(severity); - nasm_assert(index < WARN_IDX_ALL); - - return index; -} - static bool skip_this_pass(errflags severity) { errflags type = severity & ERR_MASK; @@ -1830,21 +1824,42 @@ static bool skip_this_pass(errflags severity) * @param severity the severity of the warning or error * @return true if we should abort error/warning printing */ -static bool is_suppressed(errflags severity) +static bool is_suppressed(errflags flags) { - /* Fatal errors must never be suppressed */ - if ((severity & ERR_MASK) >= ERR_FATAL) + 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; + } - /* This error/warning is pointless if we are dead anyway */ - if ((severity & ERR_UNDEAD) && terminate_after_phase) + if (flags & errflags_never) return true; - if (!(warning_state[warn_index(severity)] & WARN_ST_ENABLED)) - return true; + switch (severity) { + case ERR_WARNING: + if (!(warning_state[level] & WARN_ST_ENABLED)) + return true; + break; - if (!(severity & ERR_PP_LISTMACRO)) - return pp_suppress_error(severity); + 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; } @@ -1866,7 +1881,7 @@ static errflags pure_func true_error_type(errflags severity) /* Promote warning to error? */ if (type == ERR_WARNING) { - uint8_t state = warning_state[warn_index(severity)]; + uint8_t state = warning_state[WARN_IDX(severity)]; if ((state & warn_is_err) == warn_is_err) type = ERR_NONFATAL; } @@ -1874,37 +1889,6 @@ static errflags pure_func true_error_type(errflags severity) return type; } -/* - * The various error type prefixes - */ -/* - * The various error type prefixes - */ -static inline const char *error_pfx(errflags severity) -{ - switch (severity & ERR_MASK) { - case ERR_LISTMSG: - return ";;; "; - case ERR_NOTE: - return "note: "; - case ERR_DEBUG: - return "debug: "; - case ERR_INFO: - return "info: "; - case ERR_WARNING: - return "warning: "; - case ERR_NONFATAL: - return "error: "; - case ERR_FATAL: - return "fatal: "; - case ERR_CRITICAL: - return "critical: "; - case ERR_PANIC: - return "panic: "; - default: - return "internal error: "; - } -} static const char no_file_name[] = "nasm"; /* What to print if no file name */ /* @@ -2084,8 +2068,10 @@ 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) + if (true_type >= ERR_CRITICAL) { nasm_verror_critical(severity, fmt, args); + abort(); + } if (is_suppressed(severity)) return; @@ -2133,14 +2119,25 @@ static void nasm_issue_error(struct nasm_errtext *et) pfx = error_pfx(true_type); *warnsuf = 0; - if ((severity & (ERR_MASK|ERR_HERE|ERR_PP_LISTMACRO)) == ERR_WARNING) { - /* - * It's a warning without ERR_HERE defined, and we are not already - * unwinding the macros that led us here. - */ - snprintf(warnsuf, sizeof warnsuf, " [-w+%s%s]", - (true_type >= ERR_NONFATAL) ? "error=" : "", - warning_name[warn_index(severity)]); + 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; @@ -2208,8 +2205,10 @@ static void nasm_issue_error(struct nasm_errtext *et) if (true_type >= ERR_FATAL) die_hard(true_type, severity); - else if (true_type >= ERR_NONFATAL) + else if (true_type >= ERR_NONFATAL) { terminate_after_phase = true; + errflags_never |= ERR_UNDEAD; + } done: nasm_free_error(et); @@ -2360,8 +2359,10 @@ static void help(FILE *out, const char *what) } if (help_opt(with)) { fputs( - " -s redirect error messages to stdout\n" - " -Zfile redirect error messages to file\n" + " -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')) { diff --git a/asm/preproc.c b/asm/preproc.c index 035db74e..0258a4ab 100644 --- a/asm/preproc.c +++ b/asm/preproc.c @@ -396,7 +396,7 @@ static MMacro *pop_mmacro(MMacro **mp, MMacro *next) nasm_assert(m->refcnt > 0); if (!--m->refcnt) { if (m->name) { - nasm_info("freeing macro `%s'", m->name); + nasm_debug(2, "freeing macro `%s'", m->name); check_mmacro_refcounts(); } nasm_assert(!m->next); diff --git a/disasm/ndisasm.c b/disasm/ndisasm.c index 1e538ca3..a690f451 100644 --- a/disasm/ndisasm.c +++ b/disasm/ndisasm.c @@ -82,6 +82,8 @@ fatal_func nasm_verror_critical(errflags severity, const char *fmt, va_list val) abort(); } +errflags errflags_never = 0; + int main(int argc, char **argv) { uint8_t buffer[INSN_MAX * 2], *p; diff --git a/include/error.h b/include/error.h index 6a62b2fe..76842359 100644 --- a/include/error.h +++ b/include/error.h @@ -40,11 +40,6 @@ #include "compiler.h" -/* - * File pointer for error messages - */ -extern FILE *error_file; /* Error file descriptor */ - /* * Typedef for the severity field */ @@ -56,10 +51,8 @@ typedef uint32_t errflags; void printf_func(2, 3) nasm_error(errflags severity, const char *fmt, ...); void printf_func(1, 2) nasm_listmsg(const char *fmt, ...); void printf_func(2, 3) nasm_listmsgf(errflags flags, const char *fmt, ...); -void printf_func(1, 2) nasm_debug(const char *fmt, ...); -void printf_func(2, 3) nasm_debugf(errflags flags, const char *fmt, ...); -void printf_func(1, 2) nasm_info(const char *fmt, ...); -void printf_func(2, 3) nasm_infof(errflags flags, const char *fmt, ...); +void printf_func(2, 3) nasm_debug_(unsigned int level, const char *fmt, ...); +void printf_func(2, 3) nasm_info_(unsigned int level, const char *fmt, ...); void printf_func(1, 2) nasm_note(const char *fmt, ...); void printf_func(2, 3) nasm_notef(errflags flags, const char *fmt, ...); void printf_func(2, 3) nasm_warn_(errflags flags, const char *fmt, ...); @@ -77,20 +70,44 @@ fatal_func nasm_panic_from_macro(const char *func, const char *file, int line); void vprintf_func(2) nasm_verror(errflags severity, const char *fmt, va_list ap); fatal_func vprintf_func(2) nasm_verror_critical(errflags severity, const char *fmt, va_list ap); +const char *error_pfx(errflags severity); + /* * These are the error severity codes which get passed as the first - * argument to an efunc. + * argument to an efunc. The order here matters! */ -#define ERR_LISTMSG 0x00000000 /* for the listing file only (no prefix) */ + +/* For the list file only */ +#define ERR_LISTMSG 0x00000000 /* for the listing file only (comment prefix) */ #define ERR_NOTE 0x00000001 /* for the listing file only (with prefix) */ -#define ERR_DEBUG 0x00000002 /* debugging message */ -#define ERR_INFO 0x00000003 /* information for the list file */ -#define ERR_WARNING 0x00000004 /* warn only: no further action */ -#define ERR_NONFATAL 0x00000008 /* terminate assembly after phase */ -#define ERR_FATAL 0x00000009 /* instantly fatal: exit with error */ + +/* Non-terminating diagnostics; can be suppressed */ +#define ERR_DEBUG 0x00000004 /* internal debugging message */ +#define ERR_INFO 0x00000005 /* informational message */ +#define ERR_WARNING 0x00000006 /* warning */ + +/* Errors which terminate assembly without output */ +#define ERR_NONFATAL 0x00000008 /* terminate assembly after the current pass */ + +/* + * From this point, errors cannot be suppressed, and the C compiler is + * told that the call to nasm_verror() is terminating, to remove the + * need to generate further code. + */ +#define ERR_FATAL 0x0000000c /* terminate immediately, but perform cleanup */ + +/* + * Abort conditions - terminate with minimal or no cleanup. + * + * ERR_CRITICAL is used for system errors like out of memory, where the normal + * error and cleanup paths may impede informing the user of the nature of the failure. + * + * ERR_PANIC is used exclusively to trigger on bugs in the NASM code itself. + */ #define ERR_CRITICAL 0x0000000e /* fatal, but minimize code before exit */ #define ERR_PANIC 0x0000000f /* internal error: panic instantly - * and dump core for reference */ + * and call abort() to dump core for reference */ + #define ERR_MASK 0x0000000f /* mask off the above codes */ #define ERR_UNDEAD 0x00000010 /* skip if we already have errors */ #define ERR_NOFILE 0x00000020 /* don't give source file name/line */ @@ -111,6 +128,11 @@ fatal_func vprintf_func(2) nasm_verror_critical(errflags severity, const char *f #define WARN_SHR 16 /* how far to shift right */ #define WARN_IDX(x) (((errflags)(x)) >> WARN_SHR) #define WARN_MASK ((~(errflags)0) << WARN_SHR) +#define WARNING(x) ((errflags)(x) << WARN_SHR) + +/* The same field is used for debug and info levels */ +#define LEVEL_SHR WARN_SHR +#define LEVEL(x) WARNING(x) /* This is a bitmask */ #define WARN_ST_ENABLED 1 /* Warning is currently enabled */ @@ -154,38 +176,88 @@ errflags nasm_error_hold_pop(errhold hold, bool issue); #include "warnings.h" /* True if a warning is enabled, either as a warning or an error */ +extern errflags errflags_never; static inline bool warn_active(errflags warn) { - enum warn_index wa = WARN_IDX(warn); - return unlikely(warning_state[wa] & WARN_ST_ENABLED); + if (warn & errflags_never) + return false; + + return !!(warning_state[WARN_IDX(warn)] & WARN_ST_ENABLED); } -#ifdef HAVE_VARIADIC_MACROS - -#define nasm_warn(w, ...) \ - do { \ - if (unlikely(warn_active(w))) \ - nasm_warn_(w, __VA_ARGS__); \ - } while (0) - -#else - -#define nasm_warn nasm_warn_ - -#endif - -/* By defining MAX_DEBUG, we can compile out messages entirely */ +/* + * By defining MAX_DEBUG or MAX_INFO, it is possible to + * compile out messages entirely. + */ #ifndef MAX_DEBUG -# define MAX_DEBUG (~0U) +# define MAX_DEBUG UINT_MAX +#endif +#ifndef MAX_INFO +# define MAX_INFO UINT_MAX #endif /* Debug level checks */ +extern unsigned int debug_nasm; static inline bool debug_level(unsigned int level) { - extern unsigned int debug_nasm; if (is_constant(level) && level > MAX_DEBUG) return false; return unlikely(level <= debug_nasm); } +/* Info level checks */ +extern unsigned int opt_verbose_info; +static inline bool info_level(unsigned int level) +{ + if (is_constant(level) && level > MAX_INFO) + return false; + return unlikely(level <= opt_verbose_info); +} + +#ifdef HAVE_VARIADIC_MACROS + +/* + * Marked unlikely() to avoid excessive speed penalties on disabled warnings; + * if the warning is issued then the performance penalty is substantial + * anyway. + */ +#define nasm_warn(w, ...) \ + do { \ + const errflags _w = (w); \ + if (unlikely(warn_active(_w))) { \ + nasm_warn_(_w, __VA_ARGS__); \ + } \ + } while (0) + +#define nasm_info(l, ...) \ + do { \ + const unsigned int _l = (l); \ + if (unlikely(info_level(_l))) \ + nasm_info_(_l, __VA_ARGS__); \ + } while (0) + +#define nasm_debug(l, ...) \ + do { \ + const unsigned int _l = (l); \ + if (unlikely(debug_level(_l))) \ + nasm_debug_(_l, __VA_ARGS__); \ + } while (0) + +#else + +#define nasm_warn nasm_warn_ +#if MAX_DEBUG +# define nasm_debug nasm_debug_ +#else +# define nasm_debug (void) +#endif +#if MAX_INFO +# define nasm_info nasm_info_ +#else +# define nasm_info (void) +#endif + +#endif + + #endif /* NASM_ERROR_H */ diff --git a/nasmlib/errfile.c b/nasmlib/errfile.c deleted file mode 100644 index d3f5e5dc..00000000 --- a/nasmlib/errfile.c +++ /dev/null @@ -1,3 +0,0 @@ -#include "compiler.h" - -FILE *error_file; diff --git a/output/outbin.c b/output/outbin.c index 21297791..781bc432 100644 --- a/output/outbin.c +++ b/output/outbin.c @@ -228,10 +228,10 @@ static void bin_cleanup(void) uint64_t pend; int h; - if (debug_level(1)) { - nasm_debug("bin_cleanup: Sections were initially referenced in this order:\n"); + if (debug_level(2)) { + nasm_debug(2, "bin_cleanup: Sections were initially referenced in this order:\n"); for (h = 0, s = sections; s; h++, s = s->next) - nasm_debug("%i. %s\n", h, s->name); + nasm_debug(2, "%i. %s\n", h, s->name); } /* Assembly has completed, so now we need to generate the output file. @@ -513,11 +513,11 @@ static void bin_cleanup(void) if (h) nasm_fatal("circular vfollows path detected"); - if (debug_level(1)) { - nasm_debug("bin_cleanup: Confirm final section order for output file:\n"); + if (debug_level(2)) { + nasm_debug(2, "bin_cleanup: Confirm final section order for output file:\n"); for (h = 0, s = sections; s && (s->flags & TYPE_PROGBITS); h++, s = s->next) - nasm_debug("%i. %s\n", h, s->name); + nasm_debug(2, "%i. %s\n", h, s->name); } /* Step 5: Apply relocations. */ diff --git a/output/outelf.c b/output/outelf.c index 33dfd37e..3ec5c70a 100644 --- a/output/outelf.c +++ b/output/outelf.c @@ -760,10 +760,8 @@ static void elf_deflabel(char *name, int32_t segment, int64_t offset, int bind, type; /* st_info components */ const struct elf_section *sec = NULL; - if (debug_level(2)) { - nasm_debug(" elf_deflabel: %s, seg=%"PRIx32", off=%"PRIx64", is_global=%d, %s\n", - name, segment, offset, is_global, special); - } + nasm_debug(2, " elf_deflabel: %s, seg=%"PRIx32", off=%"PRIx64", is_global=%d, %s\n", + name, segment, offset, is_global, special); if (name[0] == '.' && name[1] == '.' && name[2] != '@') { /* diff --git a/output/outobj.c b/output/outobj.c index bfd216bd..491261a5 100644 --- a/output/outobj.c +++ b/output/outobj.c @@ -781,9 +781,8 @@ static void obj_deflabel(char *name, int32_t segment, int i; bool used_special = false; /* have we used the special text? */ - if (debug_level(2)) - nasm_debug(" obj_deflabel: %s, seg=%"PRIx32", off=%"PRIx64", is_global=%d, %s\n", - name, segment, offset, is_global, special); + nasm_debug(2, " obj_deflabel: %s, seg=%"PRIx32", off=%"PRIx64", is_global=%d, %s\n", + name, segment, offset, is_global, special); /* * If it's a special-retry from pass two, discard it. @@ -1337,8 +1336,7 @@ static int32_t obj_segment(char *name, int *bits) * using the pointer it gets passed. That way we save memory, * by sponging off the label manager. */ - if (debug_level(3)) - nasm_debug(" obj_segment: < %s >, *bits=%d\n", name, *bits); + nasm_debug(3, " obj_segment: < %s >, *bits=%d\n", name, *bits); if (!name) { *bits = 16;