0
0
mirror of https://github.com/netwide-assembler/nasm.git synced 2025-09-22 10:43:39 -04:00

Sanitize the handling of messsages; improve info and debug

Make the handling of messages saner. In particular, regularize the
handling of info and debug messages, so that nasm_info() and
nasm_debug() actually become useful.

Signed-off-by: H. Peter Anvin (Intel) <hpa@zytor.com>
This commit is contained in:
H. Peter Anvin
2025-09-05 12:51:29 -07:00
parent dfe7d54901
commit ac93d75da3
9 changed files with 271 additions and 154 deletions

View File

@@ -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,13 +36,15 @@
*/
#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) \
do { \
va_list ap; \
va_start(ap, fmt); \
if ((_sev) >= ERR_CRITICAL) \
@@ -51,12 +53,17 @@
nasm_verror((_sev)|(_flags), fmt, ap); \
va_end(ap); \
if ((_sev) >= ERR_FATAL) \
abort();
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)
@@ -91,6 +96,21 @@ void nasm_warn_(errflags flags, const char *fmt, ...)
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)
{
if (!func)
@@ -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: ";
}
}

View File

@@ -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,9 +1343,15 @@ static bool process_arg(char *p, char *q, int pass)
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;
@@ -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))
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.
*/
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[warn_index(severity)]);
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')) {

View File

@@ -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);

View File

@@ -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;

View File

@@ -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 */

View File

@@ -1,3 +0,0 @@
#include "compiler.h"
FILE *error_file;

View File

@@ -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. */

View File

@@ -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",
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] != '@') {
/*

View File

@@ -781,8 +781,7 @@ 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",
nasm_debug(2, " obj_deflabel: %s, seg=%"PRIx32", off=%"PRIx64", is_global=%d, %s\n",
name, segment, offset, is_global, special);
/*
@@ -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;