Files
binutils-gdb/gdb/corefile.c
Andrew Burgess 84f8be0d9c gdb: remove program_space::core_bfd member function
This commit removes the program_space::core_bfd member function, which
was left in place as a temporary hack in the last commit in order to
reduce the size of the last commit.

In every place that 'current_program_space->core_bfd ()' was used I
now call 'get_inferior_core_bfd (current_inferior ())'.

I think there is further scope for improving things in the future,
reducing the number of times we access the core file via global state,
but doing that cleanup might be more involved than the clean up I've
done up to this point.  So I'm leaving that work for the future.

But I think in some places, at the top level (e.g. user command
functions), there's always going to be some cases where we just need
to access the current global state, this is just the nature of the
command handlers.

There should be no user visible changes after this commit.

Approved-By: Tom Tromey <tom@tromey.com>
2025-10-08 13:29:06 +01:00

423 lines
11 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Core dump and executable file functions above target vector, for GDB.
Copyright (C) 1986-2025 Free Software Foundation, Inc.
This file is part of GDB.
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/>. */
#include <signal.h>
#include <fcntl.h>
#include "event-top.h"
#include "extract-store-integer.h"
#include "inferior.h"
#include "symtab.h"
#include "command.h"
#include "cli/cli-cmds.h"
#include "bfd.h"
#include "target.h"
#include "gdbcore.h"
#include "dis-asm.h"
#include <sys/stat.h>
#include "completer.h"
#include "observable.h"
#include "cli/cli-utils.h"
#include "gdbarch.h"
#include "interps.h"
#include "arch-utils.h"
void
reopen_exec_file (void)
{
bfd *exec_bfd = current_program_space->exec_bfd ();
/* Don't do anything if there isn't an exec file. */
if (exec_bfd == nullptr)
return;
/* The main executable can't be an in-memory BFD object. If it was then
the use of bfd_stat below would not work as expected. */
gdb_assert ((exec_bfd->flags & BFD_IN_MEMORY) == 0);
/* If the timestamp of the exec file has changed, reopen it. */
struct stat st;
int res = gdb_bfd_stat (exec_bfd, &st);
if (res == 0
&& current_program_space->ebfd_mtime != 0
&& current_program_space->ebfd_mtime != st.st_mtime)
exec_file_attach (bfd_get_filename (exec_bfd), 0);
}
/* If we have both a core file and an exec file,
print a warning if they don't go together. */
void
validate_files (void)
{
bfd *ebfd = current_program_space->exec_bfd ();
bfd *cbfd = get_inferior_core_bfd (current_inferior ());
if (ebfd != nullptr && cbfd != nullptr)
{
if (!core_file_matches_executable_p (cbfd, ebfd))
warning (_("core file may not match specified executable file."));
else if (gdb_bfd_get_mtime (ebfd) > gdb_bfd_get_mtime (cbfd))
warning (_("exec file is newer than core file."));
}
}
/* See arch-utils.h. */
core_file_exec_context
default_core_parse_exec_context (struct gdbarch *gdbarch, bfd *cbfd)
{
return {};
}
std::string
memory_error_message (enum target_xfer_status err,
struct gdbarch *gdbarch, CORE_ADDR memaddr)
{
switch (err)
{
case TARGET_XFER_E_IO:
/* Actually, address between memaddr and memaddr + len was out of
bounds. */
return string_printf (_("Cannot access memory at address %s"),
paddress (gdbarch, memaddr));
case TARGET_XFER_UNAVAILABLE:
return string_printf (_("Memory at address %s unavailable."),
paddress (gdbarch, memaddr));
default:
internal_error ("unhandled target_xfer_status: %s (%s)",
target_xfer_status_to_string (err),
plongest (err));
}
}
/* Report a memory error by throwing a suitable exception. */
void
memory_error (enum target_xfer_status err, CORE_ADDR memaddr)
{
enum errors exception = GDB_NO_ERROR;
/* Build error string. */
std::string str
= memory_error_message (err, current_inferior ()->arch (), memaddr);
/* Choose the right error to throw. */
switch (err)
{
case TARGET_XFER_E_IO:
exception = MEMORY_ERROR;
break;
case TARGET_XFER_UNAVAILABLE:
exception = NOT_AVAILABLE_ERROR;
break;
}
/* Throw it. */
throw_error (exception, ("%s"), str.c_str ());
}
/* Helper function. */
static void
read_memory_object (enum target_object object, CORE_ADDR memaddr,
gdb_byte *myaddr, ssize_t len)
{
ULONGEST xfered = 0;
while (xfered < len)
{
enum target_xfer_status status;
ULONGEST xfered_len;
status = target_xfer_partial (current_inferior ()->top_target (), object,
NULL, myaddr + xfered, NULL,
memaddr + xfered, len - xfered,
&xfered_len);
if (status != TARGET_XFER_OK)
memory_error (status == TARGET_XFER_EOF ? TARGET_XFER_E_IO : status,
memaddr + xfered);
xfered += xfered_len;
QUIT;
}
}
/* Same as target_read_memory, but report an error if can't read. */
void
read_memory (CORE_ADDR memaddr, gdb_byte *myaddr, ssize_t len)
{
read_memory_object (TARGET_OBJECT_MEMORY, memaddr, myaddr, len);
}
/* Same as target_read_stack, but report an error if can't read. */
void
read_stack (CORE_ADDR memaddr, gdb_byte *myaddr, ssize_t len)
{
read_memory_object (TARGET_OBJECT_STACK_MEMORY, memaddr, myaddr, len);
}
/* Same as target_read_code, but report an error if can't read. */
void
read_code (CORE_ADDR memaddr, gdb_byte *myaddr, ssize_t len)
{
read_memory_object (TARGET_OBJECT_CODE_MEMORY, memaddr, myaddr, len);
}
/* Read memory at MEMADDR of length LEN and put the contents in
RETURN_VALUE. Return 0 if MEMADDR couldn't be read and non-zero
if successful. */
int
safe_read_memory_integer (CORE_ADDR memaddr, int len,
enum bfd_endian byte_order,
LONGEST *return_value)
{
gdb_byte buf[sizeof (LONGEST)];
if (target_read_memory (memaddr, buf, len))
return 0;
*return_value = extract_signed_integer (buf, len, byte_order);
return 1;
}
/* Read memory at MEMADDR of length LEN and put the contents in
RETURN_VALUE. Return 0 if MEMADDR couldn't be read and non-zero
if successful. */
int
safe_read_memory_unsigned_integer (CORE_ADDR memaddr, int len,
enum bfd_endian byte_order,
ULONGEST *return_value)
{
gdb_byte buf[sizeof (ULONGEST)];
if (target_read_memory (memaddr, buf, len))
return 0;
*return_value = extract_unsigned_integer (buf, len, byte_order);
return 1;
}
LONGEST
read_memory_integer (CORE_ADDR memaddr, int len,
enum bfd_endian byte_order)
{
gdb_byte buf[sizeof (LONGEST)];
read_memory (memaddr, buf, len);
return extract_signed_integer (buf, len, byte_order);
}
ULONGEST
read_memory_unsigned_integer (CORE_ADDR memaddr, int len,
enum bfd_endian byte_order)
{
gdb_byte buf[sizeof (ULONGEST)];
read_memory (memaddr, buf, len);
return extract_unsigned_integer (buf, len, byte_order);
}
LONGEST
read_code_integer (CORE_ADDR memaddr, int len,
enum bfd_endian byte_order)
{
gdb_byte buf[sizeof (LONGEST)];
read_code (memaddr, buf, len);
return extract_signed_integer (buf, len, byte_order);
}
ULONGEST
read_code_unsigned_integer (CORE_ADDR memaddr, int len,
enum bfd_endian byte_order)
{
gdb_byte buf[sizeof (ULONGEST)];
read_code (memaddr, buf, len);
return extract_unsigned_integer (buf, len, byte_order);
}
CORE_ADDR
read_memory_typed_address (CORE_ADDR addr, struct type *type)
{
gdb_byte *buf = (gdb_byte *) alloca (type->length ());
read_memory (addr, buf, type->length ());
return extract_typed_address (buf, type);
}
/* See gdbcore.h. */
void
write_memory (CORE_ADDR memaddr,
const bfd_byte *myaddr, ssize_t len)
{
int status;
status = target_write_memory (memaddr, myaddr, len);
if (status != 0)
memory_error (TARGET_XFER_E_IO, memaddr);
}
/* Notify interpreters and observers that INF's memory was changed. */
static void
notify_memory_changed (inferior *inf, CORE_ADDR addr, ssize_t len,
const bfd_byte *data)
{
interps_notify_memory_changed (inf, addr, len, data);
gdb::observers::memory_changed.notify (inf, addr, len, data);
}
/* Same as write_memory, but notify 'memory_changed' observers. */
void
write_memory_with_notification (CORE_ADDR memaddr, const bfd_byte *myaddr,
ssize_t len)
{
write_memory (memaddr, myaddr, len);
notify_memory_changed (current_inferior (), memaddr, len, myaddr);
}
/* Store VALUE at ADDR in the inferior as a LEN-byte unsigned
integer. */
void
write_memory_unsigned_integer (CORE_ADDR addr, int len,
enum bfd_endian byte_order,
ULONGEST value)
{
gdb_byte *buf = (gdb_byte *) alloca (len);
store_unsigned_integer (buf, len, byte_order, value);
write_memory (addr, buf, len);
}
/* Store VALUE at ADDR in the inferior as a LEN-byte signed
integer. */
void
write_memory_signed_integer (CORE_ADDR addr, int len,
enum bfd_endian byte_order,
LONGEST value)
{
gdb_byte *buf = (gdb_byte *) alloca (len);
store_signed_integer (buf, len, byte_order, value);
write_memory (addr, buf, len);
}
/* The current default bfd target. Points to storage allocated for
gnutarget_string. */
const char *gnutarget;
/* Same thing, except it is "auto" not NULL for the default case. */
static std::string gnutarget_string;
static void
show_gnutarget_string (struct ui_file *file, int from_tty,
struct cmd_list_element *c,
const char *value)
{
gdb_printf (file,
_("The current BFD target is \"%s\".\n"), value);
}
static void
set_gnutarget_command (const char *ignore, int from_tty,
struct cmd_list_element *c)
{
const char *gend = gnutarget_string.c_str () + gnutarget_string.size ();
gend = remove_trailing_whitespace (gnutarget_string.c_str (), gend);
gnutarget_string
= gnutarget_string.substr (0, gend - gnutarget_string.data ());
if (gnutarget_string == "auto")
gnutarget = NULL;
else
gnutarget = gnutarget_string.c_str ();
}
/* A completion function for "set gnutarget". */
static void
complete_set_gnutarget (struct cmd_list_element *cmd,
completion_tracker &tracker,
const char *text, const char *word)
{
static const char **bfd_targets;
if (bfd_targets == NULL)
{
int last;
bfd_targets = bfd_target_list ();
for (last = 0; bfd_targets[last] != NULL; ++last)
;
bfd_targets = XRESIZEVEC (const char *, bfd_targets, last + 2);
bfd_targets[last] = "auto";
bfd_targets[last + 1] = NULL;
}
complete_on_enum (tracker, bfd_targets, text, word);
}
/* Set the gnutarget. */
void
set_gnutarget (const char *newtarget)
{
gnutarget_string = newtarget;
set_gnutarget_command (NULL, 0, NULL);
}
INIT_GDB_FILE (core)
{
cmd_list_element *core_file_cmd
= add_cmd ("core-file", class_files, core_file_command, _("\
Use FILE as core dump for examining memory and registers.\n\
Usage: core-file FILE\n\
No arg means have no core file. This command has been superseded by the\n\
`target core' and `detach' commands."), &cmdlist);
set_cmd_completer (core_file_cmd, deprecated_filename_completer);
set_show_commands set_show_gnutarget
= add_setshow_string_noescape_cmd ("gnutarget", class_files,
&gnutarget_string, _("\
Set the current BFD target."), _("\
Show the current BFD target."), _("\
Use `set gnutarget auto' to specify automatic detection."),
set_gnutarget_command,
show_gnutarget_string,
&setlist, &showlist);
set_cmd_completer (set_show_gnutarget.set, complete_set_gnutarget);
add_alias_cmd ("g", set_show_gnutarget.set, class_files, 1, &setlist);
if (getenv ("GNUTARGET"))
set_gnutarget (getenv ("GNUTARGET"));
else
set_gnutarget ("auto");
}