forked from Imagelibrary/binutils-gdb
This commit adds code to allow user extension to instantiate gdb.Compunit. This is a step towards a Python support for dynamically generated code (JIT) in GDB. Reviewed-By: Eli Zaretskii <eliz@gnu.org>
415 lines
11 KiB
C
415 lines
11 KiB
C
/* Python interface to compunits.
|
||
|
||
Copyright (C) 2008-2024 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 <algorithm>
|
||
#include "charset.h"
|
||
#include "symtab.h"
|
||
#include "source.h"
|
||
#include "python-internal.h"
|
||
#include "objfiles.h"
|
||
#include "block.h"
|
||
|
||
struct compunit_object {
|
||
PyObject_HEAD
|
||
|
||
/* The GDB compunit structure. */
|
||
struct compunit_symtab *compunit;
|
||
|
||
/* A compunit object is associated with an objfile, so keep track with
|
||
a doubly-linked list, rooted in the objfile. This allows
|
||
invalidation of the underlying struct compunit_symtab when the objfile is
|
||
deleted. */
|
||
compunit_object *prev;
|
||
compunit_object *next;
|
||
};
|
||
|
||
/* This function is called when an objfile is about to be freed.
|
||
Invalidate the compunit as further actions on the compunit
|
||
would result in bad data. All access to obj->compunit should be
|
||
gated by CUPY_REQUIRE_VALID which will raise an exception on
|
||
compunits. */
|
||
struct cupy_deleter
|
||
{
|
||
void operator() (compunit_object *obj)
|
||
{
|
||
while (obj)
|
||
{
|
||
compunit_object *next = obj->next;
|
||
|
||
obj->compunit = nullptr;
|
||
obj->next = nullptr;
|
||
obj->prev = nullptr;
|
||
obj = next;
|
||
}
|
||
}
|
||
};
|
||
|
||
extern PyTypeObject compunit_object_type
|
||
CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("compunit_object");
|
||
static const registry<objfile>::key<compunit_object, cupy_deleter>
|
||
cupy_objfile_data_key;
|
||
|
||
/* Require a valid compunit. All access to compunit_object->compunit
|
||
should be gated by this call. */
|
||
|
||
#define CUPY_REQUIRE_VALID(compunit_obj, compunit) \
|
||
do { \
|
||
compunit = compunit_object_to_compunit (compunit_obj); \
|
||
if (compunit == nullptr) \
|
||
{ \
|
||
PyErr_SetString (PyExc_RuntimeError, \
|
||
_("Compunit object is invalid.")); \
|
||
return nullptr; \
|
||
} \
|
||
} while (0)
|
||
|
||
|
||
/* Getter function for gdb.Compunit.objfile. */
|
||
|
||
static PyObject *
|
||
cupy_get_objfile (PyObject *self, void *closure)
|
||
{
|
||
struct compunit_symtab *compunit = nullptr;
|
||
|
||
CUPY_REQUIRE_VALID (self, compunit);
|
||
|
||
return objfile_to_objfile_object (compunit->objfile ()).release ();
|
||
}
|
||
|
||
/* Getter function for gdb.Compunit.producer. */
|
||
|
||
static PyObject *
|
||
cupy_get_producer (PyObject *self, void *closure)
|
||
{
|
||
struct compunit_symtab *compunit = nullptr;
|
||
|
||
CUPY_REQUIRE_VALID (self, compunit);
|
||
if (compunit->producer () != nullptr)
|
||
{
|
||
const char *producer = compunit->producer ();
|
||
|
||
return host_string_to_python_string (producer).release ();
|
||
}
|
||
|
||
Py_RETURN_NONE;
|
||
}
|
||
|
||
/* Implementation of gdb.Compunit.is_valid (self) -> Boolean.
|
||
Returns True if this Symbol table still exists in GDB. */
|
||
|
||
static PyObject *
|
||
cupy_is_valid (PyObject *self, PyObject *args)
|
||
{
|
||
struct compunit_symtab *compunit = nullptr;
|
||
|
||
compunit = compunit_object_to_compunit (self);
|
||
if (compunit == nullptr)
|
||
Py_RETURN_FALSE;
|
||
|
||
Py_RETURN_TRUE;
|
||
}
|
||
|
||
/* Return the GLOBAL_BLOCK of the underlying compunit. */
|
||
|
||
static PyObject *
|
||
cupy_global_block (PyObject *self, PyObject *args)
|
||
{
|
||
struct compunit_symtab *compunit = nullptr;
|
||
const struct blockvector *blockvector;
|
||
|
||
CUPY_REQUIRE_VALID (self, compunit);
|
||
|
||
blockvector = compunit->blockvector ();
|
||
const struct block *block = blockvector->global_block ();
|
||
|
||
return block_to_block_object (block, compunit->objfile ());
|
||
}
|
||
|
||
/* Return the STATIC_BLOCK of the underlying compunit. */
|
||
|
||
static PyObject *
|
||
cupy_static_block (PyObject *self, PyObject *args)
|
||
{
|
||
struct compunit_symtab *compunit = nullptr;
|
||
const struct blockvector *blockvector;
|
||
|
||
CUPY_REQUIRE_VALID (self, compunit);
|
||
|
||
blockvector = compunit->blockvector ();
|
||
const struct block *block = blockvector->static_block ();
|
||
|
||
return block_to_block_object (block, compunit->objfile ());
|
||
}
|
||
|
||
static PyObject *
|
||
cupy_get_symtabs (PyObject *self, void *closure)
|
||
{
|
||
struct compunit_symtab *compunit = nullptr;
|
||
|
||
CUPY_REQUIRE_VALID (self, compunit);
|
||
|
||
gdbpy_ref<> list (PyList_New (0));
|
||
if (list == nullptr)
|
||
return nullptr;
|
||
|
||
for (struct symtab *each : compunit->filetabs ())
|
||
{
|
||
{
|
||
gdbpy_ref<> item (symtab_to_symtab_object (each));
|
||
if (item.get () == nullptr
|
||
|| PyList_Append (list.get (), item.get ()) == -1)
|
||
return nullptr;
|
||
}
|
||
}
|
||
|
||
return list.release ();
|
||
}
|
||
|
||
static void
|
||
cupy_dealloc (PyObject *obj)
|
||
{
|
||
compunit_object *compunit = (compunit_object *) obj;
|
||
|
||
if (compunit->prev)
|
||
compunit->prev->next = compunit->next;
|
||
else if (compunit->compunit)
|
||
cupy_objfile_data_key.set (compunit->compunit->objfile (),
|
||
compunit->next);
|
||
if (compunit->next)
|
||
compunit->next->prev = compunit->prev;
|
||
compunit->compunit = nullptr;
|
||
Py_TYPE (obj)->tp_free (obj);
|
||
}
|
||
|
||
/* Given a compunit, and a compunit_object that has previously been
|
||
allocated and initialized, populate the compunit_object with the
|
||
struct compunit_symtab data. Also, register the compunit_object life-cycle
|
||
with the life-cycle of the object file associated with this
|
||
compunit, if needed. */
|
||
static void
|
||
set_compunit (compunit_object *obj, struct compunit_symtab *compunit)
|
||
{
|
||
obj->compunit = compunit;
|
||
obj->prev = nullptr;
|
||
if (compunit)
|
||
{
|
||
obj->next = cupy_objfile_data_key.get (compunit->objfile ());
|
||
if (obj->next)
|
||
obj->next->prev = obj;
|
||
cupy_objfile_data_key.set (compunit->objfile (), obj);
|
||
}
|
||
else
|
||
obj->next = nullptr;
|
||
}
|
||
|
||
/* Object initializer; creates a new compunit.
|
||
|
||
Use: __init__(FILENAME, OBJFILE, START, END [, CAPACITY]). */
|
||
|
||
static int
|
||
cupy_init (PyObject *zelf, PyObject *args, PyObject *kw)
|
||
{
|
||
struct compunit_object *self = (struct compunit_object*) zelf;
|
||
|
||
if (self->compunit)
|
||
{
|
||
PyErr_Format (PyExc_RuntimeError,
|
||
_("Compunit object already initialized."));
|
||
return -1;
|
||
}
|
||
|
||
static const char *keywords[] = { "filename", "objfile", "start", "end",
|
||
"capacity", nullptr };
|
||
const char *filename;
|
||
PyObject *objf_obj = nullptr;
|
||
uint64_t start = 0;
|
||
uint64_t end = 0;
|
||
uint64_t capacity = 8;
|
||
|
||
|
||
if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "sOKK|K", keywords,
|
||
&filename, &objf_obj, &start, &end,
|
||
&capacity))
|
||
return -1;
|
||
|
||
auto objf = objfile_object_to_objfile (objf_obj);
|
||
if (! objf)
|
||
{
|
||
PyErr_Format (PyExc_TypeError,
|
||
_("The objfile argument is not valid gdb.Objfile object"));
|
||
return -1;
|
||
}
|
||
|
||
/* Check that start-end range is valid. */
|
||
if (! (start <= end))
|
||
{
|
||
PyErr_Format (PyExc_ValueError,
|
||
_("The start argument must be less or equal to the end "
|
||
"argument"));
|
||
return -1;
|
||
|
||
}
|
||
|
||
/* Check that to-be created compunits' global block does not overlap
|
||
with existing compunit. */
|
||
|
||
for (struct objfile *of : objf->pspace ()->objfiles_safe ())
|
||
{
|
||
for (compunit_symtab *cu : of->compunits ())
|
||
{
|
||
global_block *gb = cu->blockvector ()->global_block ();
|
||
if (std::max (start, gb->start ()) < std::min(end, gb->end ()))
|
||
{
|
||
PyErr_Format (PyExc_ValueError,
|
||
_("The start-end range overlaps with existing compunit"));
|
||
return -1;
|
||
}
|
||
}
|
||
}
|
||
|
||
blockvector *bv = allocate_blockvector (&objf->objfile_obstack,
|
||
FIRST_LOCAL_BLOCK, capacity);
|
||
compunit_symtab *cu = allocate_compunit_symtab (objf, filename);
|
||
cu->set_dirname (nullptr);
|
||
cu->set_blockvector (bv);
|
||
|
||
/* Allocate global block. */
|
||
global_block *gb = new (&objf->objfile_obstack) global_block ();
|
||
gb->set_multidict (mdict_create_linear_expandable (language_minimal));
|
||
gb->set_start ((CORE_ADDR) start);
|
||
gb->set_end ((CORE_ADDR) end);
|
||
gb->set_compunit (cu);
|
||
bv->set_block (GLOBAL_BLOCK, gb);
|
||
|
||
/* Allocate static block. */
|
||
struct block *sb = new (&objf->objfile_obstack) block ();
|
||
sb->set_multidict (mdict_create_linear_expandable (language_minimal));
|
||
sb->set_start ((CORE_ADDR) start);
|
||
sb->set_end ((CORE_ADDR) end);
|
||
sb->set_superblock (gb);
|
||
bv->set_block (STATIC_BLOCK, sb);
|
||
|
||
add_compunit_symtab_to_objfile (cu);
|
||
|
||
set_compunit(self, cu);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* Return a new reference to gdb.Compunit Python object representing
|
||
COMPUNIT. Return NULL and set the Python error on failure. */
|
||
gdbpy_ref<>
|
||
compunit_to_compunit_object (struct compunit_symtab *compunit)
|
||
{
|
||
compunit_object *compunit_obj;
|
||
|
||
compunit_obj = PyObject_NEW(compunit_object, &compunit_object_type);
|
||
if (compunit_obj)
|
||
set_compunit(compunit_obj, compunit);
|
||
|
||
return gdbpy_ref<>::new_reference ((PyObject * )compunit_obj);
|
||
}
|
||
|
||
/* Return struct compunit_symtab reference that is wrapped by this object. */
|
||
struct compunit_symtab *
|
||
compunit_object_to_compunit (PyObject *obj)
|
||
{
|
||
if (! PyObject_TypeCheck (obj, &compunit_object_type))
|
||
return nullptr;
|
||
return ((compunit_object *) obj)->compunit;
|
||
}
|
||
|
||
static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
|
||
gdbpy_initialize_compunits (void)
|
||
{
|
||
if (gdbpy_type_ready (&compunit_object_type) < 0)
|
||
return -1;
|
||
|
||
return 0;
|
||
}
|
||
|
||
GDBPY_INITIALIZE_FILE (gdbpy_initialize_compunits);
|
||
|
||
|
||
|
||
static gdb_PyGetSetDef compunit_object_getset[] = {
|
||
{ "objfile", cupy_get_objfile, nullptr, "The compunit's objfile.",
|
||
nullptr },
|
||
{ "producer", cupy_get_producer, nullptr,
|
||
"The name/version of the program that compiled this compunit.", nullptr },
|
||
{ "symtabs", cupy_get_symtabs, nullptr,
|
||
"List of symbol tables associated with this compunit", nullptr },
|
||
{nullptr} /* Sentinel */
|
||
};
|
||
|
||
static PyMethodDef compunit_object_methods[] = {
|
||
{ "is_valid", cupy_is_valid, METH_NOARGS,
|
||
"is_valid () -> Boolean.\n\
|
||
Return true if this compunit is valid, false if not." },
|
||
{ "global_block", cupy_global_block, METH_NOARGS,
|
||
"global_block () -> gdb.Block.\n\
|
||
Return the global block of the compunit." },
|
||
{ "static_block", cupy_static_block, METH_NOARGS,
|
||
"static_block () -> gdb.Block.\n\
|
||
Return the static block of the compunit." },
|
||
{nullptr} /* Sentinel */
|
||
};
|
||
|
||
PyTypeObject compunit_object_type = {
|
||
PyVarObject_HEAD_INIT (nullptr, 0)
|
||
"gdb.Compunit", /*tp_name*/
|
||
sizeof (compunit_object), /*tp_basicsize*/
|
||
0, /*tp_itemsize*/
|
||
cupy_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 compunit object", /*tp_doc */
|
||
0, /*tp_traverse */
|
||
0, /*tp_clear */
|
||
gdbpy_richcompare<compunit_object, compunit_symtab,
|
||
&compunit_object::compunit>,/*tp_richcompare */
|
||
0, /*tp_weaklistoffset */
|
||
0, /*tp_iter */
|
||
0, /*tp_iternext */
|
||
compunit_object_methods, /*tp_methods */
|
||
0, /*tp_members */
|
||
compunit_object_getset, /*tp_getset */
|
||
0, /* tp_base */
|
||
0, /* tp_dict */
|
||
0, /* tp_descr_get */
|
||
0, /* tp_descr_set */
|
||
0, /* tp_dictoffset */
|
||
cupy_init, /* tp_init */
|
||
0, /* tp_alloc */
|
||
PyType_GenericNew /* tp_new */
|
||
};
|