xref: /netbsd-src/external/gpl3/binutils/dist/gas/messages.c (revision cb63e24e8d6aae7ddac1859a9015f48b1d8bd90e)
1 /* messages.c - error reporter -
2    Copyright (C) 1987-2024 Free Software Foundation, Inc.
3    This file is part of GAS, the GNU Assembler.
4 
5    GAS is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3, or (at your option)
8    any later version.
9 
10    GAS is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License
16    along with GAS; see the file COPYING.  If not, write to the Free
17    Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
18    02110-1301, USA.  */
19 
20 #include "as.h"
21 #include <limits.h>
22 #include <signal.h>
23 
24 /* If the system doesn't provide strsignal, we get it defined in
25    libiberty but no declaration is supplied.  Because, reasons. */
26 #if !defined (HAVE_STRSIGNAL) && !defined (strsignal)
27 extern const char *strsignal (int);
28 #endif
29 
30 static void identify (const char *);
31 static void as_show_where (void);
32 static void as_warn_internal (const char *, unsigned int, char *);
33 static void as_bad_internal (const char *, unsigned int, char *);
34 static void signal_crash (int) ATTRIBUTE_NORETURN;
35 
36 /* Despite the rest of the comments in this file, (FIXME-SOON),
37    here is the current scheme for error messages etc:
38 
39    as_fatal() is used when gas is quite confused and
40    continuing the assembly is pointless.  In this case we
41    exit immediately with error status.
42 
43    as_bad() is used to mark errors that result in what we
44    presume to be a useless object file.  Say, we ignored
45    something that might have been vital.  If we see any of
46    these, assembly will continue to the end of the source,
47    no object file will be produced, and we will terminate
48    with error status.  The new option, -Z, tells us to
49    produce an object file anyway but we still exit with
50    error status.  The assumption here is that you don't want
51    this object file but we could be wrong.
52 
53    as_warn() is used when we have an error from which we
54    have a plausible error recovery.  eg, masking the top
55    bits of a constant that is longer than will fit in the
56    destination.  In this case we will continue to assemble
57    the source, although we may have made a bad assumption,
58    and we will produce an object file and return normal exit
59    status (ie, no error).  The new option -X tells us to
60    treat all as_warn() errors as as_bad() errors.  That is,
61    no object file will be produced and we will exit with
62    error status.  The idea here is that we don't kill an
63    entire make because of an error that we knew how to
64    correct.  On the other hand, sometimes you might want to
65    stop the make at these points.
66 
67    as_tsktsk() is used when we see a minor error for which
68    our error recovery action is almost certainly correct.
69    In this case, we print a message and then assembly
70    continues as though no error occurred.
71 
72    as_abort () is used for logic failure (assert or abort, signal).
73 */
74 
75 static void
identify(const char * file)76 identify (const char *file)
77 {
78   static int identified;
79 
80   if (identified)
81     return;
82   identified++;
83 
84   if (!file)
85     {
86       unsigned int x;
87       file = as_where (&x);
88     }
89 
90   if (file)
91     fprintf (stderr, "%s: ", file);
92   fprintf (stderr, _("Assembler messages:\n"));
93 }
94 
95 /* The number of warnings issued.  */
96 static int warning_count;
97 
98 int
had_warnings(void)99 had_warnings (void)
100 {
101   return warning_count;
102 }
103 
104 /* Nonzero if we've hit a 'bad error', and should not write an obj file,
105    and exit with a nonzero error code.  */
106 
107 static int error_count;
108 
109 int
had_errors(void)110 had_errors (void)
111 {
112   return error_count;
113 }
114 
115 /* Print the current location to stderr.  */
116 
117 static void
as_show_where(void)118 as_show_where (void)
119 {
120   const char *file;
121   unsigned int line;
122 
123   file = as_where_top (&line);
124   identify (file);
125   if (file)
126     {
127       if (line != 0)
128 	fprintf (stderr, "%s:%u: ", file, line);
129       else
130 	fprintf (stderr, "%s: ", file);
131     }
132 }
133 
134 /* Send to stderr a string as information, with location data passed in.
135    Note that for now this is not intended for general use.  */
136 
137 void
as_info_where(const char * file,unsigned int line,unsigned int indent,const char * format,...)138 as_info_where (const char *file, unsigned int line, unsigned int indent,
139 	       const char *format, ...)
140 {
141   va_list args;
142   char buffer[2000];
143 
144   va_start (args, format);
145   vsnprintf (buffer, sizeof (buffer), format, args);
146   va_end (args);
147   fprintf (stderr, "%s:%u: %*s%s%s\n",
148 	   file, line, (int)indent, "", _("Info: "), buffer);
149 }
150 
151 /* Send to stderr a string as a warning, and locate warning
152    in input file(s).
153    Please only use this for when we have some recovery action.
154    Please explain in string (which may have '\n's) what recovery was
155    done.  */
156 
157 void
as_tsktsk(const char * format,...)158 as_tsktsk (const char *format, ...)
159 {
160   va_list args;
161 
162   as_show_where ();
163   va_start (args, format);
164   vfprintf (stderr, format, args);
165   va_end (args);
166   (void) putc ('\n', stderr);
167   as_report_context ();
168 }
169 
170 /* The common portion of as_warn and as_warn_where.  */
171 
172 static void
as_warn_internal(const char * file,unsigned int line,char * buffer)173 as_warn_internal (const char *file, unsigned int line, char *buffer)
174 {
175   bool context = false;
176 
177   ++warning_count;
178 
179   if (file == NULL)
180     {
181       file = as_where_top (&line);
182       context = true;
183     }
184 
185   identify (file);
186   if (file)
187     {
188       if (line != 0)
189 	fprintf (stderr, "%s:%u: %s%s\n", file, line, _("Warning: "), buffer);
190       else
191 	fprintf (stderr, "%s: %s%s\n", file, _("Warning: "), buffer);
192     }
193   else
194     fprintf (stderr, "%s%s\n", _("Warning: "), buffer);
195 
196   if (context)
197     as_report_context ();
198 
199 #ifndef NO_LISTING
200   listing_warning (buffer);
201 #endif
202 }
203 
204 /* Send to stderr a string as a warning, and locate warning
205    in input file(s).
206    Please only use this for when we have some recovery action.
207    Please explain in string (which may have '\n's) what recovery was
208    done.  */
209 
210 void
as_warn(const char * format,...)211 as_warn (const char *format, ...)
212 {
213   va_list args;
214   char buffer[2000];
215 
216   if (!flag_no_warnings)
217     {
218       va_start (args, format);
219       vsnprintf (buffer, sizeof (buffer), format, args);
220       va_end (args);
221       as_warn_internal ((char *) NULL, 0, buffer);
222     }
223 }
224 
225 /* Like as_warn but the file name and line number are passed in.
226    Unfortunately, we have to repeat the function in order to handle
227    the varargs correctly and portably.  */
228 
229 void
as_warn_where(const char * file,unsigned int line,const char * format,...)230 as_warn_where (const char *file, unsigned int line, const char *format, ...)
231 {
232   va_list args;
233   char buffer[2000];
234 
235   if (!flag_no_warnings)
236     {
237       va_start (args, format);
238       vsnprintf (buffer, sizeof (buffer), format, args);
239       va_end (args);
240       as_warn_internal (file, line, buffer);
241     }
242 }
243 
244 /* The common portion of as_bad and as_bad_where.  */
245 
246 static void
as_bad_internal(const char * file,unsigned int line,char * buffer)247 as_bad_internal (const char *file, unsigned int line, char *buffer)
248 {
249   bool context = false;
250 
251   ++error_count;
252 
253   if (file == NULL)
254     {
255       file = as_where_top (&line);
256       context = true;
257     }
258 
259   identify (file);
260   if (file)
261     {
262       if (line != 0)
263 	fprintf (stderr, "%s:%u: %s%s\n", file, line, _("Error: "), buffer);
264       else
265 	fprintf (stderr, "%s: %s%s\n", file, _("Error: "), buffer);
266     }
267   else
268     fprintf (stderr, "%s%s\n", _("Error: "), buffer);
269 
270   if (context)
271     as_report_context ();
272 
273 #ifndef NO_LISTING
274   listing_error (buffer);
275 #endif
276 }
277 
278 /* Send to stderr a string as a warning, and locate warning in input
279    file(s).  Please use when there is no recovery, but we want to
280    continue processing but not produce an object file.
281    Please explain in string (which may have '\n's) what recovery was
282    done.  */
283 
284 void
as_bad(const char * format,...)285 as_bad (const char *format, ...)
286 {
287   va_list args;
288   char buffer[2000];
289 
290   va_start (args, format);
291   vsnprintf (buffer, sizeof (buffer), format, args);
292   va_end (args);
293 
294   as_bad_internal ((char *) NULL, 0, buffer);
295 }
296 
297 /* Like as_bad but the file name and line number are passed in.
298    Unfortunately, we have to repeat the function in order to handle
299    the varargs correctly and portably.  */
300 
301 void
as_bad_where(const char * file,unsigned int line,const char * format,...)302 as_bad_where (const char *file, unsigned int line, const char *format, ...)
303 {
304   va_list args;
305   char buffer[2000];
306 
307   va_start (args, format);
308   vsnprintf (buffer, sizeof (buffer), format, args);
309   va_end (args);
310 
311   as_bad_internal (file, line, buffer);
312 }
313 
314 /* Send to stderr a string as a fatal message, and print location of
315    error in input file(s).
316    Please only use this for when we DON'T have some recovery action.
317    It xexit()s with a warning status.  */
318 
319 void
as_fatal(const char * format,...)320 as_fatal (const char *format, ...)
321 {
322   va_list args;
323 
324   as_show_where ();
325   va_start (args, format);
326   fprintf (stderr, _("Fatal error: "));
327   vfprintf (stderr, format, args);
328   (void) putc ('\n', stderr);
329   va_end (args);
330   as_report_context ();
331   /* Delete the output file, if it exists.  This will prevent make from
332      thinking that a file was created and hence does not need rebuilding.  */
333   if (out_file_name != NULL)
334     unlink_if_ordinary (out_file_name);
335   xexit (EXIT_FAILURE);
336 }
337 
338 /* Indicate internal constency error.
339    Arguments: Filename, line number, optional function name.
340    FILENAME may be NULL, which we use for crash-via-signal.  */
341 
342 void
as_abort(const char * file,int line,const char * fn)343 as_abort (const char *file, int line, const char *fn)
344 {
345   as_show_where ();
346 
347   if (!file)
348     fprintf (stderr, _("Internal error (%s).\n"), fn ? fn : "unknown");
349   else if (fn)
350     fprintf (stderr, _("Internal error in %s at %s:%d.\n"), fn, file, line);
351   else
352     fprintf (stderr, _("Internal error at %s:%d.\n"), file, line);
353   as_report_context ();
354 
355   fprintf (stderr, _("Please report this bug.\n"));
356 
357   xexit (EXIT_FAILURE);
358 }
359 
360 /* Handler for fatal signals, such as SIGSEGV. */
361 
362 static void
signal_crash(int signo)363 signal_crash (int signo)
364 {
365   /* Reset, to prevent unbounded recursion.  */
366   signal (signo, SIG_DFL);
367 
368   as_abort (NULL, 0, strsignal (signo));
369 }
370 
371 /* Register signal handlers, for less abrubt crashes.  */
372 
373 void
signal_init(void)374 signal_init (void)
375 {
376 #ifdef SIGSEGV
377   signal (SIGSEGV, signal_crash);
378 #endif
379 #ifdef SIGILL
380   signal (SIGILL, signal_crash);
381 #endif
382 #ifdef SIGBUS
383   signal (SIGBUS, signal_crash);
384 #endif
385 #ifdef SIGABRT
386   signal (SIGABRT, signal_crash);
387 #endif
388 #if defined SIGIOT && (!defined SIGABRT || SIGABRT != SIGIOT)
389   signal (SIGIOT, signal_crash);
390 #endif
391 #ifdef SIGFPE
392   signal (SIGFPE, signal_crash);
393 #endif
394 }
395 
396 /* Support routines.  */
397 
398 #define HEX_MAX_THRESHOLD	1024
399 #define HEX_MIN_THRESHOLD	-(HEX_MAX_THRESHOLD)
400 
401 static void
as_internal_value_out_of_range(const char * prefix,offsetT val,offsetT min,offsetT max,const char * file,unsigned line,bool bad)402 as_internal_value_out_of_range (const char *prefix,
403 				offsetT val,
404 				offsetT min,
405 				offsetT max,
406 				const char *file,
407 				unsigned line,
408 				bool bad)
409 {
410   const char * err;
411 
412   if (prefix == NULL)
413     prefix = "";
414 
415   if (val >= min && val <= max)
416     {
417       addressT right = max & -max;
418 
419       if (max <= 1)
420 	abort ();
421 
422       /* xgettext:c-format  */
423       err = _("%s out of domain (%" PRId64
424 	      " is not a multiple of %" PRId64 ")");
425 
426       if (bad)
427 	as_bad_where (file, line, err, prefix, (int64_t) val, (int64_t) right);
428       else
429 	as_warn_where (file, line, err, prefix, (int64_t) val, (int64_t) right);
430     }
431   else if (   val < HEX_MAX_THRESHOLD
432 	   && min < HEX_MAX_THRESHOLD
433 	   && max < HEX_MAX_THRESHOLD
434 	   && val > HEX_MIN_THRESHOLD
435 	   && min > HEX_MIN_THRESHOLD
436 	   && max > HEX_MIN_THRESHOLD)
437     {
438       /* xgettext:c-format.  */
439       err = _("%s out of range (%" PRId64
440 	      " is not between %" PRId64 " and %" PRId64 ")");
441 
442       if (bad)
443 	as_bad_where (file, line, err, prefix,
444 		      (int64_t) val, (int64_t) min, (int64_t) max);
445       else
446 	as_warn_where (file, line, err, prefix,
447 		       (int64_t) val, (int64_t) min, (int64_t) max);
448     }
449   else
450     {
451       /* xgettext:c-format.  */
452       err = _("%s out of range (0x%" PRIx64
453 	      " is not between 0x%" PRIx64 " and 0x%" PRIx64 ")");
454 
455       if (bad)
456 	as_bad_where (file, line, err, prefix,
457 		      (int64_t) val, (int64_t) min, (int64_t) max);
458       else
459 	as_warn_where (file, line, err, prefix,
460 		       (int64_t) val, (int64_t) min, (int64_t) max);
461     }
462 }
463 
464 void
as_warn_value_out_of_range(const char * prefix,offsetT value,offsetT min,offsetT max,const char * file,unsigned line)465 as_warn_value_out_of_range (const char *prefix,
466 			   offsetT value,
467 			   offsetT min,
468 			   offsetT max,
469 			   const char *file,
470 			   unsigned line)
471 {
472   as_internal_value_out_of_range (prefix, value, min, max, file, line, false);
473 }
474 
475 void
as_bad_value_out_of_range(const char * prefix,offsetT value,offsetT min,offsetT max,const char * file,unsigned line)476 as_bad_value_out_of_range (const char *prefix,
477 			   offsetT value,
478 			   offsetT min,
479 			   offsetT max,
480 			   const char *file,
481 			   unsigned line)
482 {
483   as_internal_value_out_of_range (prefix, value, min, max, file, line, true);
484 }
485