/* Core dump and executable file functions below target vector, for GDB.
- Copyright (C) 1986-2020 Free Software Foundation, Inc.
+ Copyright (C) 1986-2021 Free Software Foundation, Inc.
This file is part of GDB.
#include "build-id.h"
#include "gdbsupport/pathstuff.h"
#include <unordered_map>
+#include <unordered_set>
+#include "gdbcmd.h"
+#include "xml-tdesc.h"
#ifndef O_LARGEFILE
#define O_LARGEFILE 0
{
public:
core_target ();
- ~core_target () override;
const target_info &info () const override
{ return core_target_info; }
const char *human_name,
bool required);
+ /* See definition. */
+ void info_proc_mappings (struct gdbarch *gdbarch);
+
private: /* per-core data */
/* The core's section table. Note that these target sections are
shared library bfds. The core bfd sections are an implementation
detail of the core target, just like ptrace is for unix child
targets. */
- target_section_table m_core_section_table {};
+ target_section_table m_core_section_table;
/* File-backed address space mappings: some core files include
information about memory mapped files. */
- target_section_table m_core_file_mappings {};
+ target_section_table m_core_file_mappings;
+
+ /* Unavailable mappings. These correspond to pathnames which either
+ weren't found or could not be opened. Knowing these addresses can
+ still be useful. */
+ std::vector<mem_range> m_core_unavailable_mappings;
/* Build m_core_file_mappings. Called from the constructor. */
void build_file_mappings ();
+ /* Helper method for xfer_partial. */
+ enum target_xfer_status xfer_memory_via_mappings (gdb_byte *readbuf,
+ const gdb_byte *writebuf,
+ ULONGEST offset,
+ ULONGEST len,
+ ULONGEST *xfered_len);
+
/* FIXME: kettenis/20031023: Eventually this field should
disappear. */
struct gdbarch *m_core_gdbarch = NULL;
core_target::core_target ()
{
+ /* Find a first arch based on the BFD. We need the initial gdbarch so
+ we can setup the hooks to find a target description. */
m_core_gdbarch = gdbarch_from_bfd (core_bfd);
+ /* If the arch is able to read a target description from the core, it
+ could yield a more specific gdbarch. */
+ const struct target_desc *tdesc = read_description ();
+
+ if (tdesc != nullptr)
+ {
+ struct gdbarch_info info;
+ info.abfd = core_bfd;
+ info.target_desc = tdesc;
+ m_core_gdbarch = gdbarch_find_by_info (info);
+ }
+
if (!m_core_gdbarch
|| !gdbarch_iterate_over_regset_sections_p (m_core_gdbarch))
error (_("\"%s\": Core file format not supported"),
bfd_get_filename (core_bfd));
/* Find the data section */
- if (build_section_table (core_bfd,
- &m_core_section_table.sections,
- &m_core_section_table.sections_end))
- error (_("\"%s\": Can't find sections: %s"),
- bfd_get_filename (core_bfd), bfd_errmsg (bfd_get_error ()));
+ m_core_section_table = build_section_table (core_bfd);
build_file_mappings ();
}
-core_target::~core_target ()
-{
- xfree (m_core_section_table.sections);
- xfree (m_core_file_mappings.sections);
-}
-
/* Construct the target_section_table for file-backed mappings if
they exist.
core_target::build_file_mappings ()
{
std::unordered_map<std::string, struct bfd *> bfd_map;
+ std::unordered_set<std::string> unavailable_paths;
/* See linux_read_core_file_mappings() in linux-tdep.c for an example
read_core_file_mappings method. */
gdbarch_read_core_file_mappings (m_core_gdbarch, core_bfd,
/* After determining the number of mappings, read_core_file_mappings
- will invoke this lambda which allocates target_section storage for
- the mappings. */
- [&] (ULONGEST count)
+ will invoke this lambda. */
+ [&] (ULONGEST)
{
- m_core_file_mappings.sections = XNEWVEC (struct target_section, count);
- m_core_file_mappings.sections_end = m_core_file_mappings.sections;
},
/* read_core_file_mappings will invoke this lambda for each mapping
that it finds. */
[&] (int num, ULONGEST start, ULONGEST end, ULONGEST file_ofs,
- const char *filename, const void *other)
+ const char *filename, const bfd_build_id *build_id)
{
/* Architecture-specific read_core_mapping methods are expected to
weed out non-file-backed mappings. */
= exec_file_find (filename, NULL);
if (expanded_fname == nullptr)
{
- warning (_("Can't open file %s during file-backed mapping "
- "note processing"),
- expanded_fname.get ());
+ m_core_unavailable_mappings.emplace_back (start, end - start);
+ /* Print just one warning per path. */
+ if (unavailable_paths.insert (filename).second)
+ warning (_("Can't open file %s during file-backed mapping "
+ "note processing"),
+ filename);
return;
}
bfd = bfd_map[filename] = bfd_openr (expanded_fname.get (),
- "binary");
+ "binary");
if (bfd == nullptr || !bfd_check_format (bfd, bfd_object))
{
+ m_core_unavailable_mappings.emplace_back (start, end - start);
/* If we get here, there's a good chance that it's due to
an internal error. We issue a warning instead of an
internal error because of the possibility that the
bfd_set_section_alignment (sec, 2);
/* Set target_section fields. */
- struct target_section *ts = m_core_file_mappings.sections_end++;
- ts->addr = start;
- ts->endaddr = end;
- ts->owner = nullptr;
- ts->the_bfd_section = sec;
+ m_core_file_mappings.emplace_back (start, end, sec);
});
-}
-static void add_to_thread_list (bfd *, asection *, void *);
+ normalize_mem_ranges (&m_core_unavailable_mappings);
+}
/* An arbitrary identifier for the core inferior. */
#define CORELOW_PID 1
exit_inferior_silent (current_inferior ());
/* Clear out solib state while the bfd is still open. See
- comments in clear_solib in solib.c. */
+ comments in clear_solib in solib.c. */
clear_solib ();
current_program_space->cbfd.reset (nullptr);
extract the list of threads in a core file. */
static void
-add_to_thread_list (bfd *abfd, asection *asect, void *reg_sect_arg)
+add_to_thread_list (asection *asect, asection *reg_sect)
{
int core_tid;
int pid, lwpid;
- asection *reg_sect = (asection *) reg_sect_arg;
bool fake_pid_p = false;
struct inferior *inf;
}
gdb::unique_xmalloc_ptr<char> filename (tilde_expand (arg));
- if (!IS_ABSOLUTE_PATH (filename.get ()))
+ if (strlen (filename.get ()) != 0
+ && !IS_ABSOLUTE_PATH (filename.get ()))
filename = gdb_abspath (filename.get ());
flags = O_BINARY | O_LARGEFILE;
flags |= O_RDWR;
else
flags |= O_RDONLY;
- scratch_chan = gdb_open_cloexec (filename.get (), flags, 0);
+ scratch_chan = gdb_open_cloexec (filename.get (), flags, 0).release ();
if (scratch_chan < 0)
perror_with_name (filename.get ());
{
/* Do it after the err msg */
/* FIXME: should be checking for errors from bfd_close (for one
- thing, on error it does not free all the storage associated
- with the bfd). */
+ thing, on error it does not free all the storage associated
+ with the bfd). */
error (_("\"%s\" is not a core dump: %s"),
filename.get (), bfd_errmsg (bfd_get_error ()));
}
core file. We don't do this unconditionally since an exec file
typically contains more information that helps us determine the
architecture than a core file. */
- if (!exec_bfd)
+ if (!current_program_space->exec_bfd ())
set_gdbarch_from_file (core_bfd);
- push_target (std::move (target_holder));
+ current_inferior ()->push_target (std::move (target_holder));
switch_to_no_thread ();
/* Build up thread list from BFD sections, and possibly set the
current thread to the .reg/NN section matching the .reg
section. */
- bfd_map_over_sections (core_bfd, add_to_thread_list,
- bfd_get_section_by_name (core_bfd, ".reg"));
+ asection *reg_sect = bfd_get_section_by_name (core_bfd, ".reg");
+ for (asection *sect : gdb_bfd_sections (core_bfd))
+ add_to_thread_list (sect, reg_sect);
if (inferior_ptid == null_ptid)
{
switch_to_thread (thread);
}
- if (exec_bfd == nullptr)
+ if (current_program_space->exec_bfd () == nullptr)
locate_exec_from_corefile_build_id (core_bfd, from_tty);
- post_create_inferior (target, from_tty);
+ post_create_inferior (from_tty);
/* Now go through the target stack looking for threads since there
may be a thread_stratum target loaded on top of target core by
/* Note that 'this' is dangling after this call. unpush_target
closes the target, and our close implementation deletes
'this'. */
- unpush_target (this);
+ inf->unpush_target (this);
/* Clear the register cache and the frame cache. */
registers_changed ();
print_section_info (&m_core_section_table, core_bfd);
}
\f
+/* Helper method for core_target::xfer_partial. */
+
+enum target_xfer_status
+core_target::xfer_memory_via_mappings (gdb_byte *readbuf,
+ const gdb_byte *writebuf,
+ ULONGEST offset, ULONGEST len,
+ ULONGEST *xfered_len)
+{
+ enum target_xfer_status xfer_status;
+
+ xfer_status = (section_table_xfer_memory_partial
+ (readbuf, writebuf,
+ offset, len, xfered_len,
+ m_core_file_mappings));
+
+ if (xfer_status == TARGET_XFER_OK || m_core_unavailable_mappings.empty ())
+ return xfer_status;
+
+ /* There are instances - e.g. when debugging within a docker
+ container using the AUFS storage driver - where the pathnames
+ obtained from the note section are incorrect. Despite the path
+ being wrong, just knowing the start and end addresses of the
+ mappings is still useful; we can attempt an access of the file
+ stratum constrained to the address ranges corresponding to the
+ unavailable mappings. */
+
+ ULONGEST memaddr = offset;
+ ULONGEST memend = offset + len;
+
+ for (const auto &mr : m_core_unavailable_mappings)
+ {
+ if (address_in_mem_range (memaddr, &mr))
+ {
+ if (!address_in_mem_range (memend, &mr))
+ len = mr.start + mr.length - memaddr;
+
+ xfer_status = this->beneath ()->xfer_partial (TARGET_OBJECT_MEMORY,
+ NULL,
+ readbuf,
+ writebuf,
+ offset,
+ len,
+ xfered_len);
+ break;
+ }
+ }
+
+ return xfer_status;
+}
+
enum target_xfer_status
core_target::xfer_partial (enum target_object object, const char *annex,
gdb_byte *readbuf, const gdb_byte *writebuf,
xfer_status = section_table_xfer_memory_partial
(readbuf, writebuf,
offset, len, xfered_len,
- m_core_section_table.sections,
- m_core_section_table.sections_end,
+ m_core_section_table,
has_contents_cb);
if (xfer_status == TARGET_XFER_OK)
return TARGET_XFER_OK;
core file provided mappings (e.g. from .note.linuxcore.file
or the like) as this should provide a more accurate
result. If not, check the stratum beneath us, which should
- be the file stratum. */
- if (m_core_file_mappings.sections != nullptr)
- xfer_status = section_table_xfer_memory_partial
- (readbuf, writebuf,
- offset, len, xfered_len,
- m_core_file_mappings.sections,
- m_core_file_mappings.sections_end);
+ be the file stratum.
+
+ We also check unavailable mappings due to Docker/AUFS driver
+ issues. */
+ if (!m_core_file_mappings.empty ()
+ || !m_core_unavailable_mappings.empty ())
+ {
+ xfer_status = xfer_memory_via_mappings (readbuf, writebuf, offset,
+ len, xfered_len);
+ }
else
xfer_status = this->beneath ()->xfer_partial (object, annex, readbuf,
writebuf, offset, len,
xfer_status = section_table_xfer_memory_partial
(readbuf, writebuf,
offset, len, xfered_len,
- m_core_section_table.sections,
- m_core_section_table.sections_end,
+ m_core_section_table,
no_contents_cb);
return xfer_status;
const struct target_desc *
core_target::read_description ()
{
+ /* If the core file contains a target description note then we will use
+ that in preference to anything else. */
+ bfd_size_type tdesc_note_size = 0;
+ struct bfd_section *tdesc_note_section
+ = bfd_get_section_by_name (core_bfd, ".gdb-tdesc");
+ if (tdesc_note_section != nullptr)
+ tdesc_note_size = bfd_section_size (tdesc_note_section);
+ if (tdesc_note_size > 0)
+ {
+ gdb::char_vector contents (tdesc_note_size + 1);
+ if (bfd_get_section_contents (core_bfd, tdesc_note_section,
+ contents.data (), (file_ptr) 0,
+ tdesc_note_size))
+ {
+ /* Ensure we have a null terminator. */
+ contents[tdesc_note_size] = '\0';
+ const struct target_desc *result
+ = string_read_description_xml (contents.data ());
+ if (result != nullptr)
+ return result;
+ }
+ }
+
if (m_core_gdbarch && gdbarch_core_read_description_p (m_core_gdbarch))
{
const struct target_desc *result;
return true;
}
+/* Get a pointer to the current core target. If not connected to a
+ core target, return NULL. */
+
+static core_target *
+get_current_core_target ()
+{
+ target_ops *proc_target = current_inferior ()->process_target ();
+ return dynamic_cast<core_target *> (proc_target);
+}
+
+/* Display file backed mappings from core file. */
+
+void
+core_target::info_proc_mappings (struct gdbarch *gdbarch)
+{
+ if (!m_core_file_mappings.empty ())
+ {
+ printf_filtered (_("Mapped address spaces:\n\n"));
+ if (gdbarch_addr_bit (gdbarch) == 32)
+ {
+ printf_filtered ("\t%10s %10s %10s %10s %s\n",
+ "Start Addr",
+ " End Addr",
+ " Size", " Offset", "objfile");
+ }
+ else
+ {
+ printf_filtered (" %18s %18s %10s %10s %s\n",
+ "Start Addr",
+ " End Addr",
+ " Size", " Offset", "objfile");
+ }
+ }
+
+ for (const target_section &tsp : m_core_file_mappings)
+ {
+ ULONGEST start = tsp.addr;
+ ULONGEST end = tsp.endaddr;
+ ULONGEST file_ofs = tsp.the_bfd_section->filepos;
+ const char *filename = bfd_get_filename (tsp.the_bfd_section->owner);
+
+ if (gdbarch_addr_bit (gdbarch) == 32)
+ printf_filtered ("\t%10s %10s %10s %10s %s\n",
+ paddress (gdbarch, start),
+ paddress (gdbarch, end),
+ hex_string (end - start),
+ hex_string (file_ofs),
+ filename);
+ else
+ printf_filtered (" %18s %18s %10s %10s %s\n",
+ paddress (gdbarch, start),
+ paddress (gdbarch, end),
+ hex_string (end - start),
+ hex_string (file_ofs),
+ filename);
+ }
+}
+
+/* Implement "maintenance print core-file-backed-mappings" command.
+
+ If mappings are loaded, the results should be similar to the
+ mappings shown by "info proc mappings". This command is mainly a
+ debugging tool for GDB developers to make sure that the expected
+ mappings are present after loading a core file. For Linux, the
+ output provided by this command will be very similar (if not
+ identical) to that provided by "info proc mappings". This is not
+ necessarily the case for other OSes which might provide
+ more/different information in the "info proc mappings" output. */
+
+static void
+maintenance_print_core_file_backed_mappings (const char *args, int from_tty)
+{
+ core_target *targ = get_current_core_target ();
+ if (targ != nullptr)
+ targ->info_proc_mappings (targ->core_gdbarch ());
+}
+
void _initialize_corelow ();
void
_initialize_corelow ()
{
add_target (core_target_info, core_target_open, filename_completer);
+ add_cmd ("core-file-backed-mappings", class_maintenance,
+ maintenance_print_core_file_backed_mappings,
+ _("Print core file's file-backed mappings."),
+ &maintenanceprintlist);
}