aboutsummaryrefslogtreecommitdiff
path: root/gdb/python/py-disasm.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/python/py-disasm.c')
-rw-r--r--gdb/python/py-disasm.c1090
1 files changed, 1090 insertions, 0 deletions
diff --git a/gdb/python/py-disasm.c b/gdb/python/py-disasm.c
new file mode 100644
index 0000000..4c78ca3
--- /dev/null
+++ b/gdb/python/py-disasm.c
@@ -0,0 +1,1090 @@
+/* Python interface to instruction disassembly.
+
+ Copyright (C) 2021-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 "dis-asm.h"
+#include "arch-utils.h"
+#include "charset.h"
+#include "disasm.h"
+#include "progspace.h"
+
+/* Implement gdb.disassembler.DisassembleInfo type. An object of this type
+ represents a single disassembler request from GDB. */
+
+struct disasm_info_object
+{
+ PyObject_HEAD
+
+ /* The architecture in which we are disassembling. */
+ struct gdbarch *gdbarch;
+
+ /* The program_space in which we are disassembling. */
+ struct program_space *program_space;
+
+ /* Address of the instruction to disassemble. */
+ bfd_vma address;
+
+ /* The disassemble_info passed from core GDB, this contains the
+ callbacks necessary to read the instruction from core GDB, and to
+ print the disassembled instruction. */
+ disassemble_info *gdb_info;
+
+ /* If copies of this object are created then they are chained together
+ via this NEXT pointer, this allows all the copies to be invalidated at
+ the same time as the parent object. */
+ struct disasm_info_object *next;
+};
+
+extern PyTypeObject disasm_info_object_type
+ CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("disasm_info_object");
+
+/* Implement gdb.disassembler.DisassemblerResult type, an object that holds
+ the result of calling the disassembler. This is mostly the length of
+ the disassembled instruction (in bytes), and the string representing the
+ disassembled instruction. */
+
+struct disasm_result_object
+{
+ PyObject_HEAD
+
+ /* The length of the disassembled instruction in bytes. */
+ int length;
+
+ /* A buffer which, when allocated, holds the disassembled content of an
+ instruction. */
+ string_file *content;
+};
+
+extern PyTypeObject disasm_result_object_type
+ CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("disasm_result_object");
+
+/* When this is false we fast path out of gdbpy_print_insn, which should
+ keep the performance impact of the Python disassembler down. This is
+ set to true from Python by calling gdb.disassembler._set_enabled() when
+ the user registers a disassembler. */
+
+static bool python_print_insn_enabled = false;
+
+/* A sub-class of gdb_disassembler that holds a pointer to a Python
+ DisassembleInfo object. A pointer to an instance of this class is
+ placed in the application_data field of the disassemble_info that is
+ used when we call gdbarch_print_insn. */
+
+struct gdbpy_disassembler : public gdb_printing_disassembler
+{
+ /* Constructor. */
+ gdbpy_disassembler (disasm_info_object *obj, PyObject *memory_source);
+
+ /* Get the DisassembleInfo object pointer. */
+ disasm_info_object *
+ py_disasm_info () const
+ {
+ return m_disasm_info_object;
+ }
+
+ /* Callbacks used by disassemble_info. */
+ static void memory_error_func (int status, bfd_vma memaddr,
+ struct disassemble_info *info);
+ static void print_address_func (bfd_vma addr,
+ struct disassemble_info *info);
+ static int read_memory_func (bfd_vma memaddr, gdb_byte *buff,
+ unsigned int len,
+ struct disassemble_info *info);
+
+ /* Return a reference to an optional that contains the address at which a
+ memory error occurred. The optional will only have a value if a
+ memory error actually occurred. */
+ const gdb::optional<CORE_ADDR> &memory_error_address () const
+ { return m_memory_error_address; }
+
+ /* Return the content of the disassembler as a string. The contents are
+ moved out of the disassembler, so after this call the disassembler
+ contents have been reset back to empty. */
+ std::string release ()
+ {
+ return m_string_file.release ();
+ }
+
+private:
+
+ /* Where the disassembler result is written. */
+ string_file m_string_file;
+
+ /* The DisassembleInfo object we are disassembling for. */
+ disasm_info_object *m_disasm_info_object;
+
+ /* When the user indicates that a memory error has occurred then the
+ address of the memory error is stored in here. */
+ gdb::optional<CORE_ADDR> m_memory_error_address;
+
+ /* When the user calls the builtin_disassemble function, if they pass a
+ memory source object then a pointer to the object is placed in here,
+ otherwise, this field is nullptr. */
+ PyObject *m_memory_source;
+};
+
+/* Return true if OBJ is still valid, otherwise, return false. A valid OBJ
+ will have a non-nullptr gdb_info field. */
+
+static bool
+disasm_info_object_is_valid (disasm_info_object *obj)
+{
+ return obj->gdb_info != nullptr;
+}
+
+/* Fill in OBJ with all the other arguments. */
+
+static void
+disasm_info_fill (disasm_info_object *obj, struct gdbarch *gdbarch,
+ program_space *progspace, bfd_vma address,
+ disassemble_info *di, disasm_info_object *next)
+{
+ obj->gdbarch = gdbarch;
+ obj->program_space = progspace;
+ obj->address = address;
+ obj->gdb_info = di;
+ obj->next = next;
+}
+
+/* Implement DisassembleInfo.__init__. Takes a single argument that must
+ be another DisassembleInfo object and copies the contents from the
+ argument into this new object. */
+
+static int
+disasm_info_init (PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ static const char *keywords[] = { "info", NULL };
+ PyObject *info_obj;
+ if (!gdb_PyArg_ParseTupleAndKeywords (args, kwargs, "O!", keywords,
+ &disasm_info_object_type,
+ &info_obj))
+ return -1;
+
+ disasm_info_object *other = (disasm_info_object *) info_obj;
+ disasm_info_object *info = (disasm_info_object *) self;
+ disasm_info_fill (info, other->gdbarch, other->program_space,
+ other->address, other->gdb_info, other->next);
+ other->next = info;
+
+ /* As the OTHER object now holds a pointer to INFO we inc the ref count
+ on INFO. This stops INFO being deleted until OTHER has gone away. */
+ Py_INCREF ((PyObject *) info);
+ return 0;
+}
+
+/* The tp_dealloc callback for the DisassembleInfo type. */
+
+static void
+disasm_info_dealloc (PyObject *self)
+{
+ disasm_info_object *obj = (disasm_info_object *) self;
+
+ /* We no longer care about the object our NEXT pointer points at, so we
+ can decrement its reference count. This macro handles the case when
+ NEXT is nullptr. */
+ Py_XDECREF ((PyObject *) obj->next);
+
+ /* Now core deallocation behaviour. */
+ Py_TYPE (self)->tp_free (self);
+}
+
+/* Implement DisassembleInfo.is_valid(), really just a wrapper around the
+ disasm_info_object_is_valid function above. */
+
+static PyObject *
+disasmpy_info_is_valid (PyObject *self, PyObject *args)
+{
+ disasm_info_object *disasm_obj = (disasm_info_object *) self;
+
+ if (disasm_info_object_is_valid (disasm_obj))
+ Py_RETURN_TRUE;
+
+ Py_RETURN_FALSE;
+}
+
+/* Set the Python exception to be a gdb.MemoryError object, with ADDRESS
+ as its payload. */
+
+static void
+disasmpy_set_memory_error_for_address (CORE_ADDR address)
+{
+ PyObject *address_obj = gdb_py_object_from_longest (address).release ();
+ PyErr_SetObject (gdbpy_gdb_memory_error, address_obj);
+}
+
+/* Ensure that a gdb.disassembler.DisassembleInfo is valid. */
+
+#define DISASMPY_DISASM_INFO_REQUIRE_VALID(Info) \
+ do { \
+ if (!disasm_info_object_is_valid (Info)) \
+ { \
+ PyErr_SetString (PyExc_RuntimeError, \
+ _("DisassembleInfo is no longer valid.")); \
+ return nullptr; \
+ } \
+ } while (0)
+
+/* Initialise OBJ, a DisassemblerResult object with LENGTH and CONTENT.
+ OBJ might already have been initialised, in which case any existing
+ content should be discarded before the new CONTENT is moved in. */
+
+static void
+disasmpy_init_disassembler_result (disasm_result_object *obj, int length,
+ std::string content)
+{
+ if (obj->content == nullptr)
+ obj->content = new string_file;
+ else
+ obj->content->clear ();
+
+ obj->length = length;
+ *(obj->content) = std::move (content);
+}
+
+/* Implement gdb.disassembler.builtin_disassemble(). Calls back into GDB's
+ builtin disassembler. The first argument is a DisassembleInfo object
+ describing what to disassemble. The second argument is optional and
+ provides a mechanism to modify the memory contents that the builtin
+ disassembler will actually disassemble.
+
+ Returns an instance of gdb.disassembler.DisassemblerResult, an object
+ that wraps a disassembled instruction, or it raises a
+ gdb.MemoryError. */
+
+static PyObject *
+disasmpy_builtin_disassemble (PyObject *self, PyObject *args, PyObject *kw)
+{
+ PyObject *info_obj, *memory_source_obj = nullptr;
+ static const char *keywords[] = { "info", "memory_source", nullptr };
+ if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "O!|O", keywords,
+ &disasm_info_object_type, &info_obj,
+ &memory_source_obj))
+ return nullptr;
+
+ disasm_info_object *disasm_info = (disasm_info_object *) info_obj;
+ DISASMPY_DISASM_INFO_REQUIRE_VALID (disasm_info);
+
+ /* Where the result will be written. */
+ gdbpy_disassembler disassembler (disasm_info, memory_source_obj);
+
+ /* Now actually perform the disassembly. LENGTH is set to the length of
+ the disassembled instruction, or -1 if there was a memory-error
+ encountered while disassembling. See below more more details on
+ handling of -1 return value. */
+ int length;
+ try
+ {
+ length = gdbarch_print_insn (disasm_info->gdbarch, disasm_info->address,
+ disassembler.disasm_info ());
+ }
+ catch (gdbpy_err_fetch &pyerr)
+ {
+ /* Reinstall the Python exception held in PYERR. This clears to
+ pointers held in PYERR, hence the need to catch as a non-const
+ reference. */
+ pyerr.restore ();
+ return nullptr;
+ }
+
+ if (length == -1)
+ {
+
+ /* In an ideal world, every disassembler should always call the
+ memory error function before returning a status of -1 as the only
+ error a disassembler should encounter is a failure to read
+ memory. Unfortunately, there are some disassemblers who don't
+ follow this rule, and will return -1 without calling the memory
+ error function.
+
+ To make the Python API simpler, we just classify everything as a
+ memory error, but the message has to be modified for the case
+ where the disassembler didn't call the memory error function. */
+ if (disassembler.memory_error_address ().has_value ())
+ {
+ CORE_ADDR addr = *disassembler.memory_error_address ();
+ disasmpy_set_memory_error_for_address (addr);
+ }
+ else
+ {
+ std::string content = disassembler.release ();
+ if (!content.empty ())
+ PyErr_SetString (gdbpy_gdberror_exc, content.c_str ());
+ else
+ PyErr_SetString (gdbpy_gdberror_exc,
+ _("Unknown disassembly error."));
+ }
+ return nullptr;
+ }
+
+ /* Instructions are either non-zero in length, or we got an error,
+ indicated by a length of -1, which we handled above. */
+ gdb_assert (length > 0);
+
+ /* We should not have seen a memory error in this case. */
+ gdb_assert (!disassembler.memory_error_address ().has_value ());
+
+ /* Create a DisassemblerResult containing the results. */
+ std::string content = disassembler.release ();
+ PyTypeObject *type = &disasm_result_object_type;
+ gdbpy_ref<disasm_result_object> res
+ ((disasm_result_object *) type->tp_alloc (type, 0));
+ disasmpy_init_disassembler_result (res.get (), length, std::move (content));
+ return reinterpret_cast<PyObject *> (res.release ());
+}
+
+/* Implement gdb._set_enabled function. Takes a boolean parameter, and
+ sets whether GDB should enter the Python disassembler code or not.
+
+ This is called from within the Python code when a new disassembler is
+ registered. When no disassemblers are registered the global C++ flag
+ is set to false, and GDB never even enters the Python environment to
+ check for a disassembler.
+
+ When the user registers a new Python disassembler, the global C++ flag
+ is set to true, and now GDB will enter the Python environment to check
+ if there's a disassembler registered for the current architecture. */
+
+static PyObject *
+disasmpy_set_enabled (PyObject *self, PyObject *args, PyObject *kw)
+{
+ PyObject *newstate;
+ static const char *keywords[] = { "state", nullptr };
+ if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "O", keywords,
+ &newstate))
+ return nullptr;
+
+ if (!PyBool_Check (newstate))
+ {
+ PyErr_SetString (PyExc_TypeError,
+ _("The value passed to `_set_enabled' must be a boolean."));
+ return nullptr;
+ }
+
+ python_print_insn_enabled = PyObject_IsTrue (newstate);
+ Py_RETURN_NONE;
+}
+
+/* Implement DisassembleInfo.read_memory(LENGTH, OFFSET). Read LENGTH
+ bytes at OFFSET from the start of the instruction currently being
+ disassembled, and return a memory buffer containing the bytes.
+
+ OFFSET defaults to zero if it is not provided. LENGTH is required. If
+ the read fails then this will raise a gdb.MemoryError exception. */
+
+static PyObject *
+disasmpy_info_read_memory (PyObject *self, PyObject *args, PyObject *kw)
+{
+ disasm_info_object *obj = (disasm_info_object *) self;
+ DISASMPY_DISASM_INFO_REQUIRE_VALID (obj);
+
+ LONGEST length, offset = 0;
+ gdb::unique_xmalloc_ptr<gdb_byte> buffer;
+ static const char *keywords[] = { "length", "offset", nullptr };
+
+ if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "L|L", keywords,
+ &length, &offset))
+ return nullptr;
+
+ /* The apparent address from which we are reading memory. Note that in
+ some cases GDB actually disassembles instructions from a buffer, so
+ we might not actually be reading this information directly from the
+ inferior memory. This is all hidden behind the read_memory_func API
+ within the disassemble_info structure. */
+ CORE_ADDR address = obj->address + offset;
+
+ /* Setup a buffer to hold the result. */
+ buffer.reset ((gdb_byte *) xmalloc (length));
+
+ /* Read content into BUFFER. If the read fails then raise a memory
+ error, otherwise, convert BUFFER to a Python memory buffer, and return
+ it to the user. */
+ disassemble_info *info = obj->gdb_info;
+ if (info->read_memory_func ((bfd_vma) address, buffer.get (),
+ (unsigned int) length, info) != 0)
+ {
+ disasmpy_set_memory_error_for_address (address);
+ return nullptr;
+ }
+ return gdbpy_buffer_to_membuf (std::move (buffer), address, length);
+}
+
+/* Implement DisassembleInfo.address attribute, return the address at which
+ GDB would like an instruction disassembled. */
+
+static PyObject *
+disasmpy_info_address (PyObject *self, void *closure)
+{
+ disasm_info_object *obj = (disasm_info_object *) self;
+ DISASMPY_DISASM_INFO_REQUIRE_VALID (obj);
+ return gdb_py_object_from_longest (obj->address).release ();
+}
+
+/* Implement DisassembleInfo.architecture attribute. Return the
+ gdb.Architecture in which we are disassembling. */
+
+static PyObject *
+disasmpy_info_architecture (PyObject *self, void *closure)
+{
+ disasm_info_object *obj = (disasm_info_object *) self;
+ DISASMPY_DISASM_INFO_REQUIRE_VALID (obj);
+ return gdbarch_to_arch_object (obj->gdbarch);
+}
+
+/* Implement DisassembleInfo.progspace attribute. Return the
+ gdb.Progspace in which we are disassembling. */
+
+static PyObject *
+disasmpy_info_progspace (PyObject *self, void *closure)
+{
+ disasm_info_object *obj = (disasm_info_object *) self;
+ DISASMPY_DISASM_INFO_REQUIRE_VALID (obj);
+ return pspace_to_pspace_object (obj->program_space).release ();
+}
+
+/* This implements the disassemble_info read_memory_func callback and is
+ called from the libopcodes disassembler when the disassembler wants to
+ read memory.
+
+ From the INFO argument we can find the gdbpy_disassembler object for
+ which we are disassembling, and from that object we can find the
+ DisassembleInfo for the current disassembly call.
+
+ This function reads the instruction bytes by calling the read_memory
+ method on the DisassembleInfo object. This method might have been
+ overridden by user code.
+
+ Read LEN bytes from MEMADDR and place them into BUFF. Return 0 on
+ success (in which case BUFF has been filled), or -1 on error, in which
+ case the contents of BUFF are undefined. */
+
+int
+gdbpy_disassembler::read_memory_func (bfd_vma memaddr, gdb_byte *buff,
+ unsigned int len,
+ struct disassemble_info *info)
+{
+ gdbpy_disassembler *dis
+ = static_cast<gdbpy_disassembler *> (info->application_data);
+ disasm_info_object *obj = dis->py_disasm_info ();
+
+ /* The DisassembleInfo.read_memory method expects an offset from the
+ address stored within the DisassembleInfo object; calculate that
+ offset here. */
+ LONGEST offset = (LONGEST) memaddr - (LONGEST) obj->address;
+
+ /* Now call the DisassembleInfo.read_memory method. This might have been
+ overridden by the user. */
+ gdbpy_ref<> result_obj (PyObject_CallMethod ((PyObject *) obj,
+ "read_memory",
+ "KL", len, offset));
+
+ /* Handle any exceptions. */
+ if (result_obj == nullptr)
+ {
+ /* If we got a gdb.MemoryError then we ignore this and just report
+ that the read failed to the caller. The caller is then
+ responsible for calling the memory_error_func if it wants to.
+ Remember, the disassembler might just be probing to see if these
+ bytes can be read, if we automatically call the memory error
+ function, we can end up registering an error prematurely. */
+ if (PyErr_ExceptionMatches (gdbpy_gdb_memory_error))
+ {
+ PyErr_Clear ();
+ return -1;
+ }
+
+ /* For any other exception type we capture the value of the Python
+ exception and throw it, this will then be caught in
+ disasmpy_builtin_disassemble, at which point the exception will be
+ restored. */
+ throw gdbpy_err_fetch ();
+ }
+
+ /* Convert the result to a buffer. */
+ Py_buffer py_buff;
+ if (!PyObject_CheckBuffer (result_obj.get ())
+ || PyObject_GetBuffer (result_obj.get(), &py_buff, PyBUF_CONTIG_RO) < 0)
+ {
+ PyErr_Format (PyExc_TypeError,
+ _("Result from read_memory is not a buffer"));
+ throw gdbpy_err_fetch ();
+ }
+
+ /* Wrap PY_BUFF so that it is cleaned up correctly at the end of this
+ scope. */
+ Py_buffer_up buffer_up (&py_buff);
+
+ /* Validate that the buffer is the correct length. */
+ if (py_buff.len != len)
+ {
+ PyErr_Format (PyExc_ValueError,
+ _("Buffer returned from read_memory is sized %d instead of the expected %d"),
+ py_buff.len, len);
+ throw gdbpy_err_fetch ();
+ }
+
+ /* Copy the data out of the Python buffer and return success. */
+ const gdb_byte *buffer = (const gdb_byte *) py_buff.buf;
+ memcpy (buff, buffer, len);
+ return 0;
+}
+
+/* Implement DisassemblerResult.length attribute, return the length of the
+ disassembled instruction. */
+
+static PyObject *
+disasmpy_result_length (PyObject *self, void *closure)
+{
+ disasm_result_object *obj = (disasm_result_object *) self;
+ return gdb_py_object_from_longest (obj->length).release ();
+}
+
+/* Implement DisassemblerResult.string attribute, return the content string
+ of the disassembled instruction. */
+
+static PyObject *
+disasmpy_result_string (PyObject *self, void *closure)
+{
+ disasm_result_object *obj = (disasm_result_object *) self;
+
+ gdb_assert (obj->content != nullptr);
+ gdb_assert (strlen (obj->content->c_str ()) > 0);
+ gdb_assert (obj->length > 0);
+ return PyUnicode_Decode (obj->content->c_str (),
+ obj->content->size (),
+ host_charset (), nullptr);
+}
+
+/* Implement DisassemblerResult.__init__. Takes two arguments, an
+ integer, the length in bytes of the disassembled instruction, and a
+ string, the disassembled content of the instruction. */
+
+static int
+disasmpy_result_init (PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ static const char *keywords[] = { "length", "string", NULL };
+ int length;
+ const char *string;
+ if (!gdb_PyArg_ParseTupleAndKeywords (args, kwargs, "is", keywords,
+ &length, &string))
+ return -1;
+
+ if (length <= 0)
+ {
+ PyErr_SetString (PyExc_ValueError,
+ _("Length must be greater than 0."));
+ return -1;
+ }
+
+ if (strlen (string) == 0)
+ {
+ PyErr_SetString (PyExc_ValueError,
+ _("String must not be empty."));
+ return -1;
+ }
+
+ disasm_result_object *obj = (disasm_result_object *) self;
+ disasmpy_init_disassembler_result (obj, length, std::string (string));
+
+ return 0;
+}
+
+/* Implement memory_error_func callback for disassemble_info. Extract the
+ underlying DisassembleInfo Python object, and set a memory error on
+ it. */
+
+void
+gdbpy_disassembler::memory_error_func (int status, bfd_vma memaddr,
+ struct disassemble_info *info)
+{
+ gdbpy_disassembler *dis
+ = static_cast<gdbpy_disassembler *> (info->application_data);
+ dis->m_memory_error_address.emplace (memaddr);
+}
+
+/* Wrapper of print_address. */
+
+void
+gdbpy_disassembler::print_address_func (bfd_vma addr,
+ struct disassemble_info *info)
+{
+ gdbpy_disassembler *dis
+ = static_cast<gdbpy_disassembler *> (info->application_data);
+ print_address (dis->arch (), addr, (struct ui_file *) info->stream);
+}
+
+/* constructor. */
+
+gdbpy_disassembler::gdbpy_disassembler (disasm_info_object *obj,
+ PyObject *memory_source)
+ : gdb_printing_disassembler (obj->gdbarch, &m_string_file,
+ read_memory_func, memory_error_func,
+ print_address_func),
+ m_disasm_info_object (obj),
+ m_memory_source (memory_source)
+{ /* Nothing. */ }
+
+/* A wrapper around a reference to a Python DisassembleInfo object, which
+ ensures that the object is marked as invalid when we leave the enclosing
+ scope.
+
+ Each DisassembleInfo is created in gdbpy_print_insn, and is done with by
+ the time that function returns. However, there's nothing to stop a user
+ caching a reference to the DisassembleInfo, and thus keeping the object
+ around.
+
+ We therefore have the notion of a DisassembleInfo becoming invalid, this
+ happens when gdbpy_print_insn returns. This class is responsible for
+ marking the DisassembleInfo as invalid in its destructor. */
+
+struct scoped_disasm_info_object
+{
+ /* Constructor. */
+ scoped_disasm_info_object (struct gdbarch *gdbarch, CORE_ADDR memaddr,
+ disassemble_info *info)
+ : m_disasm_info (allocate_disasm_info_object ())
+ {
+ disasm_info_fill (m_disasm_info.get (), gdbarch, current_program_space,
+ memaddr, info, nullptr);
+ }
+
+ /* Upon destruction mark m_diasm_info as invalid. */
+ ~scoped_disasm_info_object ()
+ {
+ /* Invalidate the original DisassembleInfo object as well as any copies
+ that the user might have made. */
+ for (disasm_info_object *obj = m_disasm_info.get ();
+ obj != nullptr;
+ obj = obj->next)
+ obj->gdb_info = nullptr;
+ }
+
+ /* Return a pointer to the underlying disasm_info_object instance. */
+ disasm_info_object *
+ get () const
+ {
+ return m_disasm_info.get ();
+ }
+
+private:
+
+ /* Wrapper around the call to PyObject_New, this wrapper function can be
+ called from the constructor initialization list, while PyObject_New, a
+ macro, can't. */
+ static disasm_info_object *
+ allocate_disasm_info_object ()
+ {
+ return (disasm_info_object *) PyObject_New (disasm_info_object,
+ &disasm_info_object_type);
+ }
+
+ /* A reference to a gdb.disassembler.DisassembleInfo object. When this
+ containing instance goes out of scope this reference is released,
+ however, the user might be holding other references to the
+ DisassembleInfo object in Python code, so the underlying object might
+ not be deleted. */
+ gdbpy_ref<disasm_info_object> m_disasm_info;
+};
+
+/* See python-internal.h. */
+
+gdb::optional<int>
+gdbpy_print_insn (struct gdbarch *gdbarch, CORE_ADDR memaddr,
+ disassemble_info *info)
+{
+ /* Early exit case. This must be done as early as possible, and
+ definitely before we enter Python environment. The
+ python_print_insn_enabled flag is set (from Python) only when the user
+ has installed one (or more) Python disassemblers. So in the common
+ case (no custom disassembler installed) this flag will be false,
+ allowing for a quick return. */
+ if (!gdb_python_initialized || !python_print_insn_enabled)
+ return {};
+
+ gdbpy_enter enter_py (get_current_arch (), current_language);
+
+ /* Import the gdb.disassembler module. */
+ gdbpy_ref<> gdb_python_disassembler_module
+ (PyImport_ImportModule ("gdb.disassembler"));
+ if (gdb_python_disassembler_module == nullptr)
+ {
+ gdbpy_print_stack ();
+ return {};
+ }
+
+ /* Get the _print_insn attribute from the module, this should be the
+ function we are going to call to actually perform the disassembly. */
+ gdbpy_ref<> hook
+ (PyObject_GetAttrString (gdb_python_disassembler_module.get (),
+ "_print_insn"));
+ if (hook == nullptr)
+ {
+ gdbpy_print_stack ();
+ return {};
+ }
+
+ /* Create the new DisassembleInfo object we will pass into Python. This
+ object will be marked as invalid when we leave this scope. */
+ scoped_disasm_info_object scoped_disasm_info (gdbarch, memaddr, info);
+ disasm_info_object *disasm_info = scoped_disasm_info.get ();
+
+ /* Call into the registered disassembler to (possibly) perform the
+ disassembly. */
+ PyObject *insn_disas_obj = (PyObject *) disasm_info;
+ gdbpy_ref<> result (PyObject_CallFunctionObjArgs (hook.get (),
+ insn_disas_obj,
+ nullptr));
+
+ if (result == nullptr)
+ {
+ /* The call into Python code resulted in an exception. If this was a
+ gdb.MemoryError, then we can figure out an address and call the
+ disassemble_info::memory_error_func to report the error back to
+ core GDB. Any other exception type we report back to core GDB as
+ an unknown error (return -1 without first calling the
+ memory_error_func callback). */
+
+ if (PyErr_ExceptionMatches (gdbpy_gdb_memory_error))
+ {
+ /* A gdb.MemoryError might have an address attribute which
+ contains the address at which the memory error occurred. If
+ this is the case then use this address, otherwise, fallback to
+ just using the address of the instruction we were asked to
+ disassemble. */
+ gdbpy_err_fetch err;
+ PyErr_Clear ();
+
+ CORE_ADDR addr;
+ if (err.value () != nullptr
+ && PyObject_HasAttrString (err.value ().get (), "address"))
+ {
+ PyObject *addr_obj
+ = PyObject_GetAttrString (err.value ().get (), "address");
+ if (get_addr_from_python (addr_obj, &addr) < 0)
+ addr = disasm_info->address;
+ }
+ else
+ addr = disasm_info->address;
+
+ info->memory_error_func (-1, addr, info);
+ return gdb::optional<int> (-1);
+ }
+ else if (PyErr_ExceptionMatches (gdbpy_gdberror_exc))
+ {
+ gdbpy_err_fetch err;
+ gdb::unique_xmalloc_ptr<char> msg = err.to_string ();
+
+ info->fprintf_func (info->stream, "%s", msg.get ());
+ return gdb::optional<int> (-1);
+ }
+ else
+ {
+ gdbpy_print_stack ();
+ return gdb::optional<int> (-1);
+ }
+
+ }
+ else if (result == Py_None)
+ {
+ /* A return value of None indicates that the Python code could not,
+ or doesn't want to, disassemble this instruction. Just return an
+ empty result and core GDB will try to disassemble this for us. */
+ return {};
+ }
+
+ /* Check the result is a DisassemblerResult (or a sub-class). */
+ if (!PyObject_IsInstance (result.get (),
+ (PyObject *) &disasm_result_object_type))
+ {
+ PyErr_SetString (PyExc_TypeError,
+ _("Result is not a DisassemblerResult."));
+ gdbpy_print_stack ();
+ return gdb::optional<int> (-1);
+ }
+
+ /* The call into Python neither raised an exception, or returned None.
+ Check to see if the result looks valid. */
+ gdbpy_ref<> length_obj (PyObject_GetAttrString (result.get (), "length"));
+ if (length_obj == nullptr)
+ {
+ gdbpy_print_stack ();
+ return gdb::optional<int> (-1);
+ }
+
+ gdbpy_ref<> string_obj (PyObject_GetAttrString (result.get (), "string"));
+ if (string_obj == nullptr)
+ {
+ gdbpy_print_stack ();
+ return gdb::optional<int> (-1);
+ }
+ if (!gdbpy_is_string (string_obj.get ()))
+ {
+ PyErr_SetString (PyExc_TypeError, _("String attribute is not a string."));
+ gdbpy_print_stack ();
+ return gdb::optional<int> (-1);
+ }
+
+ gdb::unique_xmalloc_ptr<char> string
+ = gdbpy_obj_to_string (string_obj.get ());
+ if (string == nullptr)
+ {
+ gdbpy_print_stack ();
+ return gdb::optional<int> (-1);
+ }
+
+ long length;
+ if (!gdb_py_int_as_long (length_obj.get (), &length))
+ {
+ gdbpy_print_stack ();
+ return gdb::optional<int> (-1);
+ }
+
+ long max_insn_length = (gdbarch_max_insn_length_p (gdbarch) ?
+ gdbarch_max_insn_length (gdbarch) : INT_MAX);
+ if (length <= 0)
+ {
+ PyErr_SetString
+ (PyExc_ValueError,
+ _("Invalid length attribute: length must be greater than 0."));
+ gdbpy_print_stack ();
+ return gdb::optional<int> (-1);
+ }
+ if (length > max_insn_length)
+ {
+ PyErr_Format
+ (PyExc_ValueError,
+ _("Invalid length attribute: length %d greater than architecture maximum of %d"),
+ length, max_insn_length);
+ gdbpy_print_stack ();
+ return gdb::optional<int> (-1);
+ }
+
+ if (strlen (string.get ()) == 0)
+ {
+ PyErr_SetString (PyExc_ValueError,
+ _("String attribute must not be empty."));
+ gdbpy_print_stack ();
+ return gdb::optional<int> (-1);
+ }
+
+ /* Print the disassembled instruction back to core GDB, and return the
+ length of the disassembled instruction. */
+ info->fprintf_func (info->stream, "%s", string.get ());
+ return gdb::optional<int> (length);
+}
+
+/* The tp_dealloc callback for the DisassemblerResult type. Takes care of
+ deallocating the content buffer. */
+
+static void
+disasmpy_dealloc_result (PyObject *self)
+{
+ disasm_result_object *obj = (disasm_result_object *) self;
+ delete obj->content;
+ Py_TYPE (self)->tp_free (self);
+}
+
+/* The get/set attributes of the gdb.disassembler.DisassembleInfo type. */
+
+static gdb_PyGetSetDef disasm_info_object_getset[] = {
+ { "address", disasmpy_info_address, nullptr,
+ "Start address of the instruction to disassemble.", nullptr },
+ { "architecture", disasmpy_info_architecture, nullptr,
+ "Architecture to disassemble in", nullptr },
+ { "progspace", disasmpy_info_progspace, nullptr,
+ "Program space to disassemble in", nullptr },
+ { nullptr } /* Sentinel */
+};
+
+/* The methods of the gdb.disassembler.DisassembleInfo type. */
+
+static PyMethodDef disasm_info_object_methods[] = {
+ { "read_memory", (PyCFunction) disasmpy_info_read_memory,
+ METH_VARARGS | METH_KEYWORDS,
+ "read_memory (LEN, OFFSET = 0) -> Octets[]\n\
+Read LEN octets for the instruction to disassemble." },
+ { "is_valid", disasmpy_info_is_valid, METH_NOARGS,
+ "is_valid () -> Boolean.\n\
+Return true if this DisassembleInfo is valid, false if not." },
+ {nullptr} /* Sentinel */
+};
+
+/* The get/set attributes of the gdb.disassembler.DisassemblerResult type. */
+
+static gdb_PyGetSetDef disasm_result_object_getset[] = {
+ { "length", disasmpy_result_length, nullptr,
+ "Length of the disassembled instruction.", nullptr },
+ { "string", disasmpy_result_string, nullptr,
+ "String representing the disassembled instruction.", nullptr },
+ { nullptr } /* Sentinel */
+};
+
+/* These are the methods we add into the _gdb.disassembler module, which
+ are then imported into the gdb.disassembler module. These are global
+ functions that support performing disassembly. */
+
+PyMethodDef python_disassembler_methods[] =
+{
+ { "builtin_disassemble", (PyCFunction) disasmpy_builtin_disassemble,
+ METH_VARARGS | METH_KEYWORDS,
+ "builtin_disassemble (INFO, MEMORY_SOURCE = None) -> None\n\
+Disassemble using GDB's builtin disassembler. INFO is an instance of\n\
+gdb.disassembler.DisassembleInfo. The MEMORY_SOURCE, if not None, should\n\
+be an object with the read_memory method." },
+ { "_set_enabled", (PyCFunction) disasmpy_set_enabled,
+ METH_VARARGS | METH_KEYWORDS,
+ "_set_enabled (STATE) -> None\n\
+Set whether GDB should call into the Python _print_insn code or not." },
+ {nullptr, nullptr, 0, nullptr}
+};
+
+/* Structure to define the _gdb.disassembler module. */
+
+static struct PyModuleDef python_disassembler_module_def =
+{
+ PyModuleDef_HEAD_INIT,
+ "_gdb.disassembler",
+ nullptr,
+ -1,
+ python_disassembler_methods,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr
+};
+
+/* Called to initialize the Python structures in this file. */
+
+int
+gdbpy_initialize_disasm ()
+{
+ /* Create the _gdb.disassembler module, and add it to the _gdb module. */
+
+ PyObject *gdb_disassembler_module;
+ gdb_disassembler_module = PyModule_Create (&python_disassembler_module_def);
+ if (gdb_disassembler_module == nullptr)
+ return -1;
+ PyModule_AddObject(gdb_module, "disassembler", gdb_disassembler_module);
+
+ /* This is needed so that 'import _gdb.disassembler' will work. */
+ PyObject *dict = PyImport_GetModuleDict ();
+ PyDict_SetItemString (dict, "_gdb.disassembler", gdb_disassembler_module);
+
+ disasm_info_object_type.tp_new = PyType_GenericNew;
+ if (PyType_Ready (&disasm_info_object_type) < 0)
+ return -1;
+
+ if (gdb_pymodule_addobject (gdb_disassembler_module, "DisassembleInfo",
+ (PyObject *) &disasm_info_object_type) < 0)
+ return -1;
+
+ disasm_result_object_type.tp_new = PyType_GenericNew;
+ if (PyType_Ready (&disasm_result_object_type) < 0)
+ return -1;
+
+ if (gdb_pymodule_addobject (gdb_disassembler_module, "DisassemblerResult",
+ (PyObject *) &disasm_result_object_type) < 0)
+ return -1;
+
+ return 0;
+}
+
+/* Describe the gdb.disassembler.DisassembleInfo type. */
+
+PyTypeObject disasm_info_object_type = {
+ PyVarObject_HEAD_INIT (nullptr, 0)
+ "gdb.disassembler.DisassembleInfo", /*tp_name*/
+ sizeof (disasm_info_object), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ disasm_info_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 instruction disassembler object", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ disasm_info_object_methods, /* tp_methods */
+ 0, /* tp_members */
+ disasm_info_object_getset, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ disasm_info_init, /* tp_init */
+ 0, /* tp_alloc */
+};
+
+/* Describe the gdb.disassembler.DisassemblerResult type. */
+
+PyTypeObject disasm_result_object_type = {
+ PyVarObject_HEAD_INIT (nullptr, 0)
+ "gdb.disassembler.DisassemblerResult", /*tp_name*/
+ sizeof (disasm_result_object), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ disasmpy_dealloc_result, /*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 object, representing a disassembler result", /* 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 */
+ disasm_result_object_getset, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ disasmpy_result_init, /* tp_init */
+ 0, /* tp_alloc */
+};