/* bucomm.c -- Bin Utils COMmon code. Copyright (C) 1991-2023 Free Software Foundation, Inc. This file is part of GNU Binutils. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ /* We might put this in a library someday so it could be dynamically loaded, but for now it's not necessary. */ #include "sysdep.h" #include "bfd.h" #include "libiberty.h" #include "filenames.h" #include #include #include "bucomm.h" /* Error reporting. */ char *program_name; void bfd_nonfatal (const char *string) { const char *errmsg; enum bfd_error err = bfd_get_error (); if (err == bfd_error_no_error) errmsg = _("cause of error unknown"); else errmsg = bfd_errmsg (err); fflush (stdout); if (string) fprintf (stderr, "%s: %s: %s\n", program_name, string, errmsg); else fprintf (stderr, "%s: %s\n", program_name, errmsg); } /* Issue a non fatal error message. FILENAME, or if NULL then BFD, are used to indicate the problematic file. SECTION, if non NULL, is used to provide a section name. If FORMAT is non-null, then it is used to print additional information via vfprintf. Finally the bfd error message is printed. In summary, error messages are of one of the following forms: PROGRAM: file: bfd-error-message PROGRAM: file[section]: bfd-error-message PROGRAM: file: printf-message: bfd-error-message PROGRAM: file[section]: printf-message: bfd-error-message. */ void bfd_nonfatal_message (const char *filename, const bfd *abfd, const asection *section, const char *format, ...) { const char *errmsg; const char *section_name; enum bfd_error err = bfd_get_error (); if (err == bfd_error_no_error) errmsg = _("cause of error unknown"); else errmsg = bfd_errmsg (err); fflush (stdout); section_name = NULL; fprintf (stderr, "%s", program_name); if (abfd) { if (!filename) filename = bfd_get_archive_filename (abfd); if (section) section_name = bfd_section_name (section); } if (section_name) fprintf (stderr, ": %s[%s]", filename, section_name); else fprintf (stderr, ": %s", filename); if (format) { va_list args; va_start (args, format); fprintf (stderr, ": "); vfprintf (stderr, format, args); va_end (args); } fprintf (stderr, ": %s\n", errmsg); } void bfd_fatal (const char *string) { bfd_nonfatal (string); xexit (1); } void report (const char * format, va_list args) { fflush (stdout); fprintf (stderr, "%s: ", program_name); vfprintf (stderr, format, args); putc ('\n', stderr); } void fatal (const char *format, ...) { va_list args; va_start (args, format); report (format, args); va_end (args); xexit (1); } void non_fatal (const char *format, ...) { va_list args; va_start (args, format); report (format, args); va_end (args); } /* Like xmalloc except that ABFD's objalloc memory is returned. Use objalloc_free_block to free this memory and all more recently allocated, or more usually, leave it to bfd_close to free. */ void * bfd_xalloc (bfd *abfd, size_t size) { void *ret = bfd_alloc (abfd, size); if (ret == NULL) bfd_fatal (NULL); return ret; } /* Set the default BFD target based on the configured target. Doing this permits the binutils to be configured for a particular target, and linked against a shared BFD library which was configured for a different target. */ void set_default_bfd_target (void) { /* The macro TARGET is defined by Makefile. */ const char *target = TARGET; if (! bfd_set_default_target (target)) fatal (_("can't set BFD default target to `%s': %s"), target, bfd_errmsg (bfd_get_error ())); } /* After a FALSE return from bfd_check_format_matches with bfd_get_error () == bfd_error_file_ambiguously_recognized, print the possible matching targets and free the list of targets. */ void list_matching_formats (char **matching) { fflush (stdout); fprintf (stderr, _("%s: Matching formats:"), program_name); char **p = matching; while (*p) fprintf (stderr, " %s", *p++); free (matching); fputc ('\n', stderr); } /* List the supported targets. */ void list_supported_targets (const char *name, FILE *f) { int t; const char **targ_names; if (name == NULL) fprintf (f, _("Supported targets:")); else fprintf (f, _("%s: supported targets:"), name); targ_names = bfd_target_list (); for (t = 0; targ_names[t] != NULL; t++) fprintf (f, " %s", targ_names[t]); fprintf (f, "\n"); free (targ_names); } /* List the supported architectures. */ void list_supported_architectures (const char *name, FILE *f) { const char ** arch; const char ** arches; if (name == NULL) fprintf (f, _("Supported architectures:")); else fprintf (f, _("%s: supported architectures:"), name); for (arch = arches = bfd_arch_list (); *arch; arch++) fprintf (f, " %s", *arch); fprintf (f, "\n"); free (arches); } static const char * endian_string (enum bfd_endian endian) { switch (endian) { case BFD_ENDIAN_BIG: return _("big endian"); case BFD_ENDIAN_LITTLE: return _("little endian"); default: return _("endianness unknown"); } } /* Data passed to do_display_target and other target iterators. */ struct display_target { /* Temp file. */ char *filename; /* Return status. */ int error; /* Number of targets. */ int count; /* Size of info in bytes. */ size_t alloc; /* Per-target info. */ struct { /* Target name. */ const char *name; /* Non-zero if target/arch combination supported. */ unsigned char arch[bfd_arch_last - bfd_arch_obscure - 1]; } *info; }; /* List the targets that BFD is configured to support, each followed by its endianness and the architectures it supports. Also build info about target/archs. */ static int do_display_target (const bfd_target *targ, void *data) { struct display_target *param = (struct display_target *) data; bfd *abfd; size_t amt; param->count += 1; amt = param->count * sizeof (*param->info); if (param->alloc < amt) { size_t size = ((param->count < 64 ? 64 : param->count) * sizeof (*param->info) * 2); param->info = xrealloc (param->info, size); memset ((char *) param->info + param->alloc, 0, size - param->alloc); param->alloc = size; } param->info[param->count - 1].name = targ->name; printf (_("%s\n (header %s, data %s)\n"), targ->name, endian_string (targ->header_byteorder), endian_string (targ->byteorder)); abfd = bfd_openw (param->filename, targ->name); if (abfd == NULL) { bfd_nonfatal (param->filename); param->error = 1; } else if (!bfd_set_format (abfd, bfd_object)) { if (bfd_get_error () != bfd_error_invalid_operation) { bfd_nonfatal (targ->name); param->error = 1; } } else { enum bfd_architecture a; for (a = bfd_arch_obscure + 1; a < bfd_arch_last; a++) if (bfd_set_arch_mach (abfd, a, 0)) { printf (" %s\n", bfd_printable_arch_mach (a, 0)); param->info[param->count - 1].arch[a - bfd_arch_obscure - 1] = 1; } } if (abfd != NULL) bfd_close_all_done (abfd); return param->error; } static void display_target_list (struct display_target *arg) { arg->filename = make_temp_file (NULL); arg->error = 0; arg->count = 0; arg->alloc = 0; arg->info = NULL; bfd_iterate_over_targets (do_display_target, arg); unlink (arg->filename); free (arg->filename); } /* Calculate how many targets we can print across the page. */ static int do_info_size (int targ, int width, const struct display_target *arg) { while (targ < arg->count) { width -= strlen (arg->info[targ].name) + 1; if (width < 0) return targ; ++targ; } return targ; } /* Print header of target names. */ static void do_info_header (int targ, int stop_targ, const struct display_target *arg) { while (targ != stop_targ) printf ("%s ", arg->info[targ++].name); } /* Print a table row. */ static void do_info_row (int targ, int stop_targ, enum bfd_architecture a, const struct display_target *arg) { while (targ != stop_targ) { if (arg->info[targ].arch[a - bfd_arch_obscure - 1]) fputs (arg->info[targ].name, stdout); else { int l = strlen (arg->info[targ].name); while (l--) putchar ('-'); } ++targ; if (targ != stop_targ) putchar (' '); } } /* Print tables of all the target-architecture combinations that BFD has been configured to support. */ static void display_target_tables (const struct display_target *arg) { const char *columns; int width, start_targ, stop_targ; enum bfd_architecture arch; int longest_arch = 0; for (arch = bfd_arch_obscure + 1; arch < bfd_arch_last; arch++) { const char *s = bfd_printable_arch_mach (arch, 0); int len = strlen (s); if (len > longest_arch) longest_arch = len; } width = 0; columns = getenv ("COLUMNS"); if (columns != NULL) width = atoi (columns); if (width == 0) width = 80; for (start_targ = 0; start_targ < arg->count; start_targ = stop_targ) { stop_targ = do_info_size (start_targ, width - longest_arch - 1, arg); printf ("\n%*s", longest_arch + 1, " "); do_info_header (start_targ, stop_targ, arg); putchar ('\n'); for (arch = bfd_arch_obscure + 1; arch < bfd_arch_last; arch++) { if (strcmp (bfd_printable_arch_mach (arch, 0), "UNKNOWN!") != 0) { printf ("%*s ", longest_arch, bfd_printable_arch_mach (arch, 0)); do_info_row (start_targ, stop_targ, arch, arg); putchar ('\n'); } } } } int display_info (void) { struct display_target arg; printf (_("BFD header file version %s\n"), BFD_VERSION_STRING); display_target_list (&arg); if (!arg.error) display_target_tables (&arg); return arg.error; } /* Display the archive header for an element as if it were an ls -l listing: Mode User\tGroup\tSize\tDate Name */ void print_arelt_descr (FILE *file, bfd *abfd, bool verbose, bool offsets) { struct stat buf; if (verbose) { if (bfd_stat_arch_elt (abfd, &buf) == 0) { char modebuf[11]; char timebuf[40]; time_t when = buf.st_mtime; const char *ctime_result = (const char *) ctime (&when); /* PR binutils/17605: Check for corrupt time values. */ if (ctime_result == NULL) sprintf (timebuf, _("