| /* Utility to load a file into the simulator. |
| Copyright (C) 1997-2024 Free Software Foundation, Inc. |
| |
| 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, see <http://www.gnu.org/licenses/>. */ |
| |
| /* This is a standalone loader, independent of the sim-basic.h machinery, |
| as it is used by simulators that don't use it [though that doesn't mean |
| to suggest that they shouldn't :-)]. */ |
| |
| /* This must come before any other includes. */ |
| #include "defs.h" |
| |
| #include <stdarg.h> |
| #include <stdio.h> /* for NULL */ |
| #include <stdlib.h> |
| #include <time.h> |
| |
| #include "ansidecl.h" |
| #include "bfd.h" |
| |
| #include "sim/callback.h" |
| #include "sim/sim.h" |
| #include "sim-utils.h" |
| |
| static void eprintf (host_callback *, const char *, ...); |
| static void xprintf (host_callback *, const char *, ...); |
| static void report_transfer_performance |
| (host_callback *, unsigned long, time_t, time_t); |
| |
| /* Load program PROG into the simulator using the function DO_WRITE. |
| If PROG_BFD is non-NULL, the file has already been opened. |
| If VERBOSE_P is non-zero statistics are printed of each loaded section |
| and the transfer rate (for consistency with gdb). |
| If LMA_P is non-zero the program sections are loaded at the LMA |
| rather than the VMA |
| If this fails an error message is printed and NULL is returned. |
| If it succeeds the bfd is returned. |
| NOTE: For historical reasons, older hardware simulators incorrectly |
| write the program sections at LMA interpreted as a virtual address. |
| This is still accommodated for backward compatibility reasons. */ |
| |
| |
| bfd * |
| sim_load_file (SIM_DESC sd, const char *myname, host_callback *callback, |
| const char *prog, bfd *prog_bfd, int verbose_p, int lma_p, |
| sim_write_fn do_write) |
| { |
| asection *s; |
| /* Record separately as we don't want to close PROG_BFD if it was passed. */ |
| bfd *result_bfd; |
| time_t start_time = 0; /* Start and end times of download */ |
| time_t end_time = 0; |
| unsigned long data_count = 0; /* Number of bytes transferred to memory */ |
| int found_loadable_section; |
| |
| if (prog_bfd != NULL) |
| result_bfd = prog_bfd; |
| else |
| { |
| result_bfd = bfd_openr (prog, 0); |
| if (result_bfd == NULL) |
| { |
| eprintf (callback, "%s: can't open \"%s\": %s\n", |
| myname, prog, bfd_errmsg (bfd_get_error ())); |
| return NULL; |
| } |
| } |
| |
| if (!bfd_check_format (result_bfd, bfd_object)) |
| { |
| eprintf (callback, "%s: \"%s\" is not an object file: %s\n", |
| myname, prog, bfd_errmsg (bfd_get_error ())); |
| /* Only close if we opened it. */ |
| if (prog_bfd == NULL) |
| bfd_close (result_bfd); |
| return NULL; |
| } |
| |
| if (verbose_p) |
| start_time = time (NULL); |
| |
| found_loadable_section = 0; |
| for (s = result_bfd->sections; s; s = s->next) |
| { |
| if (s->flags & SEC_LOAD) |
| { |
| bfd_size_type size; |
| |
| size = bfd_section_size (s); |
| if (size > 0) |
| { |
| unsigned char *buffer; |
| bfd_vma lma; |
| |
| buffer = malloc (size); |
| if (buffer == NULL) |
| { |
| eprintf (callback, |
| "%s: insufficient memory to load \"%s\"\n", |
| myname, prog); |
| /* Only close if we opened it. */ |
| if (prog_bfd == NULL) |
| bfd_close (result_bfd); |
| return NULL; |
| } |
| if (lma_p) |
| lma = bfd_section_lma (s); |
| else |
| lma = bfd_section_vma (s); |
| if (verbose_p) |
| { |
| xprintf (callback, |
| "Loading section %s, size 0x%" PRIx64 |
| " %s %" PRIx64 "\n", |
| bfd_section_name (s), (uint64_t) size, |
| lma_p ? "lma" : "vma", (uint64_t) lma); |
| } |
| data_count += size; |
| bfd_get_section_contents (result_bfd, s, buffer, 0, size); |
| do_write (sd, lma, buffer, size); |
| found_loadable_section = 1; |
| free (buffer); |
| } |
| } |
| } |
| |
| if (!found_loadable_section) |
| { |
| eprintf (callback, |
| "%s: no loadable sections \"%s\"\n", |
| myname, prog); |
| return NULL; |
| } |
| |
| if (verbose_p) |
| { |
| end_time = time (NULL); |
| xprintf (callback, "Start address %" PRIx64 "\n", |
| (uint64_t) bfd_get_start_address (result_bfd)); |
| report_transfer_performance (callback, data_count, start_time, end_time); |
| } |
| |
| bfd_cache_close (result_bfd); |
| |
| return result_bfd; |
| } |
| |
| static void |
| xprintf (host_callback *callback, const char *fmt, ...) |
| { |
| va_list ap; |
| |
| va_start (ap, fmt); |
| |
| (*callback->vprintf_filtered) (callback, fmt, ap); |
| |
| va_end (ap); |
| } |
| |
| static void |
| eprintf (host_callback *callback, const char *fmt, ...) |
| { |
| va_list ap; |
| |
| va_start (ap, fmt); |
| |
| (*callback->evprintf_filtered) (callback, fmt, ap); |
| |
| va_end (ap); |
| } |
| |
| /* Report how fast the transfer went. */ |
| |
| static void |
| report_transfer_performance (host_callback *callback, unsigned long data_count, |
| time_t start_time, time_t end_time) |
| { |
| xprintf (callback, "Transfer rate: "); |
| if (end_time != start_time) |
| xprintf (callback, "%ld bits/sec", |
| (data_count * 8) / (end_time - start_time)); |
| else |
| xprintf (callback, "%ld bits in <1 sec", (data_count * 8)); |
| xprintf (callback, ".\n"); |
| } |