[RFC,v2,11/21] gdb/python: allow instantiation of gdb.Objfile from Python

Message ID 20241121124714.419946-12-jan.vrany@labware.com
State New
Headers
Series Add Python "JIT" API |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gdb_build--master-arm warning Skipped upon request
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 warning Skipped upon request

Commit Message

Jan Vraný Nov. 21, 2024, 12:47 p.m. UTC
  This commit adds code to allow user extension to instantiate
gdb.Objfile. This is a step towards a Python support for dynamically
generated code (JIT) in GDB.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
 gdb/doc/python.texi                     |  13 +++
 gdb/python/py-inferior.c                |  10 +++
 gdb/python/py-objfile.c                 | 111 +++++++++++++++++++++++-
 gdb/python/python-internal.h            |   1 +
 gdb/testsuite/gdb.python/py-objfile.exp |  38 +++++++-
 5 files changed, 169 insertions(+), 4 deletions(-)
  

Comments

Eli Zaretskii Nov. 21, 2024, 1:42 p.m. UTC | #1
> From: Jan Vrany <jan.vrany@labware.com>
> CC: Jan Vrany <jan.vrany@labware.com>,
> 	Eli Zaretskii <eliz@gnu.org>
> Date: Thu, 21 Nov 2024 12:47:04 +0000
> 
> This commit adds code to allow user extension to instantiate
> gdb.Objfile. This is a step towards a Python support for dynamically
> generated code (JIT) in GDB.
> 
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> ---
>  gdb/doc/python.texi                     |  13 +++
>  gdb/python/py-inferior.c                |  10 +++
>  gdb/python/py-objfile.c                 | 111 +++++++++++++++++++++++-
>  gdb/python/python-internal.h            |   1 +
>  gdb/testsuite/gdb.python/py-objfile.exp |  38 +++++++-
>  5 files changed, 169 insertions(+), 4 deletions(-)

The documentation part is okay, provided that this not is fixed:

> +The optional @var{inferior} argument specifies the inferior to which the newly
> +created objfile is added to.

The second "to" should be removed.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
  

Patch

diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 2065f001320..c32d84fb9ed 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -5656,6 +5656,19 @@  Reading symbols from ./hello...
 
 A @code{gdb.Objfile} object has the following methods:
 
+@defun Objfile.__init__ (filename @r{[}, inferior @r{][}, arch @r{]})
+Create a new objfile with given @var{filename}.
+
+The optional @var{inferior} argument specifies the inferior to which the newly
+created objfile is added to.  Defaults to currently selected inferior.
+@pxref{Inferiors In Python}.
+
+The optional @var{arch} argument specifies the architectore to associate with
+the newly created objfile.  Defaults to inferior's architecture.
+@xref{Architectures In Python}.
+
+@end defun
+
 @defun Objfile.is_valid ()
 Returns @code{True} if the @code{gdb.Objfile} object is valid,
 @code{False} if not.  A @code{gdb.Objfile} object can become invalid
diff --git a/gdb/python/py-inferior.c b/gdb/python/py-inferior.c
index 60bf56d17de..725580770c7 100644
--- a/gdb/python/py-inferior.c
+++ b/gdb/python/py-inferior.c
@@ -225,6 +225,16 @@  python_free_objfile (struct objfile *objfile)
     gdbpy_print_stack ();
 }
 
+/* Return inferior reference that is wrapped by this object.  */
+
+inferior *
+inferior_object_to_inferior (PyObject *obj)
+{
+  if (! PyObject_TypeCheck (obj, &inferior_object_type))
+    return nullptr;
+  return ((inferior_object *) obj)->inferior;
+}
+
 /* Return a reference to the Python object of type Inferior
    representing INFERIOR.  If the object has already been created,
    return it and increment the reference count,  otherwise, create it.
diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c
index e006fcc6e75..cc225ca135d 100644
--- a/gdb/python/py-objfile.c
+++ b/gdb/python/py-objfile.c
@@ -25,6 +25,7 @@ 
 #include "symtab.h"
 #include "python.h"
 #include "inferior.h"
+#include "observable.h"
 
 struct objfile_object
 {
@@ -251,6 +252,84 @@  objfpy_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
   return (PyObject *) self.release ();
 }
 
+/* Object initializer; creates new a objfile.
+
+   Use: __init__(FILENAME [, INFERIOR [,ARCH]]).  */
+
+static int
+objfpy_init (PyObject *zelf, PyObject *args, PyObject *kw)
+{
+  struct objfile_object *self = (struct objfile_object*) zelf;
+
+  if (self->objfile)
+    {
+      PyErr_Format (PyExc_RuntimeError,
+		    _("Objfile object already initialized."));
+      return -1;
+    }
+
+  static const char *keywords[] = { "filename", "inferior", "arch", nullptr };
+  const char *filename;
+  PyObject* inf_obj = nullptr;
+  PyObject* arch_obj = nullptr;
+
+
+  if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "s|OO", keywords,
+					&filename, &inf_obj, &arch_obj))
+    return -1;
+
+  inferior *inf = nullptr;
+  if (inf_obj)
+    {
+      inf = inferior_object_to_inferior (inf_obj);
+      if (! inf)
+	{
+	  PyErr_Format (PyExc_TypeError,
+		    _("The inferior argument is not gdb.Inferior object"));
+	  return -1;
+	}
+    }
+  else
+    {
+      inf = current_inferior ();
+    }
+
+  gdbarch *arch = nullptr;
+  if (arch_obj)
+    {
+      if (! gdbpy_is_architecture (arch_obj))
+	{
+	  PyErr_Format (PyExc_TypeError,
+		    _("The arch argument is not gdb.Architecture object"));
+	  return -1;
+	}
+      arch = arch_object_to_gdbarch (arch_obj);
+    }
+  else
+   {
+     arch = inf->arch ();
+   }
+
+  if (!objfpy_initialize (self))
+    {
+      PyErr_Format (PyExc_RuntimeError,
+		    _("Failed to initialize Objfile object."));
+      return -1;
+    }
+
+  struct objfile *objfile;
+
+  objfile = objfile::make (nullptr, inf->pspace, filename, OBJF_NOT_FILENAME | OBJF_READNOW);
+  objfile->per_bfd->gdbarch = arch;
+
+  self->objfile = objfile;
+  objfpy_objfile_data_key.set(objfile, self);
+  /* Increment refcount on self as it is now referenced from objfile!  */
+  Py_INCREF (self);
+
+  return 0;
+}
+
 PyObject *
 objfpy_get_printers (PyObject *o, void *ignore)
 {
@@ -731,6 +810,32 @@  objfile_to_objfile_object (struct objfile *objfile)
   return gdbpy_ref<>::new_reference (result);
 }
 
+/* This function remove any dynamic objfiles left over when the
+   inferior exits.  */
+
+static void
+objfpy_inferior_exit_hook (struct inferior *inf)
+{
+  for (objfile *objf : current_program_space->objfiles_safe ())
+    {
+      if (objf->obfd == nullptr)
+	{
+	  /* Following check is to only unlink dynamic objfiles created by
+	     Python code.  Dynamic objfiles created by JIT reader API are
+	     unlinked in jit_inferior_exit_hook ().  */
+	  if (objf->jited_data == nullptr || objf->jited_data->addr != 0)
+	    objf->unlink ();
+	}
+    }
+}
+
+void _initialize_py_objfile ();
+void
+_initialize_py_objfile ()
+{
+  gdb::observers::inferior_exit.attach (objfpy_inferior_exit_hook, "py-objfile");
+}
+
 static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_objfile (void)
 {
@@ -835,8 +940,8 @@  PyTypeObject objfile_object_type =
   0,				  /* tp_dict */
   0,				  /* tp_descr_get */
   0,				  /* tp_descr_set */
-  offsetof (objfile_object, dict), /* tp_dictoffset */
-  0,				  /* tp_init */
+  offsetof (objfile_object, dict),/* tp_dictoffset */
+  objfpy_init,	                  /* tp_init */
   0,				  /* tp_alloc */
-  objfpy_new,			  /* tp_new */
+  objfpy_new,   		  /* tp_new */
 };
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 0a52cb86b4a..a5bcfedf79b 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -574,6 +574,7 @@  struct symtab_and_line *sal_object_to_symtab_and_line (PyObject *obj);
 frame_info_ptr frame_object_to_frame_info (PyObject *frame_obj);
 struct gdbarch *arch_object_to_gdbarch (PyObject *obj);
 struct compunit_symtab *compunit_object_to_compunit (PyObject *obj);
+inferior *inferior_object_to_inferior(PyObject *obj);
 
 extern PyObject *gdbpy_execute_mi_command (PyObject *self, PyObject *args,
 					   PyObject *kw);
diff --git a/gdb/testsuite/gdb.python/py-objfile.exp b/gdb/testsuite/gdb.python/py-objfile.exp
index 2f5b7752b43..250ae4763b7 100644
--- a/gdb/testsuite/gdb.python/py-objfile.exp
+++ b/gdb/testsuite/gdb.python/py-objfile.exp
@@ -172,4 +172,40 @@  if ![ishost *-*-mingw*] {
 gdb_py_test_silent_cmd "python objfile = gdb.objfiles()\[0\]" \
     "get first objfile" 1
 gdb_file_cmd ${binfile}
-gdb_test "python print(objfile)" "<gdb.Objfile \\\(invalid\\\)>"
+gdb_test "python print(objfile)" "<gdb.Objfile \\\(invalid\\\)>" "print invalid objfile"
+
+# Test creating objfile dynamically from Python
+gdb_py_test_silent_cmd "python objfile = gdb.Objfile(\"Test objfile\")" \
+	"create objfile" 1
+
+gdb_test "python print(objfile)" \
+	"<gdb.Objfile filename=Test objfile>" \
+	"print dynamic objfile"
+
+gdb_test "python print(objfile.is_file)" \
+	"False" \
+	"(dynamic) objfile.is_file"
+
+gdb_test "python print(objfile.is_valid())" \
+	"True" \
+	"(dynamic) objfile.is_valid()"
+
+gdb_test "python print(objfile in gdb.objfiles())" \
+	"True" \
+	"(dynamic) objfile in gdb.objfiles()"
+
+gdb_test "python print( gdb.Objfile(\"Test objfile 2\", gdb.selected_inferior()))" \
+	"<gdb.Objfile filename=Test objfile 2>" \
+	"create objfile with inferior"
+
+gdb_test "python print( gdb.Objfile(\"Test objfile 3\", gdb.selected_inferior(), gdb.selected_inferior().architecture()))" \
+	"<gdb.Objfile filename=Test objfile 3>" \
+	"create objfile with inferior and arch"
+
+gdb_test "python print( gdb.Objfile(\"Test objfile 4\", gdb))" \
+	"TypeError.*:.*" \
+	"create objfile with invalid inferior"
+
+gdb_test "python print( gdb.Objfile(\"Test objfile 5\", gdb.selected_inferior(), gdb.selected_inferior()))" \
+	"TypeError.*:.*" \
+	"create objfile with valid inferior but invalid arch"