#include "exec.h"
#include "readline/tilde.h"
#include "solib.h"
+#include "solist.h"
#include "filenames.h"
#include "progspace.h"
#include "objfiles.h"
#include "gdbsupport/filestuff.h"
#include "build-id.h"
#include "gdbsupport/pathstuff.h"
+#include <unordered_map>
#ifndef O_LARGEFILE
#define O_LARGEFILE 0
targets. */
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 {};
+
+ /* Build m_core_file_mappings. Called from the constructor. */
+ void build_file_mappings ();
+
/* FIXME: kettenis/20031023: Eventually this field should
disappear. */
struct gdbarch *m_core_gdbarch = NULL;
&m_core_section_table.sections_end))
error (_("\"%s\": Can't find sections: %s"),
bfd_get_filename (core_bfd), bfd_errmsg (bfd_get_error ()));
+
+ 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.
+
+ For each unique path in the note, we'll open a BFD with a bfd
+ target of "binary". This is an unstructured bfd target upon which
+ we'll impose a structure from the mappings in the architecture-specific
+ mappings note. A BFD section is allocated and initialized for each
+ file-backed mapping.
+
+ We take care to not share already open bfds with other parts of
+ GDB; in particular, we don't want to add new sections to existing
+ BFDs. We do, however, ensure that the BFDs that we allocate here
+ will go away (be deallocated) when the core target is detached. */
+
+void
+core_target::build_file_mappings ()
+{
+ std::unordered_map<std::string, struct bfd *> bfd_map;
+
+ /* 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)
+ {
+ 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)
+ {
+ /* Architecture-specific read_core_mapping methods are expected to
+ weed out non-file-backed mappings. */
+ gdb_assert (filename != nullptr);
+
+ struct bfd *bfd = bfd_map[filename];
+ if (bfd == nullptr)
+ {
+ /* Use exec_file_find() to do sysroot expansion. It'll
+ also strip the potential sysroot "target:" prefix. If
+ there is no sysroot, an equivalent (possibly more
+ canonical) pathname will be provided. */
+ gdb::unique_xmalloc_ptr<char> expanded_fname
+ = exec_file_find (filename, NULL);
+ if (expanded_fname == nullptr)
+ {
+ warning (_("Can't open file %s during file-backed mapping "
+ "note processing"),
+ expanded_fname.get ());
+ return;
+ }
+
+ bfd = bfd_map[filename] = bfd_openr (expanded_fname.get (),
+ "binary");
+
+ if (bfd == nullptr || !bfd_check_format (bfd, bfd_object))
+ {
+ /* 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
+ file was removed in between checking for its
+ existence during the expansion in exec_file_find()
+ and the calls to bfd_openr() / bfd_check_format().
+ Output both the path from the core file note along
+ with its expansion to make debugging this problem
+ easier. */
+ warning (_("Can't open file %s which was expanded to %s "
+ "during file-backed mapping note processing"),
+ filename, expanded_fname.get ());
+ if (bfd != nullptr)
+ bfd_close (bfd);
+ return;
+ }
+ /* Ensure that the bfd will be closed when core_bfd is closed.
+ This can be checked before/after a core file detach via
+ "maint info bfds". */
+ gdb_bfd_record_inclusion (core_bfd, bfd);
+ }
+
+ /* Make new BFD section. All sections have the same name,
+ which is permitted by bfd_make_section_anyway(). */
+ asection *sec = bfd_make_section_anyway (bfd, "load");
+ if (sec == nullptr)
+ error (_("Can't make section"));
+ sec->filepos = file_ofs;
+ bfd_set_section_flags (sec, SEC_READONLY | SEC_HAS_CONTENTS);
+ bfd_set_section_size (sec, end - start);
+ bfd_set_section_vma (sec, start);
+ bfd_set_section_lma (sec, start);
+ 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;
+ });
}
static void add_to_thread_list (bfd *, asection *, void *);
if (xfer_status == TARGET_XFER_OK)
return TARGET_XFER_OK;
- /* Now check the stratum beneath us; this should be file_stratum. */
- xfer_status = this->beneath ()->xfer_partial (object, annex, readbuf,
- writebuf, offset, len,
- xfered_len);
+ /* Check file backed mappings. If they're available, use
+ 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);
+ else
+ xfer_status = this->beneath ()->xfer_partial (object, annex, readbuf,
+ writebuf, offset, len,
+ xfered_len);
if (xfer_status == TARGET_XFER_OK)
return TARGET_XFER_OK;
}
}
-/* Implement "info proc mappings" for a corefile. */
+/* Implementation of `gdbarch_read_core_file_mappings', as defined in
+ gdbarch.h.
+
+ This function reads the NT_FILE note (which BFD turns into the
+ section ".note.linuxcore.file"). The format of this note / section
+ is described as follows in the Linux kernel sources in
+ fs/binfmt_elf.c:
+
+ long count -- how many files are mapped
+ long page_size -- units for file_ofs
+ array of [COUNT] elements of
+ long start
+ long end
+ long file_ofs
+ followed by COUNT filenames in ASCII: "FILE1" NUL "FILE2" NUL...
+
+ CBFD is the BFD of the core file.
+
+ PRE_LOOP_CB is the callback function to invoke prior to starting
+ the loop which processes individual entries. This callback will
+ only be executed after the note has been examined in enough
+ detail to verify that it's not malformed in some way.
+
+ LOOP_CB is the callback function that will be executed once
+ for each mapping. */
static void
-linux_core_info_proc_mappings (struct gdbarch *gdbarch, const char *args)
+linux_read_core_file_mappings (struct gdbarch *gdbarch,
+ struct bfd *cbfd,
+ gdb::function_view<void (ULONGEST count)>
+ pre_loop_cb,
+ gdb::function_view<void (int num,
+ ULONGEST start,
+ ULONGEST end,
+ ULONGEST file_ofs,
+ const char *filename,
+ const void *other)>
+ loop_cb)
{
- asection *section;
- ULONGEST count, page_size;
- unsigned char *descdata, *filenames, *descend;
- size_t note_size;
- unsigned int addr_size_bits, addr_size;
- struct gdbarch *core_gdbarch = gdbarch_from_bfd (core_bfd);
- /* We assume this for reading 64-bit core files. */
+ /* Ensure that ULONGEST is big enough for reading 64-bit core files. */
gdb_static_assert (sizeof (ULONGEST) >= 8);
- section = bfd_get_section_by_name (core_bfd, ".note.linuxcore.file");
- if (section == NULL)
- {
- warning (_("unable to find mappings in core file"));
- return;
- }
+ /* It's not required that the NT_FILE note exists, so return silently
+ if it's not found. Beyond this point though, we'll complain
+ if problems are found. */
+ asection *section = bfd_get_section_by_name (cbfd, ".note.linuxcore.file");
+ if (section == nullptr)
+ return;
- addr_size_bits = gdbarch_addr_bit (core_gdbarch);
- addr_size = addr_size_bits / 8;
- note_size = bfd_section_size (section);
+ unsigned int addr_size_bits = gdbarch_addr_bit (gdbarch);
+ unsigned int addr_size = addr_size_bits / 8;
+ size_t note_size = bfd_section_size (section);
if (note_size < 2 * addr_size)
- error (_("malformed core note - too short for header"));
+ {
+ warning (_("malformed core note - too short for header"));
+ return;
+ }
- gdb::def_vector<unsigned char> contents (note_size);
+ gdb::def_vector<gdb_byte> contents (note_size);
if (!bfd_get_section_contents (core_bfd, section, contents.data (),
0, note_size))
- error (_("could not get core note contents"));
+ {
+ warning (_("could not get core note contents"));
+ return;
+ }
- descdata = contents.data ();
- descend = descdata + note_size;
+ gdb_byte *descdata = contents.data ();
+ char *descend = (char *) descdata + note_size;
if (descdata[note_size - 1] != '\0')
- error (_("malformed note - does not end with \\0"));
+ {
+ warning (_("malformed note - does not end with \\0"));
+ return;
+ }
- count = bfd_get (addr_size_bits, core_bfd, descdata);
+ ULONGEST count = bfd_get (addr_size_bits, core_bfd, descdata);
descdata += addr_size;
- page_size = bfd_get (addr_size_bits, core_bfd, descdata);
+ ULONGEST page_size = bfd_get (addr_size_bits, core_bfd, descdata);
descdata += addr_size;
if (note_size < 2 * addr_size + count * 3 * addr_size)
- error (_("malformed note - too short for supplied file count"));
-
- 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");
+ warning (_("malformed note - too short for supplied file count"));
+ return;
}
- filenames = descdata + count * 3 * addr_size;
- while (--count > 0)
+ char *filenames = (char *) descdata + count * 3 * addr_size;
+
+ /* Make sure that the correct number of filenames exist. Complain
+ if there aren't enough or are too many. */
+ char *f = filenames;
+ for (int i = 0; i < count; i++)
{
- ULONGEST start, end, file_ofs;
+ if (f >= descend)
+ {
+ warning (_("malformed note - filename area is too small"));
+ return;
+ }
+ f += strnlen (f, descend - f) + 1;
+ }
+ /* Complain, but don't return early if the filename area is too big. */
+ if (f != descend)
+ warning (_("malformed note - filename area is too big"));
- if (filenames == descend)
- error (_("malformed note - filenames end too early"));
+ pre_loop_cb (count);
- start = bfd_get (addr_size_bits, core_bfd, descdata);
+ for (int i = 0; i < count; i++)
+ {
+ ULONGEST start = bfd_get (addr_size_bits, core_bfd, descdata);
descdata += addr_size;
- end = bfd_get (addr_size_bits, core_bfd, descdata);
+ ULONGEST end = bfd_get (addr_size_bits, core_bfd, descdata);
descdata += addr_size;
- file_ofs = bfd_get (addr_size_bits, core_bfd, descdata);
+ ULONGEST file_ofs
+ = bfd_get (addr_size_bits, core_bfd, descdata) * page_size;
descdata += addr_size;
+ char * filename = filenames;
+ filenames += strlen ((char *) filenames) + 1;
- file_ofs *= page_size;
-
- 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),
- filenames);
- else
- printf_filtered (" %18s %18s %10s %10s %s\n",
- paddress (gdbarch, start),
- paddress (gdbarch, end),
- hex_string (end - start),
- hex_string (file_ofs),
- filenames);
-
- filenames += 1 + strlen ((char *) filenames);
+ loop_cb (i, start, end, file_ofs, filename, nullptr);
}
}
+/* Implement "info proc mappings" for a corefile. */
+
+static void
+linux_core_info_proc_mappings (struct gdbarch *gdbarch, const char *args)
+{
+ linux_read_core_file_mappings (gdbarch, core_bfd,
+ [=] (ULONGEST count)
+ {
+ 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");
+ }
+ },
+ [=] (int num, ULONGEST start, ULONGEST end, ULONGEST file_ofs,
+ const char *filename, const void *other)
+ {
+ 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 "info proc" for a corefile. */
static void
set_gdbarch_info_proc (gdbarch, linux_info_proc);
set_gdbarch_core_info_proc (gdbarch, linux_core_info_proc);
set_gdbarch_core_xfer_siginfo (gdbarch, linux_core_xfer_siginfo);
+ set_gdbarch_read_core_file_mappings (gdbarch, linux_read_core_file_mappings);
set_gdbarch_find_memory_regions (gdbarch, linux_find_memory_regions);
set_gdbarch_make_corefile_notes (gdbarch, linux_make_corefile_notes);
set_gdbarch_has_shared_address_space (gdbarch,