From f4f7d18c06449a1859a62a10e7e24ca644032dff Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin (Intel)" Date: Fri, 7 Nov 2025 14:32:16 -0800 Subject: [PATCH] error: factor out error functions into separate files Tidy up a *lot* of code by moving error functions into separate source files. This required breaking out some of the assembler-only files into a separate library, as it conflicts with stubs in the disassembler. Signed-off-by: H. Peter Anvin (Intel) --- Makefile.in | 132 ++++----- Mkfiles/msvc.mak | 134 ++++----- Mkfiles/openwcom.mak | 136 ++++----- asm/error.c | 646 ++++++++++++++++++++++++++++++++++++------- asm/nasm.c | 628 +++++------------------------------------ asm/warnings.pl | 7 +- common/common.c | 9 + common/errstubs.c | 109 ++++++++ disasm/disasm.h | 4 + disasm/diserror.c | 46 +++ disasm/ndisasm.c | 44 +-- include/compiler.h | 3 + include/error.h | 34 ++- include/nasm.h | 1 + nasmlib/readnum.c | 9 +- 15 files changed, 1024 insertions(+), 918 deletions(-) create mode 100644 common/errstubs.c create mode 100644 disasm/diserror.c diff --git a/Makefile.in b/Makefile.in index eb2adcd7..d3bae08c 100644 --- a/Makefile.in +++ b/Makefile.in @@ -135,54 +135,22 @@ NDISASM = disasm/ndisasm.$(O) PROGOBJ = $(NASM) $(NDISASM) PROGS = nasm$(X) ndisasm$(X) -# Files dependent on warnings.dat -WARNOBJ = asm/warnings.$(O) -WARNFILES = asm/warnings_c.h include/warnings.h doc/warnings.src +# Objects for the local copy of zlib. The variable ZLIB is set to +# $(ZLIBOBJ) if the internal version of zlib should be used. +ZLIBOBJ = \ + zlib/adler32.$(O) \ + zlib/crc32.$(O) \ + zlib/infback.$(O) \ + zlib/inffast.$(O) \ + zlib/inflate.$(O) \ + zlib/inftrees.$(O) \ + zlib/zutil.$(O) -OUTPUTOBJ = \ - output/outform.$(O) output/outlib.$(O) \ - output/nulldbg.$(O) output/nullout.$(O) \ - output/outbin.$(O) output/outaout.$(O) output/outcoff.$(O) \ - output/outelf.$(O) \ - output/outobj.$(O) output/outas86.$(O) \ - output/outdbg.$(O) output/outieee.$(O) output/outmacho.$(O) \ - output/codeview.$(O) - -# The source files for these objects are scanned for warnings -LIBOBJ_W = \ - nasmlib/readnum.$(O) \ - \ - asm/error.$(O) \ - asm/floats.$(O) \ - asm/directiv.$(O) \ - asm/pragma.$(O) \ - asm/assemble.$(O) asm/labels.$(O) asm/parser.$(O) \ - asm/preproc.$(O) asm/quote.$(O) \ - asm/listing.$(O) asm/eval.$(O) asm/exprlib.$(O) asm/exprdump.$(O) \ - asm/stdscan.$(O) \ - asm/getbool.$(O) \ - asm/strfunc.$(O) \ - asm/segalloc.$(O) \ - asm/rdstrnum.$(O) \ - asm/srcfile.$(O) \ - \ - $(OUTPUTOBJ) - -# The source files for these objects are NOT scanned for warnings; -# normally this will include all generated files. -# It is entirely possible that it may be necessary to move some of these -# files to LIBOBJ_W, notably $(OUTPUTOBJ) -LIBOBJ_NW = \ +# Common library objects +LIBOBJ_COM = \ stdlib/snprintf.$(O) stdlib/vsnprintf.$(O) stdlib/strlcpy.$(O) \ stdlib/strnlen.$(O) stdlib/strrchrnul.$(O) \ \ - asm/directbl.$(O) \ - asm/pptok.$(O) \ - asm/tokhash.$(O) \ - asm/uncompress.$(O) \ - \ - macros/macros.$(O) \ - \ nasmlib/ver.$(O) \ nasmlib/alloc.$(O) nasmlib/asprintf.$(O) \ nasmlib/crc32b.$(O) nasmlib/crc64.$(O) nasmlib/md5c.$(O) \ @@ -196,35 +164,63 @@ LIBOBJ_NW = \ nasmlib/raa.$(O) nasmlib/saa.$(O) \ nasmlib/strlist.$(O) \ nasmlib/perfhash.$(O) nasmlib/badenum.$(O) \ + nasmlib/readnum.$(O) \ \ - common/common.$(O) \ + common/common.$(O) common/errstubs.$(O) \ \ x86/insnsa.$(O) x86/insnsb.$(O) x86/insnsn.$(O) \ x86/regs.$(O) x86/regvals.$(O) x86/regflags.$(O) \ x86/iflag.$(O) \ \ - $(WARNOBJ) + $(ZLIB) + +# Files dependent on warnings.dat +WARNOBJ = asm/warnings.$(O) +WARNFILES = asm/warnings_c.h include/warnings.h doc/warnings.src + +OUTPUTOBJ = \ + output/outform.$(O) output/outlib.$(O) \ + output/nulldbg.$(O) output/nullout.$(O) \ + output/outbin.$(O) output/outaout.$(O) output/outcoff.$(O) \ + output/outelf.$(O) \ + output/outobj.$(O) output/outas86.$(O) \ + output/outdbg.$(O) output/outieee.$(O) output/outmacho.$(O) \ + output/codeview.$(O) + +# Assembler-only library objects +LIBOBJ_ASM = \ + asm/error.$(O) \ + asm/floats.$(O) \ + asm/directiv.$(O) \ + asm/pragma.$(O) \ + asm/assemble.$(O) asm/labels.$(O) asm/parser.$(O) \ + asm/preproc.$(O) asm/quote.$(O) \ + asm/listing.$(O) asm/eval.$(O) asm/exprlib.$(O) asm/exprdump.$(O) \ + asm/stdscan.$(O) \ + asm/getbool.$(O) \ + asm/strfunc.$(O) \ + asm/segalloc.$(O) \ + asm/rdstrnum.$(O) \ + asm/srcfile.$(O) \ + asm/directbl.$(O) \ + asm/pptok.$(O) \ + asm/tokhash.$(O) \ + asm/uncompress.$(O) \ + \ + macros/macros.$(O) \ + \ + $(WARNOBJ) \ + $(OUTPUTOBJ) # Objects which are only used for the disassembler LIBOBJ_DIS = \ disasm/disasm.$(O) disasm/sync.$(O) disasm/prefix.$(O) \ + disasm/diserror.$(O) \ \ x86/insnsd.$(O) x86/regdis.$(O) -# Objects for the local copy of zlib. The variable ZLIB is set to -# $(ZLIBOBJ) if the internal version of zlib should be used. -ZLIBOBJ = \ - zlib/adler32.$(O) \ - zlib/crc32.$(O) \ - zlib/infback.$(O) \ - zlib/inffast.$(O) \ - zlib/inflate.$(O) \ - zlib/inftrees.$(O) \ - zlib/zutil.$(O) - -LIBOBJ = $(LIBOBJ_W) $(LIBOBJ_NW) $(ZLIB) -ALLOBJ_W = $(NASM) $(LIBOBJ_W) -ALLOBJ = $(PROGOBJ) $(LIBOBJ) $(LIBOBJ_DIS) +LIBOBJ = $(LIBOBJ_COM) $(LIBOBJ_ASM) $(LIBOBJ_DIS) +ALLOBJ = $(PROGOBJ) $(LIBOBJ) SUBDIRS = stdlib nasmlib include config output asm disasm x86 \ common zlib macros misc XSUBDIRS = nsis win test doc editors @@ -241,21 +237,27 @@ $(DIRS): @$(MKDIR_P) $(SUBDIRS) $(XSUBDIRS) NASMLIB = libnasm.$(A) -DISLIB = libndis.$(A) +ASMLIB = libasm.$(A) +DISLIB = libdis.$(A) -$(NASMLIB): $(LIBOBJ) +$(NASMLIB): $(LIBOBJ_COM) $(RM_F) $(NASMLIB) - $(AR) cq $(NASMLIB) $(LIBOBJ) + $(AR) cq $(NASMLIB) $(LIBOBJ_COM) $(RANLIB) $(NASMLIB) +$(ASMLIB): $(LIBOBJ_ASM) + $(RM_F) $(ASMLIB) + $(AR) cq $(ASMLIB) $(LIBOBJ_ASM) + $(RANLIB) $(ASMLIB) + $(DISLIB): $(LIBOBJ_DIS) $(RM_F) $(DISLIB) $(AR) cq $(DISLIB) $(LIBOBJ_DIS) $(RANLIB) $(DISLIB) -nasm$(X): $(NASM) $(MANIFEST) $(NASMLIB) +nasm$(X): $(NASM) $(MANIFEST) $(ASMLIB) $(NASMLIB) $(CC) $(ALL_LDFLAGS) -o $@ $(NASM) $(MANIFEST) \ - $(NASMLIB) $(LIBS) + $(ASMLIB) $(NASMLIB) $(LIBS) ndisasm$(X): $(NDISASM) $(MANIFEST) $(DISLIB) $(NASMLIB) $(CC) $(ALL_LDFLAGS) -o $@ $(NDISASM) $(MANIFEST) \ diff --git a/Mkfiles/msvc.mak b/Mkfiles/msvc.mak index a1e4404b..67ff5bbf 100644 --- a/Mkfiles/msvc.mak +++ b/Mkfiles/msvc.mak @@ -86,54 +86,22 @@ NDISASM = disasm\ndisasm.obj PROGOBJ = $(NASM) $(NDISASM) PROGS = nasm$(X) ndisasm$(X) -# Files dependent on warnings.dat -WARNOBJ = asm\warnings.obj -WARNFILES = asm\warnings_c.h include\warnings.h doc\warnings.src +# Objects for the local copy of zlib. The variable ZLIB is set to +# $(ZLIBOBJ) if the internal version of zlib should be used. +ZLIBOBJ = \ + zlib\adler32.obj \ + zlib\crc32.obj \ + zlib\infback.obj \ + zlib\inffast.obj \ + zlib\inflate.obj \ + zlib\inftrees.obj \ + zlib\zutil.obj -OUTPUTOBJ = \ - output\outform.obj output\outlib.obj \ - output\nulldbg.obj output\nullout.obj \ - output\outbin.obj output\outaout.obj output\outcoff.obj \ - output\outelf.obj \ - output\outobj.obj output\outas86.obj \ - output\outdbg.obj output\outieee.obj output\outmacho.obj \ - output\codeview.obj - -# The source files for these objects are scanned for warnings -LIBOBJ_W = \ - nasmlib\readnum.obj \ - \ - asm\error.obj \ - asm\floats.obj \ - asm\directiv.obj \ - asm\pragma.obj \ - asm\assemble.obj asm\labels.obj asm\parser.obj \ - asm\preproc.obj asm\quote.obj \ - asm\listing.obj asm\eval.obj asm\exprlib.obj asm\exprdump.obj \ - asm\stdscan.obj \ - asm\getbool.obj \ - asm\strfunc.obj \ - asm\segalloc.obj \ - asm\rdstrnum.obj \ - asm\srcfile.obj \ - \ - $(OUTPUTOBJ) - -# The source files for these objects are NOT scanned for warnings; -# normally this will include all generated files. -# It is entirely possible that it may be necessary to move some of these -# files to LIBOBJ_W, notably $(OUTPUTOBJ) -LIBOBJ_NW = \ +# Common library objects +LIBOBJ_COM = \ stdlib\snprintf.obj stdlib\vsnprintf.obj stdlib\strlcpy.obj \ stdlib\strnlen.obj stdlib\strrchrnul.obj \ \ - asm\directbl.obj \ - asm\pptok.obj \ - asm\tokhash.obj \ - asm\uncompress.obj \ - \ - macros\macros.obj \ - \ nasmlib\ver.obj \ nasmlib\alloc.obj nasmlib\asprintf.obj \ nasmlib\crc32b.obj nasmlib\crc64.obj nasmlib\md5c.obj \ @@ -147,35 +115,63 @@ LIBOBJ_NW = \ nasmlib\raa.obj nasmlib\saa.obj \ nasmlib\strlist.obj \ nasmlib\perfhash.obj nasmlib\badenum.obj \ + nasmlib\readnum.obj \ \ - common\common.obj \ + common\common.obj common\errstubs.obj \ \ x86\insnsa.obj x86\insnsb.obj x86\insnsn.obj \ x86\regs.obj x86\regvals.obj x86\regflags.obj \ x86\iflag.obj \ \ - $(WARNOBJ) + $(ZLIB) + +# Files dependent on warnings.dat +WARNOBJ = asm\warnings.obj +WARNFILES = asm\warnings_c.h include\warnings.h doc\warnings.src + +OUTPUTOBJ = \ + output\outform.obj output\outlib.obj \ + output\nulldbg.obj output\nullout.obj \ + output\outbin.obj output\outaout.obj output\outcoff.obj \ + output\outelf.obj \ + output\outobj.obj output\outas86.obj \ + output\outdbg.obj output\outieee.obj output\outmacho.obj \ + output\codeview.obj + +# Assembler-only library objects +LIBOBJ_ASM = \ + asm\error.obj \ + asm\floats.obj \ + asm\directiv.obj \ + asm\pragma.obj \ + asm\assemble.obj asm\labels.obj asm\parser.obj \ + asm\preproc.obj asm\quote.obj \ + asm\listing.obj asm\eval.obj asm\exprlib.obj asm\exprdump.obj \ + asm\stdscan.obj \ + asm\getbool.obj \ + asm\strfunc.obj \ + asm\segalloc.obj \ + asm\rdstrnum.obj \ + asm\srcfile.obj \ + asm\directbl.obj \ + asm\pptok.obj \ + asm\tokhash.obj \ + asm\uncompress.obj \ + \ + macros\macros.obj \ + \ + $(WARNOBJ) \ + $(OUTPUTOBJ) # Objects which are only used for the disassembler LIBOBJ_DIS = \ disasm\disasm.obj disasm\sync.obj disasm\prefix.obj \ + disasm\diserror.obj \ \ x86\insnsd.obj x86\regdis.obj -# Objects for the local copy of zlib. The variable ZLIB is set to -# $(ZLIBOBJ) if the internal version of zlib should be used. -ZLIBOBJ = \ - zlib\adler32.obj \ - zlib\crc32.obj \ - zlib\infback.obj \ - zlib\inffast.obj \ - zlib\inflate.obj \ - zlib\inftrees.obj \ - zlib\zutil.obj - -LIBOBJ = $(LIBOBJ_W) $(LIBOBJ_NW) $(ZLIB) -ALLOBJ_W = $(NASM) $(LIBOBJ_W) -ALLOBJ = $(PROGOBJ) $(LIBOBJ) $(LIBOBJ_DIS) +LIBOBJ = $(LIBOBJ_COM) $(LIBOBJ_ASM) $(LIBOBJ_DIS) +ALLOBJ = $(PROGOBJ) $(LIBOBJ) SUBDIRS = stdlib nasmlib include config output asm disasm x86 \ common zlib macros misc XSUBDIRS = nsis win test doc editors @@ -186,22 +182,26 @@ EDITORS = editors\nasmtok.el editors\nasmtok.json #-- End File Lists --# NASMLIB = libnasm.$(A) -NDISLIB = libndis.$(A) +ASMLIB = libasm.$(A) +DISLIB = libdis.$(A) all: nasm$(X) ndisasm$(X) -nasm$(X): $(NASM) $(MANIFEST) $(NASMLIB) - $(CC) /Fe:$@ $(ALL_CFLAGS) $(NASM) $(NASMLIB) $(LIBS) \ +nasm$(X): $(NASM) $(MANIFEST) $(ASMLIB) $(NASMLIB) + $(CC) /Fe:$@ $(ALL_CFLAGS) $(NASM) $(ASMLIB) $(NASMLIB) $(LIBS) \ $(ALL_LDFLAGS) -ndisasm$(X): $(NDISASM) $(MANIFEST) $(NDISLIB) $(NASMLIB) - $(CC) /Fe:$@ $(ALL_CFLAGS) $(NDISASM) $(NDISLIB) $(NASMLIB) $(LIBS) \ +ndisasm$(X): $(NDISASM) $(MANIFEST) $(DISLIB) $(NASMLIB) + $(CC) /Fe:$@ $(ALL_CFLAGS) $(NDISASM) $(DISLIB) $(NASMLIB) $(LIBS) \ $(ALL_LDFLAGS) -$(NASMLIB): $(LIBOBJ) +$(NASMLIB): $(LIBOBJ_COM) $(AR) $(ARFLAGS) /out:$@ $** -$(NDISLIB): $(LIBOBJ_DIS) +$(ASMLIB): $(LIBOBJ_ASM) + $(AR) $(ARFLAGS) /out:$@ $** + +$(DISLIB): $(LIBOBJ_DIS) $(AR) $(ARFLAGS) /out:$@ $** # These are specific to certain Makefile syntaxes... diff --git a/Mkfiles/openwcom.mak b/Mkfiles/openwcom.mak index d752e24c..eabbb7dc 100644 --- a/Mkfiles/openwcom.mak +++ b/Mkfiles/openwcom.mak @@ -72,54 +72,22 @@ NDISASM = disasm\ndisasm.obj PROGOBJ = $(NASM) $(NDISASM) PROGS = nasm$(X) ndisasm$(X) -# Files dependent on warnings.dat -WARNOBJ = asm\warnings.obj -WARNFILES = asm\warnings_c.h include\warnings.h doc\warnings.src +# Objects for the local copy of zlib. The variable ZLIB is set to +# $(ZLIBOBJ) if the internal version of zlib should be used. +ZLIBOBJ = & + zlib\adler32.obj & + zlib\crc32.obj & + zlib\infback.obj & + zlib\inffast.obj & + zlib\inflate.obj & + zlib\inftrees.obj & + zlib\zutil.obj -OUTPUTOBJ = & - output\outform.obj output\outlib.obj & - output\nulldbg.obj output\nullout.obj & - output\outbin.obj output\outaout.obj output\outcoff.obj & - output\outelf.obj & - output\outobj.obj output\outas86.obj & - output\outdbg.obj output\outieee.obj output\outmacho.obj & - output\codeview.obj - -# The source files for these objects are scanned for warnings -LIBOBJ_W = & - nasmlib\readnum.obj & - & - asm\error.obj & - asm\floats.obj & - asm\directiv.obj & - asm\pragma.obj & - asm\assemble.obj asm\labels.obj asm\parser.obj & - asm\preproc.obj asm\quote.obj & - asm\listing.obj asm\eval.obj asm\exprlib.obj asm\exprdump.obj & - asm\stdscan.obj & - asm\getbool.obj & - asm\strfunc.obj & - asm\segalloc.obj & - asm\rdstrnum.obj & - asm\srcfile.obj & - & - $(OUTPUTOBJ) - -# The source files for these objects are NOT scanned for warnings; -# normally this will include all generated files. -# It is entirely possible that it may be necessary to move some of these -# files to LIBOBJ_W, notably $(OUTPUTOBJ) -LIBOBJ_NW = & +# Common library objects +LIBOBJ_COM = & stdlib\snprintf.obj stdlib\vsnprintf.obj stdlib\strlcpy.obj & stdlib\strnlen.obj stdlib\strrchrnul.obj & & - asm\directbl.obj & - asm\pptok.obj & - asm\tokhash.obj & - asm\uncompress.obj & - & - macros\macros.obj & - & nasmlib\ver.obj & nasmlib\alloc.obj nasmlib\asprintf.obj & nasmlib\crc32b.obj nasmlib\crc64.obj nasmlib\md5c.obj & @@ -133,35 +101,63 @@ LIBOBJ_NW = & nasmlib\raa.obj nasmlib\saa.obj & nasmlib\strlist.obj & nasmlib\perfhash.obj nasmlib\badenum.obj & + nasmlib\readnum.obj & & - common\common.obj & + common\common.obj common\errstubs.obj & & x86\insnsa.obj x86\insnsb.obj x86\insnsn.obj & x86\regs.obj x86\regvals.obj x86\regflags.obj & x86\iflag.obj & & - $(WARNOBJ) + $(ZLIB) + +# Files dependent on warnings.dat +WARNOBJ = asm\warnings.obj +WARNFILES = asm\warnings_c.h include\warnings.h doc\warnings.src + +OUTPUTOBJ = & + output\outform.obj output\outlib.obj & + output\nulldbg.obj output\nullout.obj & + output\outbin.obj output\outaout.obj output\outcoff.obj & + output\outelf.obj & + output\outobj.obj output\outas86.obj & + output\outdbg.obj output\outieee.obj output\outmacho.obj & + output\codeview.obj + +# Assembler-only library objects +LIBOBJ_ASM = & + asm\error.obj & + asm\floats.obj & + asm\directiv.obj & + asm\pragma.obj & + asm\assemble.obj asm\labels.obj asm\parser.obj & + asm\preproc.obj asm\quote.obj & + asm\listing.obj asm\eval.obj asm\exprlib.obj asm\exprdump.obj & + asm\stdscan.obj & + asm\getbool.obj & + asm\strfunc.obj & + asm\segalloc.obj & + asm\rdstrnum.obj & + asm\srcfile.obj & + asm\directbl.obj & + asm\pptok.obj & + asm\tokhash.obj & + asm\uncompress.obj & + & + macros\macros.obj & + & + $(WARNOBJ) & + $(OUTPUTOBJ) # Objects which are only used for the disassembler LIBOBJ_DIS = & disasm\disasm.obj disasm\sync.obj disasm\prefix.obj & + disasm\diserror.obj & & x86\insnsd.obj x86\regdis.obj -# Objects for the local copy of zlib. The variable ZLIB is set to -# $(ZLIBOBJ) if the internal version of zlib should be used. -ZLIBOBJ = & - zlib\adler32.obj & - zlib\crc32.obj & - zlib\infback.obj & - zlib\inffast.obj & - zlib\inflate.obj & - zlib\inftrees.obj & - zlib\zutil.obj - -LIBOBJ = $(LIBOBJ_W) $(LIBOBJ_NW) $(ZLIB) -ALLOBJ_W = $(NASM) $(LIBOBJ_W) -ALLOBJ = $(PROGOBJ) $(LIBOBJ) $(LIBOBJ_DIS) +LIBOBJ = $(LIBOBJ_COM) $(LIBOBJ_ASM) $(LIBOBJ_DIS) +ALLOBJ = $(PROGOBJ) $(LIBOBJ) SUBDIRS = stdlib nasmlib include config output asm disasm x86 & common zlib macros misc XSUBDIRS = nsis win test doc editors @@ -198,18 +194,22 @@ all: perlreq nasm$(X) ndisasm$(X) .SYMBOLIC # cd rdoff && $(MAKE) all NASMLIB = nasm.lib -NDISLIB = ndisasm.lib +ASMLIB = asm.lib +DISLIB = dis.lib -nasm$(X): $(NASM) $(NASMLIB) - $(LD) $(LDFLAGS) name nasm$(X) libr {$(NASMLIB) $(LIBS)} file {$(NASM)} +nasm$(X): $(NASM) $(ASMLIB) $(NASMLIB) + $(LD) $(LDFLAGS) name nasm$(X) libr {$(ASMLIB) $(NASMLIB) $(LIBS)} file {$(NASM)} -ndisasm$(X): $(NDISASM) $(NDISLIB) $(NASMLIB) - $(LD) $(LDFLAGS) name ndisasm$(X) libr {$(NDISLIB) $(NASMLIB) $(LIBS)} file {$(NDISASM)} +ndisasm$(X): $(NDISASM) $(DISLIB) $(NASMLIB) + $(LD) $(LDFLAGS) name ndisasm$(X) libr {$(DISLIB) $(NASMLIB) $(LIBS)} file {$(NDISASM)} -nasm.lib: $(LIBOBJ) - wlib -q -b -n $@ $(LIBOBJ) +nasm.lib: $(LIBOBJ_COM) + wlib -q -b -n $@ $(LIBOBJ_COM) -ndisasm.lib: $(LIBOBJ_DIS) +asm.lib: $(LIBOBJ_ASM) + wlib -q -b -n $@ $(LIBOBJ_ASM) + +dis.lib: $(LIBOBJ_DIS) wlib -q -b -n $@ $(LIBOBJ_DIS) # These are specific to certain Makefile syntaxes (what are they diff --git a/asm/error.c b/asm/error.c index 332f9254..a43f0696 100644 --- a/asm/error.c +++ b/asm/error.c @@ -8,108 +8,27 @@ #include "compiler.h" #include "nasmlib.h" #include "error.h" +#include "listing.h" +#include "srcfile.h" +#include "strlist.h" -unsigned int debug_nasm; /* Debugging messages? */ -unsigned int opt_verbose_info; /* Informational messages? */ +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 */ +}; -/* Common function body */ -#define nasm_do_error(_sev,_flags) \ - do { \ - const errflags nde_severity = (_sev); \ - const errflags nde_flags = nde_severity | (_flags); \ - va_list ap; \ - va_start(ap, fmt); \ - if (nde_severity >= ERR_CRITICAL) { \ - nasm_verror_critical(nde_flags, fmt, ap); \ - unreachable(); \ - } else { \ - nasm_verror(nde_flags, fmt, ap); \ - if (nde_severity >= ERR_FATAL) \ - unreachable(); \ - } \ - va_end(ap); \ - } while (0) - -/* - * 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(flags & ERR_MASK, flags); -} - -#define nasm_err_helpers(_type, _name, _sev) \ -_type nasm_ ## _name ## f (errflags flags, const char *fmt, ...) \ -{ \ - nasm_do_error(_sev, flags); \ -} \ -_type nasm_ ## _name (const char *fmt, ...) \ -{ \ - nasm_do_error(_sev, 0); \ -} - -nasm_err_helpers(void, listmsg, ERR_LISTMSG) -nasm_err_helpers(void, note, ERR_NOTE) -nasm_err_helpers(void, nonfatal, ERR_NONFATAL) -nasm_err_helpers(fatal_func, fatal, ERR_FATAL) -nasm_err_helpers(fatal_func, critical, ERR_CRITICAL) -nasm_err_helpers(fatal_func, panic, ERR_PANIC) - -/* - * Strongly discourage warnings without level by require flags on warnings. - * This means nasm_warn() is the equivalent of the -f variants of the - * other ones. - * - * This is wrapped in a macro to be able to elide it if the warning is - * disabled, hence the extra underscore. - */ -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)); -} - -/* - * Convenience function for nasm_nonfatal(ERR_HOLD, ...) - */ -void nasm_holderr(const char *fmt, ...) -{ - nasm_do_error(ERR_NONFATAL, ERR_NONFATAL|ERR_HOLD); -} - -fatal_func nasm_panic_from_macro(const char *func, const char *file, int line) -{ - if (!func) - func = ""; - - nasm_panic("internal error in %s at %s:%d\n", func, file, line); -} - -fatal_func nasm_assert_failed(const char *msg, const char *func, - const char *file, int line) -{ - if (!func) - func = ""; - - nasm_panic("assertion %s failed in %s at %s:%d", msg, func, file, line); -} +enum error_formats { + ERRFMT_GNU, + ERRFMT_MSVC +}; +static const struct error_format errfmts[] = { + { ":", "", ": " }, /* ERRFMT_GNU */ + { "(", ")", " : " } /* ERRFMT_MSVC */ +}; +static const struct error_format *errfmt = &errfmts[ERRFMT_GNU]; +static void usage(void); /* * Warning stack management. Note that there is an implicit "push" @@ -121,6 +40,7 @@ struct warning_stack { uint8_t state[sizeof warning_state]; }; static struct warning_stack *warning_stack, *warning_state_init; +static struct strlist *warn_list; /* Push the warning status onto the warning stack */ void push_warnings(void) @@ -147,16 +67,33 @@ void pop_warnings(void) } } -/* Call after the command line is parsed, but before the first pass */ -void init_warnings(void) +/* Called after the command line is parsed, but before the first pass */ +static void init_warnings(void) { push_warnings(); warning_state_init = warning_stack; } +void error_init(void) +{ + erropt.worst = 0; + init_warnings(); +} -/* Call after each pass */ -void reset_warnings(void) +/* Called before each pass. Buffer warnings if "final" is false. */ +void error_pass_start(bool final) +{ + nasm_assert(!warn_list); + + erropt.worst = 0; + if (!final) + warn_list = strlist_alloc(false); +} + +/* + * Called after the completion of each pass. This MUST preserve erropt.worst! + */ +static void reset_warnings(void) { struct warning_stack *ws = warning_stack; @@ -170,6 +107,12 @@ void reset_warnings(void) memcpy(warning_state, ws->state, sizeof warning_state); } +void error_pass_end(void) +{ + strlist_free(&warn_list); + reset_warnings(); +} + /* * This is called when processing a -w or -W option, or a warning directive. * Returns ok if the action was successful. @@ -311,3 +254,500 @@ const char *error_pfx(errflags severity) return "internal error: "; } } + +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 & erropt.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; + + if (type == ERR_WARNING) { + /* Promote warning to error? */ + 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. + * + * For FATAL errors doing cleanups, tidying up the list process, + * and so in is acceptable. + * + * For CRITICAL errors, minimize dependencies on memory allocation + * and/or having a system valid state. + * + * For PANIC, if abort_on_panic is set, abort without any other action. + */ +static_fatal_func die_hard(errflags true_type, errflags severity) +{ + if (true_type < ERR_PANIC || !erropt.abort_on_panic) { + if (true_type < ERR_CRITICAL) { + /* FATAL shutdown, general cleanup actions are valid */ + print_final_report(true); + lfmt->cleanup(); + } + + fflush(NULL); + + close_output(true); + + if (severity & ERR_USAGE) + usage(); + + /* Terminate immediately (exit closes any still open files) */ + exit(true_type - ERR_FATAL + 1); + } + + /* + * abort() shouldn't ever return, but be paranoid about this, + * plus it helps some compilers clue in to the fact that this + * function can never, ever return. + */ + while (1) + abort(); +} + +/* + * 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; + + while (unlikely(been_here)) + abort(); /* Recursive critical error... just die */ + + been_here = true; + + erropt.worst = true_type; + + where = error_where(severity); + if (!where.filename) + where.filename = no_file_name; + + fputs(error_pfx(severity), erropt.file); + fputs(where.filename, erropt.file); + if (where.lineno) { + fprintf(erropt.file, "%s%"PRId32"%s", + errfmt->beforeline, where.lineno, errfmt->afterline); + } + fputs(errfmt->beforemsg, erropt.file); + vfprintf(erropt.file, fmt, args); + fputc('\n', erropt.file); + + die_hard(true_type, severity); + unreachable(); +} + +/** + * 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; + int c_errno; /* Saved errno (for ERR_PERROR) */ +}; +struct nasm_errhold { + struct nasm_errhold *up; + struct nasm_errtext *head, **tail; +}; + +static struct strlist *warn_list; +static struct nasm_errhold *errhold_stack; + +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 erropt.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; + int c_errno = errno; + 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->c_errno = c_errno; + 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; + const char *cerrsep = ""; + const char *cerrmsg = ""; + bool buffer = true_type < ERR_NONFATAL || (severity & ERR_HOLD); + + if (severity & ERR_NO_SEVERITY) + pfx = ""; + else + pfx = error_pfx(true_type); + + *warnsuf = 0; + if (!(severity & (ERR_HERE|ERR_PP_LISTMACRO))) { + switch (severity & ERR_MASK) { + case ERR_WARNING: + { + const unsigned int level = WARN_IDX(severity); + 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]", erropt.debug_nasm); + break; + case ERR_INFO: + snprintf(warnsuf, sizeof warnsuf, " [--info=%u]", erropt.verbose_info); + break; + default: + /* Not WARNING, DEBUG or INFO, not suppressible */ + break; + } + + if (severity & ERR_PERROR) { + cerrsep = ":"; + cerrmsg = strerror(et->c_errno); + } + } + + *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) + buffer = false; + + if (buffer) { + /* + * Buffer up warnings and held errors 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%s%s", + file, linestr, errfmt->beforemsg, + pfx, et->msg, cerrsep, cerrmsg, + here, warnsuf); + } else { + /* + * Actually output an error. If we have buffered + * warnings, and this is a non-warning, output them now. + */ + if (warn_list) { + strlist_write(warn_list, "\n", erropt.file); + strlist_free(&warn_list); + } + + fprintf(erropt.file, "%s%s%s%s%s%s%s%s%s\n", + file, linestr, errfmt->beforemsg, + pfx, et->msg, cerrsep, cerrmsg, + 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 (!buffer) { + if (true_type > erropt.worst) + erropt.worst = true_type; + + if (true_type >= ERR_NONFATAL) + erropt.never |= ERR_UNDEAD; + } + +done: + nasm_free_error(et); +} + + +int set_error_format(const char *fmt) +{ + if (!nasm_stricmp("vc", fmt) || + !nasm_stricmp("msvc", fmt) || + !nasm_stricmp("ms", fmt)) + errfmt = &errfmts[ERRFMT_MSVC]; + else if (!nasm_stricmp("gnu", fmt) || + !nasm_stricmp("gcc", fmt)) + errfmt = &errfmts[ERRFMT_GNU]; + else + return -1; + + return 0; +} + +static void usage(void) +{ + fprintf(erropt.file, + "Usage: %s [-@ response_file] [options...] [--] filename\n" + " For additional help:\n" + " %s -h [run|topics|all|-option]\n", + _progname, _progname); +} + +void warn_dollar_hex(void) +{ + nasm_warn(WARN_NUMBER_DEPRECATED_HEX, + "$ prefix for hexadecimal is deprecated"); +} diff --git a/asm/nasm.c b/asm/nasm.c index 5f7168f3..128e5b72 100644 --- a/asm/nasm.c +++ b/asm/nasm.c @@ -27,6 +27,7 @@ #include "iflag.h" #include "quote.h" #include "ver.h" +#include "error.h" /* * This is the maximum number of optimization passes to do. If we ever @@ -45,22 +46,8 @@ 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; @@ -91,7 +78,6 @@ 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; @@ -125,8 +111,10 @@ 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 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); @@ -481,7 +469,7 @@ static void timestamp(void) int main(int argc, char **argv) { /* Do these as early as possible */ - error_file = stderr; + erropt.file = stderr; _progname = argv[0]; if (!_progname || !_progname[0]) _progname = "nasm"; @@ -495,11 +483,10 @@ int main(int argc, char **argv) include_path = strlist_alloc(true); + reset_global_defaults(0); _pass_type = PASS_INIT; _passn = 0; - want_usage = terminate_after_phase = false; - nasm_ctype_init(); src_init(); @@ -516,11 +503,8 @@ int main(int argc, char **argv) operating_mode = OP_NORMAL; parse_cmdline(argc, argv, 1); - if (terminate_after_phase) { - if (want_usage) - usage(); + if (terminate_after_phase()) return 1; - } /* At this point we have ofmt and the name of the desired debug format */ if (!using_debug_info) { @@ -545,14 +529,11 @@ int main(int argc, char **argv) preproc_init(include_path); parse_cmdline(argc, argv, 2); - if (terminate_after_phase) { - if (want_usage) - usage(); + if (terminate_after_phase()) return 1; - } /* Save away the default state of warnings */ - init_warnings(); + error_init(); /* Dependency filename if we are also doing other things */ if (!depend_file && (operating_mode & ~OP_DEPEND)) { @@ -580,9 +561,13 @@ int main(int argc, char **argv) if (!depend_target) depend_target = quote_for_make(outname); + 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" */ @@ -591,29 +576,28 @@ int main(int argc, char **argv) while ((line = pp_getline())) nasm_free(line); pp_cleanup_pass(); - reset_warnings(); + 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; - FILE *out; if (outname) { ofile = nasm_open_write(outname, NF_TEXT); if (!ofile) - nasm_fatal("unable to open output file `%s'", outname); - out = ofile; + nasm_fatalf(ERR_PERROR, + "unable to open output file `%s'", outname); } else { - ofile = NULL; - out = stdout; + ofile = stdout; } location.known = false; _pass_type = PASS_PREPROC; pp_reset(inname, PP_PREPROC, depend_list); + error_pass_start(true); while ((line = pp_getline())) { /* @@ -629,7 +613,7 @@ int main(int argc, char **argv) } else if (lineinc) { if (linnum + lineinc == where.lineno) { /* Add one blank line to account for increment */ - fputc('\n', out); + fputc('\n', ofile); linnum += lineinc; } else if (linnum - lineinc == where.lineno) { /* @@ -649,60 +633,49 @@ int main(int argc, char **argv) continue; if (linnum != where.lineno) { - fprintf(out, "%%line %"PRId32"%+"PRId32" %s\n", + fprintf(ofile, "%%line %"PRId32"%+"PRId32" %s\n", where.lineno, lineinc, quoted_file_name); } linnum = where.lineno + lineinc; - fputs(line, out); - fputc('\n', out); + fputs(line, ofile); + fputc('\n', ofile); } 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; + error_pass_end(); + close_output(terminate_after_phase()); } 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); + nasm_fatalf(ERR_PERROR, "unable to open output file `%s'", outname); ofmt->init(); dfmt->init(); assemble_file(inname, depend_list); - if (!terminate_after_phase) { + if (!terminate_after_phase()) { ofmt->cleanup(); cleanup_labels(); fflush(ofile); if (ferror(ofile)) - nasm_nonfatal("write error on output file `%s'", outname); + nasm_nonfatalf(ERR_PERROR, + "write error on output file `%s'", outname); } - if (ofile) { - fclose(ofile); - if (terminate_after_phase && !keep_all) - remove(outname); - ofile = NULL; - } + close_output(terminate_after_phase()); } pp_cleanup_session(); - if (depend_list && !terminate_after_phase) + if (depend_list && !terminate_after_phase()) emit_dependencies(depend_list); - if (want_usage) - usage(); - raa_free(offsets); saa_free(forwrefs); eval_cleanup(); @@ -710,7 +683,7 @@ int main(int argc, char **argv) src_free(); strlist_free(&include_path); - return terminate_after_phase; + return terminate_after_phase(); } /* @@ -984,7 +957,7 @@ static bool process_arg(char *p, char *q, int pass) break; case 's': if (pass == 1) - error_file = stdout; + erropt.file = stdout; break; case 'o': /* output file */ @@ -1030,7 +1003,7 @@ static bool process_arg(char *p, char *q, int pass) case 'v': case '+': param++; - opt_verbose_info++; + erropt.verbose_info++; break; case 'x': @@ -1039,7 +1012,8 @@ static bool process_arg(char *p, char *q, int pass) break; default: - nasm_fatal("unknown optimization option -O%c\n", + nasm_fatalf(ERR_USAGE, + "unknown optimization option -O%c\n", *param); break; } @@ -1098,17 +1072,11 @@ static bool process_arg(char *p, char *q, int pass) 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 + if (set_error_format(param)) { nasm_fatalf(ERR_USAGE, "unrecognized error reporting format `%s'", param); + } } break; @@ -1326,13 +1294,13 @@ static bool process_arg(char *p, char *q, int pass) break; case OPT_DEBUG: if (pass == 1) - debug_nasm = param ? - strtoul(param, NULL, 10) : debug_nasm+1; + erropt.debug_nasm = param ? + strtoul(param, NULL, 10) : erropt.debug_nasm+1; break; case OPT_INFO: if (pass == 1) - opt_verbose_info = param ? - strtoul(param, NULL, 10) : opt_verbose_info+1; + erropt.verbose_info = param ? + strtoul(param, NULL, 10) : erropt.verbose_info+1; break; case OPT_REPRODUCIBLE: reproducible = true; @@ -1476,7 +1444,7 @@ static void open_and_process_respfile(char *respfile, int pass) process_respfile(rfile, pass); fclose(rfile); } else { - nasm_nonfatalf(ERR_USAGE, "unable to open response file `%s'", respfile); + nasm_fatalf(ERR_PERROR, "unable to open response file `%s'", respfile); } } @@ -1536,13 +1504,15 @@ static void parse_cmdline(int argc, char **argv, int pass) (outname && !strcmp(inname, outname)) || (listname && !strcmp(inname, listname)) || (depend_file && !strcmp(inname, depend_file))) - nasm_fatalf(ERR_USAGE, "will not overwrite input file"); + nasm_fatal("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); + FILE *error_file = nasm_open_write(errname, NF_TEXT); + if (erropt.file) { + erropt.file = error_file; + } else { + nasm_fatalf(ERR_PERROR, "cannot open file `%s' for error messages", + errname); } } } @@ -1579,10 +1549,10 @@ static void forward_refs(insn *instruction) } } -static void print_pass_report(bool failure) +void print_final_report(bool failure) { /* This test is here to reduce the likelihood of a recursive failure */ - if (unlikely(opt_verbose_info >= 1)) { + if (unlikely(erropt.verbose_info >= 1)) { enum pass_type t = pass_type(); if (t >= PASS_FIRST) { @@ -1624,7 +1594,7 @@ static void assemble_file(const char *fname, struct strlist *depend_list) remove(listname); } - while (!terminate_after_phase && !pass_final()) { + while (!terminate_after_phase() && !pass_final()) { _passn++; switch (pass_type()) { case PASS_INIT: @@ -1639,30 +1609,15 @@ static void assemble_file(const char *fname, struct strlist *depend_list) break; } + error_pass_start(pass_final()); 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; + erropt.never = 0; if (!pass_final()) - errflags_never |= ERR_PASS2; + erropt.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; + reset_global_defaults(cmd_sb); cpu = cmd_cpu; if (listname) { @@ -1723,9 +1678,6 @@ static void assemble_file(const char *fname, struct strlist *depend_list) 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: @@ -1770,473 +1722,27 @@ static void assemble_file(const char *fname, struct strlist *depend_list) } } - reset_warnings(); + error_pass_end(); } - if (terminate_after_phase || pass_final()) - print_pass_report(terminate_after_phase); + print_final_report(terminate_after_phase()); lfmt->cleanup(); - strlist_free(&warn_list); } -static bool skip_this_pass(errflags severity) +void close_output(bool error) { - 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; - - if (type == ERR_WARNING) { - /* Promote warning to error? */ - 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. - * - * For FATAL errors doing cleanups, tidying up the list process, - * and so in is acceptable. - * - * For CRITICAL errors, minimize dependencies on memory allocation - * and/or having a system valid state. - * - * For PANIC, if abort_on_panic is set, abort without any other action. - */ -static_fatal_func die_hard(errflags true_type, errflags severity) -{ - if (true_type < ERR_PANIC || !abort_on_panic) { - if (true_type < ERR_CRITICAL) { - /* FATAL shutdown, general cleanup actions are valid */ - print_pass_report(true); - lfmt->cleanup(); - } - - fflush(NULL); - - if (ofile) { - fclose(ofile); - if (!keep_all) - remove(outname); - ofile = NULL; - } - - if (severity & ERR_USAGE) - usage(); - - /* Terminate immediately (exit closes any still open files) */ - exit(true_type - ERR_FATAL + 1); - } - - /* - * abort() shouldn't ever return, but be paranoid about this, - * plus it helps some compilers clue in to the fact that this - * function can never, ever return. - */ - while (1) - abort(); -} - -/* - * 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; - - while (unlikely(been_here)) - abort(); /* Recursive critical 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); - unreachable(); -} - -/** - * 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)) + if (!ofile) 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; + if (ofile == stdout || ofile == stdin) { + fflush(ofile); } else { - nasm_issue_error(et); + fclose(ofile); + if (error && !keep_all) + remove(outname); } - - /* - * 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; - bool buffer = true_type < ERR_NONFATAL || (severity & ERR_HOLD); - - 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) - buffer = false; - - if (buffer) { - /* - * Buffer up warnings and held errors 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 (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 && !buffer) { - 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); + ofile = NULL; } enum help_with { diff --git a/asm/warnings.pl b/asm/warnings.pl index 1cad4281..d533a295 100755 --- a/asm/warnings.pl +++ b/asm/warnings.pl @@ -199,6 +199,7 @@ if ($what eq 'c') { print $out " const char *name;\n"; print $out " enum warn_index warning;\n"; print $out "};\n\n"; + printf $out "#define NUM_WARNINGS %d\n", $#warn_noall + 2; printf $out "#define NUM_WARNING_ALIAS %d\n", scalar(keys %aliases); printf $out "extern const char * const warning_name[%d];\n", @@ -206,10 +207,8 @@ if ($what eq 'c') { printf $out "extern const char * const warning_help[%d];\n", $#warnings + 2; print $out "extern const struct warning_alias warning_alias[NUM_WARNING_ALIAS];\n"; - printf $out "extern const uint8_t warning_default[%d];\n", - $#warn_noall + 2; - printf $out "extern uint8_t warning_state[%d];\n", - $#warn_noall + 2; + printf $out "extern const uint8_t warning_default[NUM_WARNINGS];\n", + printf $out "extern uint8_t warning_state[NUM_WARNINGS];\n", print $out "\n#endif /* $guard */\n"; } elsif ($what eq 'doc') { my %wsec = ('on' => [], 'off' => [], 'err' => [], diff --git a/common/common.c b/common/common.c index 7ba98ca3..7329496c 100644 --- a/common/common.c +++ b/common/common.c @@ -15,6 +15,15 @@ */ struct globalopt globl; +void reset_global_defaults(int bits) +{ + globl.bits = bits; + globl.bnd = false; + globl.rel = 0; + globl.reldef = EAF_FS|EAF_GS; + globl.dollarhex = true; +} + /* * Name of a register token, if applicable; otherwise NULL */ diff --git a/common/errstubs.c b/common/errstubs.c new file mode 100644 index 00000000..0b2e811a --- /dev/null +++ b/common/errstubs.c @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* Copyright 1996-2025 The NASM Authors - All Rights Reserved */ + +#include "compiler.h" +#include "nasmlib.h" +#include "error.h" + +struct errinfo erropt; + +/* Common function body */ +#define nasm_do_error(_sev,_flags) \ + do { \ + const errflags nde_severity = (_sev); \ + const errflags nde_flags = nde_severity | (_flags); \ + va_list ap; \ + va_start(ap, fmt); \ + if (nde_severity >= ERR_CRITICAL) { \ + nasm_verror_critical(nde_flags, fmt, ap); \ + unreachable(); \ + } else { \ + nasm_verror(nde_flags, fmt, ap); \ + if (nde_severity >= ERR_FATAL) \ + unreachable(); \ + } \ + va_end(ap); \ + } while (0) + +/* + * 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(flags & ERR_MASK, flags); +} + +#define nasm_err_helpers(_type, _name, _sev) \ +_type nasm_ ## _name ## f (errflags flags, const char *fmt, ...) \ +{ \ + nasm_do_error(_sev, flags); \ +} \ +_type nasm_ ## _name (const char *fmt, ...) \ +{ \ + nasm_do_error(_sev, 0); \ +} + +nasm_err_helpers(void, listmsg, ERR_LISTMSG) +nasm_err_helpers(void, note, ERR_NOTE) +nasm_err_helpers(void, nonfatal, ERR_NONFATAL) +nasm_err_helpers(fatal_func, fatal, ERR_FATAL) +nasm_err_helpers(fatal_func, critical, ERR_CRITICAL) +nasm_err_helpers(fatal_func, panic, ERR_PANIC) + +/* + * Strongly discourage warnings without level by require flags on warnings. + * This means nasm_warn() is the equivalent of the -f variants of the + * other ones. + * + * This is wrapped in a macro to be able to elide it if the warning is + * disabled, hence the extra underscore. + */ +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)); +} + +/* + * Convenience function for nasm_nonfatal(ERR_HOLD, ...) + */ +void nasm_holderr(const char *fmt, ...) +{ + nasm_do_error(ERR_NONFATAL, ERR_NONFATAL|ERR_HOLD); +} + +/* + * panic() and nasm_assert() + */ +fatal_func nasm_panic_from_macro(const char *func, const char *file, int line) +{ + if (!func) + func = ""; + + nasm_panic("internal error in %s at %s:%d\n", func, file, line); +} + +fatal_func nasm_assert_failed(const char *msg, const char *func, + const char *file, int line) +{ + if (!func) + func = ""; + + nasm_panic("assertion %s failed in %s at %s:%d", msg, func, file, line); +} diff --git a/disasm/disasm.h b/disasm/disasm.h index c1da0b50..d2520113 100644 --- a/disasm/disasm.h +++ b/disasm/disasm.h @@ -87,4 +87,8 @@ const uint8_t *parse_prefixes(struct prefix_info *pf, const uint8_t *data, _op; \ } while (0) + +/* Error module */ +void usage(void); + #endif diff --git a/disasm/diserror.c b/disasm/diserror.c new file mode 100644 index 00000000..c9f81131 --- /dev/null +++ b/disasm/diserror.c @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* Copyright 1996-2025 The NASM Authors - All Rights Reserved */ + +/* + * diserror.c - stubs for the error functions for the disassembler + */ + +#include "compiler.h" +#include "error.h" +#include "disasm.h" + +void usage(void) +{ + const char help[] = + "usage: ndisasm [-aihlruvw] [-b bits] [-o origin] [-s sync...]\n" + " [-e bytes] [-k start,bytes] [-p vendor] file\n" + " -a or -i activates auto (intelligent) sync\n" + " -b 16, -b 32 or -b 64 sets the processor mode\n" + " -u same as -b 32\n" + " -l same as -b 64\n" + " -w wide output (avoids continuation lines)\n" + " -h displays this text\n" + " -r or -v displays the version number\n" + " -e skips bytes of header\n" + " -k avoids disassembling bytes from position \n" + " -p selects the preferred vendor instruction set (intel, amd, cyrix, idt)\n"; + + fputs(help, stderr); +} + +void nasm_verror(errflags severity, const char *fmt, va_list val) +{ + severity &= ERR_MASK; + + vfprintf(stderr, fmt, val); + if (severity >= ERR_FATAL) + exit(severity - ERR_FATAL + 1); +} + +fatal_func nasm_verror_critical(errflags severity, const char *fmt, va_list val) +{ + nasm_verror(severity, fmt, val); + abort(); +} + +uint8_t warning_state[NUM_WARNINGS]; diff --git a/disasm/ndisasm.c b/disasm/ndisasm.c index c1bf9dcf..62f4b2be 100644 --- a/disasm/ndisasm.c +++ b/disasm/ndisasm.c @@ -18,42 +18,13 @@ #include "sync.h" #include "disasm.h" -static int bpl = 8; /* bytes per line of hex dump */ +const char *_progname; -static const char *help = - "usage: ndisasm [-aihlruvw] [-b bits] [-o origin] [-s sync...]\n" - " [-e bytes] [-k start,bytes] [-p vendor] file\n" - " -a or -i activates auto (intelligent) sync\n" - " -b 16, -b 32 or -b 64 sets the processor mode\n" - " -u same as -b 32\n" - " -l same as -b 64\n" - " -w wide output (avoids continuation lines)\n" - " -h displays this text\n" - " -r or -v displays the version number\n" - " -e skips bytes of header\n" - " -k avoids disassembling bytes from position \n" - " -p selects the preferred vendor instruction set (intel, amd, cyrix, idt)\n"; +static int bpl = 8; /* bytes per line of hex dump */ static void output_ins(uint64_t, const uint8_t *, int, const char *); static void skip(uint32_t dist, FILE * fp); -void nasm_verror(errflags severity, const char *fmt, va_list val) -{ - severity &= ERR_MASK; - - vfprintf(stderr, fmt, val); - if (severity >= ERR_FATAL) - exit(severity - ERR_FATAL + 1); -} - -fatal_func nasm_verror_critical(errflags severity, const char *fmt, va_list val) -{ - nasm_verror(severity, fmt, val); - abort(); -} - -errflags errflags_never = 0; - int main(int argc, char **argv) { uint8_t buffer[INSN_MAX * 2], *p; @@ -72,6 +43,9 @@ int main(int argc, char **argv) int64_t offset; FILE *fp; + _progname = argv[0]; + + reset_global_defaults(0); nasm_ctype_init(); iflag_clear_all(&prefer); @@ -90,7 +64,7 @@ int main(int argc, char **argv) p++; break; case 'h': - fputs(help, stderr); + usage(); return 0; case 'r': case 'v': @@ -253,8 +227,8 @@ int main(int argc, char **argv) } if (!filename) { - fprintf(stderr, help, pname); - return 0; + usage(); + return 1; } if (strcmp(filename, "-")) { @@ -269,6 +243,8 @@ int main(int argc, char **argv) fp = stdin; } + reset_global_defaults(bits); + if (initskip > 0) skip(initskip, fp); diff --git a/include/compiler.h b/include/compiler.h index 21025d7f..df668800 100644 --- a/include/compiler.h +++ b/include/compiler.h @@ -477,4 +477,7 @@ static inline unsigned int watcom_switch_hack(uint64_t x) # endif #endif +/* This should be set from main() */ +extern const char *_progname; + #endif /* NASM_COMPILER_H */ diff --git a/include/error.h b/include/error.h index 22c5864a..a6b03a7e 100644 --- a/include/error.h +++ b/include/error.h @@ -90,6 +90,7 @@ const char *error_pfx(errflags severity); #define ERR_PP_PRECOND 0x00000400 /* for preprocessor use */ #define ERR_PP_LISTMACRO 0x00000800 /* from pp_error_list_macros() */ #define ERR_HOLD 0x00001000 /* this error/warning can be held */ +#define ERR_PERROR 0x00002000 /* append strerror(errno) */ /* * These codes define specific types of suppressible warning. @@ -114,14 +115,28 @@ const char *error_pfx(errflags severity); #define WARN_INIT_ON WARN_ST_ENABLED #define WARN_INIT_ERR (WARN_ST_ENABLED|WARN_ST_ERROR) +/* Options and status to/from the error module */ +struct errinfo { + FILE *file; /* Error output file pointer */ + errflags worst; /* Worst severity class encountered */ + errflags never; /* Error flags to unconditionally suppress */ + unsigned int debug_nasm; /* Debug message level */ + unsigned int verbose_info; /* Info message level */ + bool abort_on_panic; /* Call abort() on ERR_PANIC */ +}; +extern struct errinfo erropt; + +int set_error_format(const char *fmt); +void error_init(void); +void error_pass_start(bool final); +void error_pass_end(void); + /* Process a warning option or directive */ bool set_warning_status(const char *value); /* Warning stack management */ void push_warnings(void); void pop_warnings(void); -void init_warnings(void); -void reset_warnings(void); /* * Tentative error hold for warnings/errors indicated with ERR_HOLD. @@ -147,10 +162,9 @@ 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) { - if (warn & errflags_never) + if (warn & erropt.never) return false; return !!(warning_state[WARN_IDX(warn)] & WARN_ST_ENABLED); @@ -168,21 +182,19 @@ static inline bool warn_active(errflags warn) #endif /* Debug level checks */ -extern unsigned int debug_nasm; static inline bool debug_level(unsigned int level) { if (is_constant(level) && level > MAX_DEBUG) return false; - return unlikely(level <= debug_nasm); + return unlikely(level <= erropt.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); + return unlikely(level <= erropt.verbose_info); } #ifdef HAVE_VARIADIC_MACROS @@ -230,5 +242,11 @@ static inline bool info_level(unsigned int level) #endif +/* Callbacks from the error module */ +void print_final_report(bool failure); +void close_output(bool failure); +bool pp_suppress_error(errflags flags); +void pp_error_list_macros(errflags flags); + #endif /* NASM_ERROR_H */ diff --git a/include/nasm.h b/include/nasm.h index 53441afa..1e8aee02 100644 --- a/include/nasm.h +++ b/include/nasm.h @@ -1569,6 +1569,7 @@ struct globalopt { bool dollarhex; /* $-prefixed hexadecimal numbers? */ }; extern struct globalopt globl; +void reset_global_defaults(int bits); extern const char *inname; /* primary input filename */ extern const char *outname; /* output filename */ diff --git a/nasmlib/readnum.c b/nasmlib/readnum.c index 8cf2a0a8..5a004b70 100644 --- a/nasmlib/readnum.c +++ b/nasmlib/readnum.c @@ -8,19 +8,12 @@ #include "compiler.h" #include "nctype.h" - #include "nasmlib.h" +#include "nasm.h" #include "error.h" -#include "nasm.h" /* For globl.dollarhex */ #define lib_isnumchar(c) (nasm_isalnum(c) || (c) == '$' || (c) == '_') -void warn_dollar_hex(void) -{ - nasm_warn(WARN_NUMBER_DEPRECATED_HEX, - "$ prefix for hexadecimal is deprecated"); -} - int64_t readnum(const char *str, bool *error) { const char *r = str, *q;