[PATCH 3/3] gdb/python: add Corefile.mapped_files method

Andrew Burgess
2025-09-02T16:07:09Z

Add a new Corefile.mapped_files method which returns a list of
gdb.CorefileMappedFile objects.

Each gdb.CorefileMappedFile object represents a file that was mapped
into the process when the core file was created.

A gdb.CorefileMappedFile has attributes:

+ filename -- A string, the name of the mapped file.
+ build_id -- A string or None, the build-id of the mapped file if
GDB could find it (None if not).
+ is_main_executable -- A boolean, True if this mapping is the main
executable.
+ regions -- A list containing the regions of this file that were
mapped into the process.

The 'regions' list is a list of gdb.CorefileMappedFileRegion objects,
each of these objects has the following attributes:

+ start -- the start address within the inferior.
+ end -- the end address within the inferior.
+ file_offset -- the offset within the mapped file for this mapping.

There are docs and tests.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=32844
---
gdb/NEWS | 13 +-
gdb/doc/python.texi | 61 ++++
gdb/python/py-corefile.c | 407 +++++++++++++++++++++++
gdb/testsuite/gdb.python/py-corefile.exp | 41 +++
gdb/testsuite/gdb.python/py-corefile.py | 144 ++++++++
5 files changed, 665 insertions(+), 1 deletion(-)
create mode 100644 gdb/testsuite/gdb.python/py-corefile.py

diff --git a/gdb/NEWS b/gdb/NEWS
index 98e322d7be6..791ba694370 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -178,7 +178,18 @@ info threads [-gid] [-stopped] [-running] [ID]...
has an attribute Corefile.filename, the file name of the loaded
core file, and a method Corefile.is_valid(), which returns False
when a Corefile object becomes invalid (e.g. when the core file
- is unloaded).
+ is unloaded). There is also Corefile.mapped_files() which
+ returns a list of CorefileMappedFile objects, representing files
+ that were mapped into the core file when it was created.
+
+ ** New gdb.CorefileMappedFile type representing a file that was
+ mapped when the core file was created. Has read-only attributes
+ filename (string), build_id (string), is_main_executable
+ (boolean), and regions (list of CorefileMappedFileRegion objects).
+
+ ** New gdb.CorefileMappedFileRegion type, which represents a mapped
+ region of a file (see gdb.CorefileMappedFile above). Has
+ read-only attributes start, end, and file_offset.

** New Inferior.corefile attribute. This read only attribute
contains the gdb.Corefile object if a core file is loaded into
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 3a6022f22e7..558787ff3db 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -8648,12 +8648,73 @@ Core Files In Python
accessed.
@end defun

+@defun Corefile.mapped_files ()
+Return a list of @code{gdb.CorefileMappedFile} (see below) objects
+representing files that were mapped into the process when the core
+file was created. This information is read from the @samp{NT_FILE}
+core file note on Linux. Not every target supports accessing this
+information, for targets without support, an empty list will be
+returned.
+@end defun
+
One may add arbitrary attributes to @code{gdb.Corefile} objects in the
usual Python way. This is useful if, for example, one needs to do
some extra record keeping associated with the corefile.
@xref{choosing attribute names}, for guidance on selecting a suitable
name for new attributes.

+The @code{Corefile.mapped_files ()} method returns a list of
+@code{gdb.CorefileMappedFile} objects. Each of these objects
+represents a file that was fully, or partially, mapped into the
+processes address space when the core file was created.
+
+A @code{gdb.CorefileMappedFile} object has the following attributes:
+
+@defvar CorefileMappedFile.filename
+This read only attribute contains a non-empty string, the file name of
+the mapped file.
+@end defvar
+
+@defvar CorefileMappedFile.build_id
+This read only attribute contains a non-empty string or @code{None}.
+This is the build-id of the mapped file extracted from the core file,
+or @code{None} if there was no build-id, or @value{GDBN} was unable to
+extract the build-id.
+@end defvar
+
+@defvar CorefileMappedFile.is_main_executable
+This read only attribute is @code{True} if @value{GDBN} believes this
+mapping represents the main executable for which this core file was
+created. This will be @code{False} for all other mappings.
+@end defvar
+
+@defvar CorefileMappedFile.regions
+This read only attribute contains a list of
+@code{gdb.CorefileMappedFileRegion} objects. Each of these objects
+describes a region of the file that was mapped into the process when
+the core file was created, further details are given below.
+@end defvar
+
+The @code{gdb.CorefileMappedFileRegion} object describes which part of
+a file that was mapped into a process when the core file was created.
+
+A @code{gdb.CorefileMappedFile} object has the following attributes:
+
+@defvar CorefileMappedFileRegion.start
+This read only attribute contains the start address of this mapping
+within the inferior.
+@end defvar
+
+@defvar CorefileMappedFileRegion.end
+This read only attribute contains end address of this mapping within
+the inferior.
+@end defvar
+
+@defvar CorefileMappedFileRegion.file_offset
+This read only attribute contains the offset within the mapped file
+for this mapping.
+@end defvar
+
@node Python Auto-loading
@subsection Python Auto-loading
@cindex Python auto-loading
diff --git a/gdb/python/py-corefile.c b/gdb/python/py-corefile.c
index 25fdb31963c..8d941814d59 100644
--- a/gdb/python/py-corefile.c
+++ b/gdb/python/py-corefile.c
@@ -21,6 +21,8 @@
#include "progspace.h"
#include "observable.h"
#include "inferior.h"
+#include "gdbcore.h"
+#include "gdbsupport/rsp-low.h"

/* A gdb.Corefile object. */

@@ -37,11 +39,61 @@ struct corefile_object
/* Dictionary holding user-added attributes. This is the __dict__
attribute of the object. This is an owning reference. */
PyObject *dict;
+
+ /* A List of gdb.CorefileMappedFile objects. This list is only created
+ the first time the user calls gdb.Corefile.mapped_files(), the result
+ is cached here. If this pointer is not NULL then this is an owning
+ pointer (i.e. this owns a reference to the List). */
+ PyObject *mapped_files;
};

extern PyTypeObject corefile_object_type
CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("corefile_object");

+/* A gdb.CorefileMapped object. */
+
+struct corefile_mapped_file_object
+{
+ PyObject_HEAD
+
+ /* The name of a file that was mapped when the core file was created.
+ This is a 'str' object. */
+ PyObject *filename;
+
+ /* The build-id of a file that was mapped when the core file was
+ created. This is either a 'str' if the file had a build-id, or
+ 'None' if there was no build-id for this file. */
+ PyObject *build_id;
+
+ /* A List of gdb.CorefileMappedFileRegion objects. */
+ PyObject *regions;
+
+ /* True if this represents the main executable from which the core file
+ was created. */
+ bool is_main_exec_p;
+};
+
+extern PyTypeObject corefile_mapped_file_object_type
+ CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("corefile_mapped_file_object");
+
+/* A gdb.CorefileMappedFileRegion object. */
+
+struct corefile_mapped_file_region_object
+{
+ PyObject_HEAD
+
+ /* The start and end addresses for this mapping, these are addresses
+ within the inferior's address space. */
+ CORE_ADDR start;
+ CORE_ADDR end;
+
+ /* The offset within the mapped file for this mapping. */
+ ULONGEST file_offset;
+};
+
+extern PyTypeObject corefile_mapped_file_region_object_type
+ CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("corefile_mapped_file_region_object");
+
/* Clear the inferior pointer in a Corefile object OBJ when an inferior is
deleted. */

@@ -90,6 +142,7 @@ gdbpy_core_file_from_inferior (inferior *inf)
if (object->dict == nullptr)
return nullptr;
object->inferior = inf;
+ object->mapped_files = nullptr;
cfpy_inferior_corefile_data_key.set (inf, object.get ());
result = (PyObject *) object.release ();
}
@@ -155,6 +208,121 @@ cfpy_is_valid (PyObject *self, PyObject *args)
Py_RETURN_TRUE;
}

+/* Implement gdb.Corefile.mapped_files (). Return a List of
+ gdb.CorefileMappedFile objects. The list is created the first time
+ this method is called, and then cached within the gdb.Corefile object,
+ future calls just return a reference to the same list. */
+
+static PyObject *
+cfpy_mapped_files (PyObject *self, PyObject *args)
+{
+ corefile_object *obj = (corefile_object *) self;
+
+ CFPY_REQUIRE_VALID (obj);
+
+ /* If we have already created the List then just return another reference
+ to the existing list. */
+ if (obj->mapped_files != nullptr)
+ {
+ Py_INCREF (obj->mapped_files);
+ return obj->mapped_files;
+ }
+
+ /* Get all the mapping data from GDB. */
+ std::vector<core_mapped_file> mapped_files;
+ try
+ {
+ mapped_files
+ = gdb_read_core_file_mappings (obj->inferior->arch (),
+ current_program_space->core_bfd ());
+ }
+ catch (const gdb_exception &except)
+ {
+ return gdbpy_handle_gdb_exception (nullptr, except);
+ }
+
+ /* Create a new list to hold the results. */
+ gdbpy_ref<> list (PyList_New (0));
+ if (list == nullptr)
+ return nullptr;
+
+ /* Create each gdb.CorefileMappedFile object. */
+ for (const core_mapped_file &file : mapped_files)
+ {
+ /* The filename 'str' object. */
+ gdbpy_ref<> filename
+ = host_string_to_python_string (file.filename.c_str ());
+ if (filename == nullptr)
+ return nullptr;
+
+ /* The build-id object. Either a 'str' or 'None'. */
+ gdbpy_ref<> build_id;
+ if (file.build_id != nullptr)
+ {
+ std::string hex_form = bin2hex (file.build_id->data,
+ file.build_id->size);
+
+ build_id
+ = host_string_to_python_string (hex_form.c_str ());
+ if (build_id == nullptr)
+ return nullptr;
+ }
+ else
+ build_id = gdbpy_ref<>::new_reference (Py_None);
+
+ /* List to hold all the gdb.CorefileMappedFileRegion objects. */
+ gdbpy_ref<> regions (PyList_New (0));
+ if (regions == nullptr)
+ return nullptr;
+
+ /* Create all the gdb.CorefileMappedFileRegion objects. */
+ for (const core_mapped_file::region &r : file.regions)
+ {
+ /* Actually create the object. */
+ gdbpy_ref<corefile_mapped_file_region_object> region_obj
+ ((corefile_mapped_file_region_object *) PyObject_New
+ (corefile_mapped_file_region_object,
+ &corefile_mapped_file_region_object_type));
+ if (region_obj == nullptr)
+ return nullptr;
+
+ /* Initialise the object. */
+ region_obj->start = r.start;
+ region_obj->end = r.end;
+ region_obj->file_offset = r.file_ofs;
+
+ /* Add to the gdb.CorefileMappedFileRegion list. */
+ if (PyList_Append (regions.get (),
+ (PyObject *) region_obj.get ()) < 0)
+ return nullptr;
+ }
+
+ /* Actually create the gdb.CorefileMappedFile object. */
+ gdbpy_ref<corefile_mapped_file_object> entry
+ ((corefile_mapped_file_object *) PyObject_New
+ (corefile_mapped_file_object, &corefile_mapped_file_object_type));
+ if (entry == nullptr)
+ return nullptr;
+
+ /* Initialise the object. */
+ entry->filename = filename.release ();
+ entry->build_id = build_id.release ();
+ entry->regions = regions.release ();
+ entry->is_main_exec_p = file.is_main_exec;
+
+ /* Add to the gdb.CorefileMappedFile list. */
+ if (PyList_Append (list.get (), (PyObject *) entry.get ()) < 0)
+ return nullptr;
+ }
+
+ /* No errors. Move the reference currently in LIST into the Corefile
+ object itself. Then create a new reference and hand this back to the
+ user. */
+ obj->mapped_files = list.release ();
+ Py_INCREF (obj->mapped_files);
+ return obj->mapped_files;
+}
+
/* Callback from gdb::observers::core_file_changed. The core file in
PSPACE has been changed. */

@@ -195,6 +363,7 @@ cfpy_dealloc (PyObject *obj)
gdb_assert (corefile->inferior == nullptr);

Py_XDECREF (corefile->dict);
+ Py_XDECREF (corefile->mapped_files);

Py_TYPE (obj)->tp_free (obj);
}
@@ -219,6 +388,126 @@ cfpy_repr (PyObject *self)

\f

+/* Called when a gdb.CorefileMappedFile is destroyed. */
+
+static void
+cfmfpy_dealloc (PyObject *obj)
+{
+ corefile_mapped_file_object *mapped_file
+ = (corefile_mapped_file_object *) obj;
+
+ Py_XDECREF (mapped_file->filename);
+ Py_XDECREF (mapped_file->build_id);
+ Py_XDECREF (mapped_file->regions);
+
+ Py_TYPE (obj)->tp_free (obj);
+}
+
+/* Read the gdb.CorefileMappedFile.filename attribute. */
+
+static PyObject *
+cfmfpy_get_filename (PyObject *self, void *closure)
+{
+ corefile_mapped_file_object *obj
+ = (corefile_mapped_file_object *) self;
+
+ gdb_assert (obj->filename != nullptr);
+
+ Py_INCREF (obj->filename);
+ return obj->filename;
+}
+
+/* Read the gdb.CorefileMappedFile.build_id attribute. */
+
+static PyObject *
+cfmfpy_get_build_id (PyObject *self, void *closure)
+{
+ corefile_mapped_file_object *obj
+ = (corefile_mapped_file_object *) self;
+
+ gdb_assert (obj->build_id != nullptr);
+
+ Py_INCREF (obj->build_id);
+ return obj->build_id;
+}
+
+/* Read the gdb.CorefileMappedFile.regions attribute. */
+
+static PyObject *
+cfmfpy_get_regions (PyObject *self, void *closure)
+{
+ corefile_mapped_file_object *obj
+ = (corefile_mapped_file_object *) self;
+
+ gdb_assert (obj->regions != nullptr);
+
+ Py_INCREF (obj->regions);
+ return obj->regions;
+}
+
+/* Read the gdb.CorefileMappedFile.is_main_executable attribute. */
+
+static PyObject *
+cfmf_is_main_exec (PyObject *self, void *closure)
+{
+ corefile_mapped_file_object *obj
+ = (corefile_mapped_file_object *) self;
+
+ if (obj->is_main_exec_p)
+ Py_RETURN_TRUE;
+ else
+ Py_RETURN_FALSE;
+}
+
+\f
+
+/* Read the gdb.CorefileMappedFileRegion.start attribute. */
+
+static PyObject *
+cfmfrpy_get_start (PyObject *self, void *closure)
+{
+ corefile_mapped_file_region_object *obj
+ = (corefile_mapped_file_region_object *) self;
+
+ gdbpy_ref<> start (gdb_py_object_from_ulongest (obj->start));
+ if (start == nullptr)
+ return nullptr;
+
+ return start.release ();
+}
+
+/* Read the gdb.CorefileMappedFileRegion.end attribute. */
+
+static PyObject *
+cfmfrpy_get_end (PyObject *self, void *closure)
+{
+ corefile_mapped_file_region_object *obj
+ = (corefile_mapped_file_region_object *) self;
+
+ gdbpy_ref<> end (gdb_py_object_from_ulongest (obj->end));
+ if (end == nullptr)
+ return nullptr;
+
+ return end.release ();
+}
+
+/* Read the gdb.CorefileMappedFileRegion.file_offset attribute. */
+
+static PyObject *
+cfmfrpy_get_file_offset (PyObject *self, void *closure)
+{
+ corefile_mapped_file_region_object *obj
+ = (corefile_mapped_file_region_object *) self;
+
+ gdbpy_ref<> file_offset (gdb_py_object_from_ulongest (obj->file_offset));
+ if (file_offset == nullptr)
+ return nullptr;
+
+ return file_offset.release ();
+}
+
+\f
+
static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
gdbpy_initialize_corefile (void)
{
@@ -228,6 +517,12 @@ gdbpy_initialize_corefile (void)
if (gdbpy_type_ready (&corefile_object_type) < 0)
return -1;

+ if (gdbpy_type_ready (&corefile_mapped_file_object_type) < 0)
+ return -1;
+
+ if (gdbpy_type_ready (&corefile_mapped_file_region_object_type) < 0)
+ return -1;
+
return 0;
}

@@ -247,6 +542,10 @@ static PyMethodDef corefile_object_methods[] =
{ "is_valid", cfpy_is_valid, METH_NOARGS,
"is_valid () -> Boolean.\n\
Return true if this Corefile is valid, false if not." },
+ { "mapped_files", cfpy_mapped_files, METH_NOARGS,
+ "mapped_files () -> List of mapping tuples.\n\
+Return a list of tuples. Each tuple represents a mapping from the\
+core file." },
{ nullptr }
};

@@ -291,3 +590,111 @@ PyTypeObject corefile_object_type =
0, /* tp_alloc */
0, /* tp_new */
};
+
+static gdb_PyGetSetDef corefile_mapped_file_object_getset[] =
+{
+ { "filename", cfmfpy_get_filename, nullptr,
+ "The filename of a CorefileMappedFile object.", nullptr },
+ { "build_id", cfmfpy_get_build_id, nullptr,
+ "The build-id of a CorefileMappedFile object or None.", nullptr },
+ { "regions", cfmfpy_get_regions, nullptr,
+ "The list of regions from a CorefileMappedFile object.", nullptr },
+ { "is_main_executable", cfmf_is_main_exec, nullptr,
+ "True for the main executable mapping, otherwise False.", nullptr },
+ { nullptr }
+};
+
+PyTypeObject corefile_mapped_file_object_type =
+{
+ PyVarObject_HEAD_INIT (NULL, 0)
+ "gdb.CorefileMappedFile", /*tp_name*/
+ sizeof (corefile_mapped_file_object), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ cfmfpy_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ "GDB corefile mapped file object", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ corefile_mapped_file_object_getset, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+static gdb_PyGetSetDef corefile_mapped_file_region_object_getset[] =
+{
+ { "start", cfmfrpy_get_start, nullptr,
+ "The start address of a CorefileMappedFileRegion object.", nullptr },
+ { "end", cfmfrpy_get_end, nullptr,
+ "The end address of a CorefileMappedFileRegion object.", nullptr },
+ { "file_offset", cfmfrpy_get_file_offset, nullptr,
+ "The file offset of a CorefileMappedFileRegion object.", nullptr },
+ { nullptr }
+};
+
+PyTypeObject corefile_mapped_file_region_object_type =
+{
+ PyVarObject_HEAD_INIT (NULL, 0)
+ "gdb.CorefileMappedFileRegion", /*tp_name*/
+ sizeof (corefile_mapped_file_region_object), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ 0, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ "GDB corefile mapped file region object", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ corefile_mapped_file_region_object_getset, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
diff --git a/gdb/testsuite/gdb.python/py-corefile.exp b/gdb/testsuite/gdb.python/py-corefile.exp
index a65c7c9b8b5..382843b7572 100644
--- a/gdb/testsuite/gdb.python/py-corefile.exp
+++ b/gdb/testsuite/gdb.python/py-corefile.exp
@@ -167,3 +167,44 @@ with_test_prefix "remove second inferior" {
gdb_test "python print(core1.is_valid())" "^True" \
"check inferior 1 core file is still valid"
}
+
+# Test the Corefile.mapped_files() API. The Python script that is
+# sourced here implements 'info proc mappings' in Python using the
+# mapped_files API. The output from the built-in command, and the
+# Python command should be identical.
+with_test_prefix "test mapped files data" {
+ clean_restart
+
+ set remote_python_file \
+ [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
+
+ # Load the Python script into GDB.
+ gdb_test "source $remote_python_file" "^Success" \
+ "source python script"
+
+ # Load the core file.
+ gdb_test "core-file $corefile" ".*" \
+ "load core file"
+
+ # Two files to write the output to.
+ set out_1 [standard_output_file ${gdb_test_file_name}-out-1.txt]
+ set out_2 [standard_output_file ${gdb_test_file_name}-out-2.txt]
+
+ # Run the built-in command, then the new Python command, capture
+ # the output.
+ gdb_test "pipe info proc mappings | tee $out_1" ".*" \
+ "capture built-in mappings output"
+ gdb_test "pipe info proc py-mappings | tee $out_2" ".*" \
+ "capture Python based mappings data"
+
+ # Check the output is identical.
+ gdb_test "shell diff -s $out_1 $out_2" \
+ "Files \[^\r\n\]+-out-1.txt and \[^\r\n\]+-out-2.txt are identical" \
+ "diff input and output one"
+
+ # Check build-ids within the core file mapping data.
+ gdb_test "check-build-ids" "^PASS"
+
+ # Check the is_main_executable flag in the mapping data.
+ gdb_test "check-main-executable" "^PASS"
+}
diff --git a/gdb/testsuite/gdb.python/py-corefile.py b/gdb/testsuite/gdb.python/py-corefile.py
new file mode 100644
index 00000000000..cffd037a23b
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-corefile.py
@@ -0,0 +1,144 @@
+# Copyright (C) 2025 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/>.
+
+import pathlib
+
+
+class Mapping:
+ def __init__(self, mapping, region):
+ self._mapping = mapping
+ self._region = region
+
+ @property
+ def start(self):
+ return self._region.start
+
+ @property
+ def end(self):
+ return self._region.end
+
+ @property
+ def offset(self):
+ return self._region.file_offset
+
+ @property
+ def filename(self):
+ return self._mapping.filename
+
+
+def info_proc_mappings():
+ print("Mapped address spaces:")
+ print("")
+ format_str = "%-18s %-18s %-18s %-18s %s "
+ print(format_str % ("Start Addr", "End Addr", "Size", "Offset", "File"))
+
+ core = gdb.selected_inferior().corefile
+ mappings = core.mapped_files()
+
+ result = []
+ for m in mappings:
+ for r in m.regions:
+ result.append(Mapping(m, r))
+
+ result.sort(key=lambda x: x.start)
+ for r in result:
+ sz = r.end - r.start
+ print(
+ format_str
+ % (
+ "0x%016x" % r.start,
+ "0x%016x" % r.end,
+ "0x%-16x" % sz,
+ "0x%-16x" % r.offset,
+ "%s" % r.filename,
+ )
+ )
+
+
+class InfoProcPyMappings(gdb.Command):
+ def __init__(self):
+ gdb.Command.__init__(self, "info proc py-mappings", gdb.COMMAND_DATA)
+
+ def invoke(self, args, from_tty):
+ info_proc_mappings()
+
+
+InfoProcPyMappings()
+
+
+class CheckBuildIds(gdb.Command):
+ def __init__(self):
+ gdb.Command.__init__(self, "check-build-ids", gdb.COMMAND_DATA)
+
+ def invoke(self, args, from_tty):
+ inf = gdb.selected_inferior()
+ objfiles = inf.progspace.objfiles()
+
+ path_to_build_id = {}
+
+ for o in objfiles:
+ if not o.is_file or o.build_id is None:
+ continue
+ p = pathlib.Path(o.filename).resolve()
+ b = o.build_id
+ path_to_build_id[p] = b
+
+ count = 0
+ core_mapped_files = inf.corefile.mapped_files()
+ for m in core_mapped_files:
+ p = pathlib.Path(m.filename).resolve()
+ b = m.build_id
+
+ if p in path_to_build_id:
+ count += 1
+ assert path_to_build_id[p] == b, "build-id mismatch for %s" % p
+
+ assert count > 0, "no mapped files checked"
+
+ print("PASS")
+
+
+CheckBuildIds()
+
+
+class CheckMainExec(gdb.Command):
+ def __init__(self):
+ gdb.Command.__init__(self, "check-main-executable", gdb.COMMAND_DATA)
+
+ def invoke(self, args, from_tty):
+ inf = gdb.selected_inferior()
+ pspace = inf.progspace
+ exec_filename = pathlib.Path(pspace.executable_filename).resolve()
+
+ count = 0
+ core_mapped_files = inf.corefile.mapped_files()
+ for m in core_mapped_files:
+ if not m.is_main_executable:
+ continue
+
+ p = pathlib.Path(m.filename).resolve()
+
+ count += 1
+ assert exec_filename == p, "main exec filename mismatch"
+
+ assert count == 1, "invalid main executable count"
+
+ print("PASS")
+
+
+CheckMainExec()
+
+
+print("Success")
--
2.47.1