[RFC PATCH 3/3] gdb: Add support for writing overlay managers in python

Craig Blackmore craig.blackmore@embecosm.com
Mon May 9 21:51:24 GMT 2022


Adds gdb_py_overlay_manager class which overrides most of the
public gdb_overlay_manager methods, deferring to python code to
implement them.

This allows the overlay manager support to be written in a python script
which implements the required hooks and likely contains internal details
of a specific overlay system. The script can then be shipped alongside
the overlay system.

GDB python has no concept of obj_section, therefore, it is not currently
possible to support python overlay managers that need to manipulate
sections.
---
 gdb/Makefile.in              |   1 +
 gdb/python/py-overlay.c      | 409 +++++++++++++++++++++++++++++++++++
 gdb/python/python-internal.h |   3 +
 gdb/python/python.c          |   4 +-
 4 files changed, 416 insertions(+), 1 deletion(-)
 create mode 100644 gdb/python/py-overlay.c

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index d86ba85ccbc..2662958d017 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -412,6 +412,7 @@ SUBDIR_PYTHON_SRCS = \
 	python/py-micmd.c \
 	python/py-newobjfileevent.c \
 	python/py-objfile.c \
+	python/py-overlay.c \
 	python/py-param.c \
 	python/py-prettyprint.c \
 	python/py-progspace.c \
diff --git a/gdb/python/py-overlay.c b/gdb/python/py-overlay.c
new file mode 100644
index 00000000000..5ad7dbcab63
--- /dev/null
+++ b/gdb/python/py-overlay.c
@@ -0,0 +1,409 @@
+/* Python interface to overlay manager
+
+   Copyright (C) 2022 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 "defs.h"
+#include "python-internal.h"
+#include "python.h"
+#include "overlay.h"
+
+/* Constants for method names defined on a Python class.  */
+#define PC_IN_UNMAPPED_RANGE_METHOD "pc_in_unmapped_range"
+#define PC_IN_MAPPED_RANGE_METHOD "pc_in_mapped_range"
+#define OVERLAY_UNMAPPED_ADDRESS_METHOD "overlay_unmapped_address"
+#define OVERLAY_MAPPED_ADDRESS_METHOD "overlay_mapped_address"
+#define SYMBOL_OVERLAYED_ADDRESS_METHOD "symbol_overlayed_address"
+#define HANDLE_OVERLAY_BP_EVENT_METHOD "handle_overlay_bp_event"
+#define OVERLAY_UPDATE_METHOD "overlay_update"
+#define OVERLAY_ADDRESS_FOR_SAL_METHOD "overlay_address_for_sal"
+#define LIST_OVERLAYS_COMMAND_METHOD "list_overlays_command"
+#define MAP_OVERLAY_COMMAND_METHOD "map_overlay_command"
+#define UNMAP_OVERLAY_COMMAND_METHOD "unmap_overlay_command"
+
+/* Declare.  */
+struct gdbpy_ovly_mgr_object;
+
+/* An implementation of an overlay manager that delegates out to Python
+   code that the user can easily override.  */
+
+class gdb_py_overlay_manager : public gdb_overlay_manager
+{
+public:
+  /* Constructor.  */
+  gdb_py_overlay_manager (gdbpy_ovly_mgr_object *obj)
+    : gdb_overlay_manager (),
+      m_obj (obj)
+  {
+    Py_INCREF (m_obj);
+  }
+
+  /* Destructor.  */
+  ~gdb_py_overlay_manager ()
+  {
+    gdb_assert (gdb_python_initialized);
+    gdbpy_enter enter_py;
+
+    Py_DECREF (m_obj);
+  }
+
+private:
+
+  /* Generic method for calling a python method which is expected to take a
+     CORE_ADDR.  If the method is implemented return its result, else raise an
+     error.
+  */
+
+  PyObject *
+  call_python (CORE_ADDR addr, const char *method_name)
+  {
+    gdb_assert (gdb_python_initialized);
+    gdbpy_enter enter_py;
+
+    PyObject *obj = (PyObject *) m_obj;
+
+    /* If this method is not implemented, return addr.  */
+    PyObject *match_method_name
+      = PyUnicode_FromString (method_name);
+    if (!PyObject_HasAttr ((PyObject *) obj, match_method_name))
+      error (_("could not find method %s"), method_name);
+
+    gdbpy_ref<> addr_arg (gdb_py_object_from_ulongest (addr));
+    if (addr_arg == nullptr)
+      {
+	gdbpy_print_stack ();
+	error (_("failed to convert addr to pass to %s"), method_name);
+      }
+
+    gdbpy_ref<> result (PyObject_CallMethodObjArgs (obj, match_method_name,
+						    addr_arg.get (), NULL));
+    if (result == NULL)
+      {
+	gdbpy_print_stack ();
+	error (_("missing result object from %s"), method_name);
+      }
+
+    if (result == Py_None)
+      {
+	gdbpy_print_stack ();
+	error (_("%s returned None"), method_name);
+      }
+
+    return result.get ();
+  }
+
+  /* Generic method for calling a python method which is expected to take a
+     CORE_ADDR and return a bool.
+  */
+
+  bool
+  call_python_bool (CORE_ADDR addr, const char *method_name)
+  {
+    PyObject *result = call_python (addr, method_name);
+
+    return PyObject_IsTrue (result);
+  }
+
+  /* Generic method for calling a python method which is expected to take a
+     CORE_ADDR and return a CORE_ADDR.
+  */
+
+  CORE_ADDR
+  call_python_addr (CORE_ADDR addr, const char *method_name)
+  {
+    PyObject *result = call_python (addr, method_name);
+
+    if (get_addr_from_python (result, &addr) != 0)
+      {
+	gdbpy_print_stack ();
+	error (_("failed to convert address returned by %s"), method_name);
+      }
+
+    return addr;
+  }
+
+  /* Generic method for calling a python method, if implemented, which is
+     expected to take no arguments and return void.  Raise an error if the
+     method is not implemented.  */
+
+  void
+  call_python_void (const char *method_name)
+  {
+    gdb_assert (gdb_python_initialized);
+    gdbpy_enter enter_py;
+
+    PyObject *obj = (PyObject *) m_obj;
+
+    /* If this method is not implemented, return.  */
+    if (!PyObject_HasAttrString (obj, method_name))
+      error (_("could not find method %s"), method_name);
+
+    gdbpy_ref<> result (PyObject_CallMethod (obj, method_name, NULL));
+
+    if (result == NULL)
+      {
+	gdbpy_print_stack ();
+	error (_("missing result object from %s"), method_name);
+      }
+  }
+
+  /* Return the result of calling HANDLE_OVERLAY_BP_EVENT_METHOD.  */
+
+  void
+  handle_overlay_bp_event () override
+  {
+    call_python_void (HANDLE_OVERLAY_BP_EVENT_METHOD);
+  }
+
+  /* Return result of calling PC_IN_UNMAPPED_RANGE_METHOD.
+
+     We cannot pass section to PC_IN_UNMAPPED_RANGE_METHOD because GDB's python
+     support currently has no concept of obj_section.  If an overlay manager
+     needed to access sections then obj_section would first need to be
+     supported.  */
+
+  bool
+  pc_in_unmapped_range (CORE_ADDR addr,
+			ATTRIBUTE_UNUSED struct obj_section *section) override
+  {
+    return call_python_bool (addr, PC_IN_UNMAPPED_RANGE_METHOD);
+  }
+
+  /* Return result of calling PC_IN_MAPPED_RANGE_METHOD.
+
+     See pc_in_unmapped_range above regarding unused section parameter.  */
+
+  bool
+  pc_in_mapped_range (CORE_ADDR addr,
+		      ATTRIBUTE_UNUSED struct obj_section *section) override
+  {
+    return call_python_bool (addr, PC_IN_MAPPED_RANGE_METHOD);
+  }
+
+  /* Return result of calling OVERLAY_UNMAPPED_ADDRESS_METHOD.
+
+     See pc_in_unmapped_range above regarding unused section parameter.  */
+
+  CORE_ADDR
+  overlay_unmapped_address (CORE_ADDR addr,
+			    ATTRIBUTE_UNUSED struct obj_section *section)
+			   override
+  {
+    return call_python_addr (addr, OVERLAY_UNMAPPED_ADDRESS_METHOD);
+  }
+
+  /* Return result of calling OVERLAY_MAPPED_ADDRESS_METHOD.
+
+     See pc_in_unmapped_range above regarding unused section parameter.  */
+
+  CORE_ADDR
+  overlay_mapped_address (CORE_ADDR addr,
+			  ATTRIBUTE_UNUSED struct obj_section *section)
+			 override
+  {
+    return call_python_addr (addr, OVERLAY_MAPPED_ADDRESS_METHOD);
+  }
+
+  /* Return result of calling SYMBOL_OVERLAYED_ADDRESS_METHOD.
+
+     See pc_in_unmapped_range above regarding unused section parameter.  */
+
+  CORE_ADDR
+  symbol_overlayed_address (CORE_ADDR addr,
+			    ATTRIBUTE_UNUSED struct obj_section *section)
+			   override
+  {
+    return call_python_addr (addr, SYMBOL_OVERLAYED_ADDRESS_METHOD);
+  }
+
+  /* Return result of calling OVERLAY_UPDATE_METHOD.
+
+     See pc_in_unmapped_range above regarding unused section parameter.  */
+
+  void
+  overlay_update (ATTRIBUTE_UNUSED obj_section *) override
+  {
+    call_python_void (OVERLAY_UPDATE_METHOD);
+  }
+
+  /* Return result of calling OVERLAY_ADDRESS_FOR_SAL_METHOD.  */
+
+  CORE_ADDR
+  overlay_address_for_sal (CORE_ADDR addr) override
+  {
+    return call_python_addr (addr, OVERLAY_ADDRESS_FOR_SAL_METHOD);
+  }
+
+  /* Since GDB python has no concept of sections, we cannot currently support
+     python overlay managers that need to deal with overlay sections.  The
+     following methods are not required for non-section based managers so they
+     simply return nullptr or false.
+  */
+
+  struct obj_section *
+  find_pc_mapped_section (CORE_ADDR) override
+  {
+    return nullptr;
+  }
+
+  struct obj_section *
+  find_pc_overlay (CORE_ADDR) override
+  {
+    return nullptr;
+  }
+
+  bool
+  section_is_overlay (ATTRIBUTE_UNUSED struct obj_section *) override
+  {
+    return false;
+  }
+
+  bool
+  section_is_mapped (ATTRIBUTE_UNUSED struct obj_section *) override
+  {
+    return false;
+  }
+
+  void list_overlays_command (const char *args, int from_tty) override
+  {
+    call_python_void (LIST_OVERLAYS_COMMAND_METHOD);
+  }
+
+  void map_overlay_command (const char *args, int from_tty) override
+  {
+    call_python_void (MAP_OVERLAY_COMMAND_METHOD);
+  }
+
+  void unmap_overlay_command (const char *args, int from_tty) override
+  {
+    call_python_void (UNMAP_OVERLAY_COMMAND_METHOD);
+  }
+
+  void overlay_load_command (const char *args, int from_tty) override
+  {
+    overlay_update (nullptr);
+  }
+
+  /* The Python object associated with this overlay manager.  */
+  gdbpy_ovly_mgr_object *m_obj;
+};
+
+/* Wrapper around a Python object, provides a mechanism to find the overlay
+   manager object from the Python object.  */
+
+struct gdbpy_ovly_mgr_object {
+  /* Python boilerplate, must come first.  */
+  PyObject_HEAD
+
+  /* Point at the actual overlay manager we created when this Python object
+     was created.  This object is owned by the generic overlay management
+     code within GDB.  */
+  gdb_py_overlay_manager *manager;
+};
+
+/* Initializer for OverlayManager object, it takes no parameters.  */
+
+static int
+ovlpy_init (PyObject *self, PyObject *args, PyObject *kwargs)
+{
+  gdbpy_ovly_mgr_object *obj = (gdbpy_ovly_mgr_object *) self;
+  std::unique_ptr <gdb_py_overlay_manager> mgr
+    (new gdb_py_overlay_manager (obj));
+  obj->manager = mgr.get ();
+  overlay_manager_register (std::move (mgr));
+  return 0;
+}
+
+/* Deallocate OverlayManager object.  */
+
+static void
+ovlpy_dealloc (PyObject *self)
+{
+  /* TODO: Should ensure that this object is no longer registered as the
+     overlay manager for GDB otherwise bad things will happen.  */
+
+  /* Set this pointer to null not because we have to, but to protect
+     against any uses after we deallocate.  */
+  gdbpy_ovly_mgr_object *obj = (gdbpy_ovly_mgr_object *) self;
+  obj->manager = nullptr;
+
+  /* Now ask Python to free this object.  */
+  Py_TYPE (self)->tp_free (self);
+}
+
+void
+gdbpy_finalize_overlay (void)
+{
+  overlay_manager_register (nullptr);
+}
+
+/* Structure defining an OverlayManager object type.  */
+
+PyTypeObject overlay_manager_object_type =
+{
+  PyVarObject_HEAD_INIT (NULL, 0)
+  "gdb.OverlayManager",		  /*tp_name*/
+  sizeof (gdbpy_ovly_mgr_object), /*tp_basicsize*/
+  0,				  /*tp_itemsize*/
+  ovlpy_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 | Py_TPFLAGS_BASETYPE,  /*tp_flags*/
+  "GDB overlay manager 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 */
+  0,				  /* tp_getset */
+  0,				  /* tp_base */
+  0,				  /* tp_dict */
+  0,				  /* tp_descr_get */
+  0,				  /* tp_descr_set */
+  0,				  /* tp_dictoffset */
+  ovlpy_init,			  /* tp_init */
+  0,				  /* tp_alloc */
+};
+
+/* Initialize the Python overlay code.  */
+int
+gdbpy_initialize_overlay (void)
+{
+  overlay_manager_object_type.tp_new = PyType_GenericNew;
+  if (PyType_Ready (&overlay_manager_object_type) < 0)
+    return -1;
+
+  if (gdb_pymodule_addobject (gdb_module, "OverlayManager",
+			      (PyObject *) &overlay_manager_object_type) < 0)
+    return -1;
+  return 0;
+}
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index d947b96033b..e898017690c 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -540,6 +540,9 @@ int gdbpy_initialize_connection ()
 int gdbpy_initialize_micommands (void)
   CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
 void gdbpy_finalize_micommands ();
+int gdbpy_initialize_overlay (void)
+  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
+void gdbpy_finalize_overlay (void);
 
 /* A wrapper for PyErr_Fetch that handles reference counting for the
    caller.  */
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 7a9c8c1b66e..f2e03a5769e 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1892,6 +1892,7 @@ finalize_python (void *ignore)
   gdbpy_enter::finalize ();
 
   gdbpy_finalize_micommands ();
+  gdbpy_finalize_overlay ();
 
   Py_Finalize ();
 
@@ -2072,7 +2073,8 @@ do_start_initialization ()
       || gdbpy_initialize_membuf () < 0
       || gdbpy_initialize_connection () < 0
       || gdbpy_initialize_tui () < 0
-      || gdbpy_initialize_micommands () < 0)
+      || gdbpy_initialize_micommands () < 0
+      || gdbpy_initialize_overlay () < 0)
     return false;
 
 #define GDB_PY_DEFINE_EVENT_TYPE(name, py_name, doc, base)	\
-- 
2.17.1



More information about the Gdb-patches mailing list