mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2025-02-11 13:02:10 +08:00
PyModule_AddObject steals a reference on success, but not on error, which is why we have gdb_pymodule_addobject. I found one spot still calling the former, which could in theory leak memory on failure. This patch fixes this. In the same function I found an unchecked call to PyDict_SetItemString. This patch fixes this as well. Approved-By: Andrew Burgess <aburgess@redhat.com>
1928 lines
62 KiB
C
1928 lines
62 KiB
C
/* Python interface to instruction disassembly.
|
||
|
||
Copyright (C) 2021-2023 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 "language.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.DisassembleAddressPart type. An object of
|
||
this type represents a small part of a disassembled instruction; a part
|
||
that is an address that should be printed using a call to GDB's
|
||
internal print_address function. */
|
||
|
||
struct disasm_addr_part_object
|
||
{
|
||
PyObject_HEAD
|
||
|
||
/* The address to be formatted. */
|
||
bfd_vma address;
|
||
|
||
/* A gdbarch. This is only needed in the case where the user asks for
|
||
the DisassemblerAddressPart to be converted to a string. When we
|
||
return this part to GDB within a DisassemblerResult then GDB will use
|
||
the gdbarch from the initial disassembly request. */
|
||
struct gdbarch *gdbarch;
|
||
};
|
||
|
||
extern PyTypeObject disasm_addr_part_object_type
|
||
CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("disasm_addr_part_object");
|
||
|
||
/* Implement gdb.disassembler.DisassembleTextPart type. An object of
|
||
this type represents a small part of a disassembled instruction; a part
|
||
that is a piece of test along with an associated style. */
|
||
|
||
struct disasm_text_part_object
|
||
{
|
||
PyObject_HEAD
|
||
|
||
/* The string that is this part. */
|
||
std::string *string;
|
||
|
||
/* The style to use when displaying this part. */
|
||
enum disassembler_style style;
|
||
};
|
||
|
||
extern PyTypeObject disasm_text_part_object_type
|
||
CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("disasm_text_part_object");
|
||
|
||
extern PyTypeObject disasm_part_object_type
|
||
CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("PyObject");
|
||
|
||
/* 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 vector containing all the parts of the disassembled instruction.
|
||
Each part will be a DisassemblerPart sub-class. */
|
||
std::vector<gdbpy_ref<>> *parts;
|
||
};
|
||
|
||
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_disassemble_info
|
||
{
|
||
/* 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) noexcept;
|
||
static void print_address_func (bfd_vma addr,
|
||
struct disassemble_info *info) noexcept;
|
||
static int read_memory_func (bfd_vma memaddr, gdb_byte *buff,
|
||
unsigned int len,
|
||
struct disassemble_info *info) noexcept;
|
||
|
||
/* Callback used as the disassemble_info's fprintf_func callback. The
|
||
DIS_INFO pointer is a pointer to a gdbpy_disassembler object. */
|
||
static int fprintf_func (void *dis_info, const char *format, ...) noexcept
|
||
ATTRIBUTE_PRINTF(2,3);
|
||
|
||
/* Callback used as the disassemble_info's fprintf_styled_func callback.
|
||
The DIS_INFO pointer is a pointer to a gdbpy_disassembler. */
|
||
static int fprintf_styled_func (void *dis_info,
|
||
enum disassembler_style style,
|
||
const char *format, ...) noexcept
|
||
ATTRIBUTE_PRINTF(3,4);
|
||
|
||
/* Helper used by fprintf_func and fprintf_styled_func. This function
|
||
creates a new DisassemblerTextPart and adds it to the disassembler's
|
||
parts list. The actual disassembler is accessed through DIS_INFO,
|
||
which is a pointer to the gdbpy_disassembler object. */
|
||
static int vfprintf_styled_func (void *dis_info,
|
||
enum disassembler_style style,
|
||
const char *format, va_list args) noexcept
|
||
ATTRIBUTE_PRINTF(3,0);
|
||
|
||
/* 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::vector<gdbpy_ref<>> release ()
|
||
{
|
||
return std::move (m_parts);
|
||
}
|
||
|
||
/* If there is a Python exception stored in this disassembler then
|
||
restore it (i.e. set the PyErr_* state), clear the exception within
|
||
this disassembler, and return true. There must be no current
|
||
exception set (i.e. !PyErr_Occurred()) when this function is called,
|
||
as any such exception might get lost.
|
||
|
||
Otherwise, there is no exception stored in this disassembler, return
|
||
false. */
|
||
bool restore_exception ()
|
||
{
|
||
gdb_assert (!PyErr_Occurred ());
|
||
if (m_stored_exception.has_value ())
|
||
{
|
||
gdbpy_err_fetch ex = std::move (*m_stored_exception);
|
||
m_stored_exception.reset ();
|
||
ex.restore ();
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
private:
|
||
|
||
/* The list of all the parts that make up this disassembled instruction.
|
||
This is populated as a result of the callbacks from libopcodes as the
|
||
instruction is disassembled. */
|
||
std::vector<gdbpy_ref<>> m_parts;
|
||
|
||
/* 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;
|
||
|
||
/* Move the exception EX into this disassembler object. */
|
||
void store_exception (gdbpy_err_fetch &&ex)
|
||
{
|
||
/* The only calls to store_exception are from read_memory_func, which
|
||
will return early if there's already an exception stored. */
|
||
gdb_assert (!m_stored_exception.has_value ());
|
||
m_stored_exception.emplace (std::move (ex));
|
||
}
|
||
|
||
/* Return true if there is an exception stored in this disassembler. */
|
||
bool has_stored_exception () const
|
||
{
|
||
return m_stored_exception.has_value ();
|
||
}
|
||
|
||
/* Store a single exception. This is used to pass Python exceptions back
|
||
from ::memory_read to disasmpy_builtin_disassemble. */
|
||
gdb::optional<gdbpy_err_fetch> m_stored_exception;
|
||
};
|
||
|
||
/* 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 __repr__ for the DisassembleInfo type. */
|
||
|
||
static PyObject *
|
||
disasmpy_info_repr (PyObject *self)
|
||
{
|
||
disasm_info_object *obj = (disasm_info_object *) self;
|
||
|
||
const char *arch_name
|
||
= (gdbarch_bfd_arch_info (obj->gdbarch))->printable_name;
|
||
return PyUnicode_FromFormat ("<%s address=%s architecture=%s>",
|
||
Py_TYPE (obj)->tp_name,
|
||
core_addr_to_string_nz (obj->address),
|
||
arch_name);
|
||
}
|
||
|
||
/* 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);
|
||
}
|
||
|
||
/* Create a new DisassemblerTextPart and return a gdbpy_ref wrapper for
|
||
the new object. STR is the string content of the part and STYLE is the
|
||
style to be used when GDB displays this part. */
|
||
|
||
static gdbpy_ref<>
|
||
make_disasm_text_part (std::string &&str, enum disassembler_style style)
|
||
{
|
||
PyTypeObject *type = &disasm_text_part_object_type;
|
||
disasm_text_part_object *text_part
|
||
= (disasm_text_part_object *) type->tp_alloc (type, 0);
|
||
text_part->string = new std::string (str);
|
||
text_part->style = style;
|
||
|
||
return gdbpy_ref<> ((PyObject *) text_part);
|
||
}
|
||
|
||
/* Create a new DisassemblerAddressPart and return a gdbpy_ref wrapper for
|
||
the new object. GDBARCH is the architecture used when formatting the
|
||
address, and ADDRESS is the numerical address to be displayed. */
|
||
|
||
static gdbpy_ref<>
|
||
make_disasm_addr_part (struct gdbarch *gdbarch, CORE_ADDR address)
|
||
{
|
||
PyTypeObject *type = &disasm_addr_part_object_type;
|
||
disasm_addr_part_object *addr_part
|
||
= (disasm_addr_part_object *) type->tp_alloc (type, 0);
|
||
addr_part->address = address;
|
||
addr_part->gdbarch = gdbarch;
|
||
|
||
return gdbpy_ref<> ((PyObject *) addr_part);
|
||
}
|
||
|
||
/* 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)
|
||
|
||
/* Implement DisassembleInfo.text_part method. Creates and returns a new
|
||
DisassemblerTextPart object. */
|
||
|
||
static PyObject *
|
||
disasmpy_info_make_text_part (PyObject *self, PyObject *args,
|
||
PyObject *kwargs)
|
||
{
|
||
disasm_info_object *obj = (disasm_info_object *) self;
|
||
DISASMPY_DISASM_INFO_REQUIRE_VALID (obj);
|
||
|
||
static const char *keywords[] = { "style", "string", NULL };
|
||
int style_num;
|
||
const char *string;
|
||
if (!gdb_PyArg_ParseTupleAndKeywords (args, kwargs, "is", keywords,
|
||
&style_num, &string))
|
||
return nullptr;
|
||
|
||
if (style_num < 0 || style_num > ((int) dis_style_comment_start))
|
||
{
|
||
PyErr_SetString (PyExc_ValueError,
|
||
_("Invalid disassembler style."));
|
||
return nullptr;
|
||
}
|
||
|
||
if (strlen (string) == 0)
|
||
{
|
||
PyErr_SetString (PyExc_ValueError,
|
||
_("String must not be empty."));
|
||
return nullptr;
|
||
}
|
||
|
||
gdbpy_ref<> text_part
|
||
= make_disasm_text_part (std::string (string),
|
||
(enum disassembler_style) style_num);
|
||
return text_part.release ();
|
||
}
|
||
|
||
/* Implement DisassembleInfo.address_part method. Creates and returns a
|
||
new DisassemblerAddressPart object. */
|
||
|
||
static PyObject *
|
||
disasmpy_info_make_address_part (PyObject *self, PyObject *args,
|
||
PyObject *kwargs)
|
||
{
|
||
disasm_info_object *obj = (disasm_info_object *) self;
|
||
DISASMPY_DISASM_INFO_REQUIRE_VALID (obj);
|
||
|
||
static const char *keywords[] = { "address", NULL };
|
||
CORE_ADDR address;
|
||
PyObject *address_object;
|
||
if (!gdb_PyArg_ParseTupleAndKeywords (args, kwargs, "O", keywords,
|
||
&address_object))
|
||
return nullptr;
|
||
|
||
if (get_addr_from_python (address_object, &address) < 0)
|
||
return nullptr;
|
||
|
||
return make_disasm_addr_part (obj->gdbarch, address).release ();
|
||
}
|
||
|
||
/* Return a string representation of TEXT_PART. The returned string does
|
||
not include any styling. */
|
||
|
||
static std::string
|
||
disasmpy_part_to_string (const disasm_text_part_object *text_part)
|
||
{
|
||
gdb_assert (text_part->string != nullptr);
|
||
return *(text_part->string);
|
||
}
|
||
|
||
/* Return a string representation of ADDR_PART. The returned string does
|
||
not include any styling. */
|
||
|
||
static std::string
|
||
disasmpy_part_to_string (const disasm_addr_part_object *addr_part)
|
||
{
|
||
string_file buf;
|
||
print_address (addr_part->gdbarch, addr_part->address, &buf);
|
||
return buf.release ();
|
||
}
|
||
|
||
/* PARTS is a vector of Python objects, each is a sub-class of
|
||
DisassemblerPart. Create a string by concatenating the string
|
||
representation of each part, and return this new string.
|
||
|
||
Converting an address part requires that we call back into GDB core,
|
||
which could throw an exception. As such, calls to this function should
|
||
be wrapped with a try/catch. */
|
||
|
||
static std::string
|
||
disasmpy_parts_list_to_string (const std::vector<gdbpy_ref<>> &parts)
|
||
{
|
||
std::string str;
|
||
for (auto p : parts)
|
||
{
|
||
if (Py_TYPE (p.get ()) == &disasm_text_part_object_type)
|
||
{
|
||
disasm_text_part_object *text_part
|
||
= (disasm_text_part_object *) p.get ();
|
||
str += disasmpy_part_to_string (text_part);
|
||
}
|
||
else
|
||
{
|
||
gdb_assert (Py_TYPE (p.get ()) == &disasm_addr_part_object_type);
|
||
|
||
disasm_addr_part_object *addr_part
|
||
= (disasm_addr_part_object *) p.get ();
|
||
str += disasmpy_part_to_string (addr_part);
|
||
}
|
||
}
|
||
|
||
return str;
|
||
}
|
||
|
||
/* Initialise OBJ, a DisassemblerResult object with LENGTH and PARTS.
|
||
OBJ might already have been initialised, in which case any existing
|
||
content should be discarded before the new PARTS are moved in. */
|
||
|
||
static void
|
||
disasmpy_init_disassembler_result (disasm_result_object *obj, int length,
|
||
std::vector<gdbpy_ref<>> &&parts)
|
||
{
|
||
if (obj->parts == nullptr)
|
||
obj->parts = new std::vector<gdbpy_ref<>>;
|
||
else
|
||
obj->parts->clear ();
|
||
|
||
obj->length = length;
|
||
*(obj->parts) = std::move (parts);
|
||
}
|
||
|
||
/* 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 = gdbarch_print_insn (disasm_info->gdbarch, disasm_info->address,
|
||
disassembler.disasm_info ());
|
||
|
||
/* It is possible that, while calling a user overridden memory read
|
||
function, a Python exception was raised that couldn't be
|
||
translated into a standard memory-error. In this case the first such
|
||
exception is stored in the disassembler and restored here. */
|
||
if (disassembler.restore_exception ())
|
||
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
|
||
{
|
||
auto content = disassembler.release ();
|
||
std::string str;
|
||
|
||
try
|
||
{
|
||
str = disasmpy_parts_list_to_string (content);
|
||
}
|
||
catch (const gdb_exception &except)
|
||
{
|
||
GDB_PY_HANDLE_EXCEPTION (except);
|
||
}
|
||
if (!str.empty ())
|
||
PyErr_SetString (gdbpy_gdberror_exc, str.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. */
|
||
PyTypeObject *type = &disasm_result_object_type;
|
||
gdbpy_ref<disasm_result_object> res
|
||
((disasm_result_object *) type->tp_alloc (type, 0));
|
||
auto content = disassembler.release ();
|
||
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 ();
|
||
}
|
||
|
||
/* Helper function called when the libopcodes disassembler produces some
|
||
output. FORMAT and ARGS are used to create a string which GDB will
|
||
display using STYLE. The string is either added as a new
|
||
DisassemblerTextPart to the list of parts being built in the current
|
||
gdbpy_disassembler object (accessed through DIS_INFO). Or, if the last
|
||
part in the gdbpy_disassembler is a text part in the same STYLE, then
|
||
the new string is appended to the previous part.
|
||
|
||
The merging behaviour make the Python API a little more user friendly,
|
||
some disassemblers produce their output character at a time, there's no
|
||
particular reason for this, it's just how they are implemented. By
|
||
merging parts with the same style we make it easier for the user to
|
||
analyse the disassembler output. */
|
||
|
||
int
|
||
gdbpy_disassembler::vfprintf_styled_func (void *dis_info,
|
||
enum disassembler_style style,
|
||
const char *format,
|
||
va_list args) noexcept
|
||
{
|
||
gdb_disassemble_info *di = (gdb_disassemble_info *) dis_info;
|
||
gdbpy_disassembler *dis
|
||
= gdb::checked_static_cast<gdbpy_disassembler *> (di);
|
||
|
||
if (!dis->m_parts.empty ()
|
||
&& Py_TYPE (dis->m_parts.back ().get ()) == &disasm_text_part_object_type
|
||
&& (((disasm_text_part_object *) dis->m_parts.back ().get ())->style
|
||
== style))
|
||
{
|
||
std::string *string
|
||
= ((disasm_text_part_object *) dis->m_parts.back ().get ())->string;
|
||
string_vappendf (*string, format, args);
|
||
}
|
||
else
|
||
{
|
||
std::string str = string_vprintf (format, args);
|
||
if (str.size () > 0)
|
||
{
|
||
gdbpy_ref<> text_part
|
||
= make_disasm_text_part (std::move (str), style);
|
||
dis->m_parts.emplace_back (std::move (text_part));
|
||
}
|
||
}
|
||
|
||
/* Something non -ve. */
|
||
return 0;
|
||
}
|
||
|
||
/* Disassembler callback for architectures where libopcodes doesn't
|
||
created styled output. In these cases we format all the output using
|
||
the (default) text style. */
|
||
|
||
int
|
||
gdbpy_disassembler::fprintf_func (void *dis_info,
|
||
const char *format, ...) noexcept
|
||
{
|
||
va_list args;
|
||
va_start (args, format);
|
||
vfprintf_styled_func (dis_info, dis_style_text, format, args);
|
||
va_end (args);
|
||
|
||
/* Something non -ve. */
|
||
return 0;
|
||
}
|
||
|
||
/* Disassembler callback for architectures where libopcodes does create
|
||
styled output. Just creates a new text part with the given STYLE. */
|
||
|
||
int
|
||
gdbpy_disassembler::fprintf_styled_func (void *dis_info,
|
||
enum disassembler_style style,
|
||
const char *format, ...) noexcept
|
||
{
|
||
va_list args;
|
||
va_start (args, format);
|
||
vfprintf_styled_func (dis_info, style, format, args);
|
||
va_end (args);
|
||
|
||
/* Something non -ve. */
|
||
return 0;
|
||
}
|
||
|
||
/* 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) noexcept
|
||
{
|
||
gdbpy_disassembler *dis
|
||
= static_cast<gdbpy_disassembler *> (info->application_data);
|
||
disasm_info_object *obj = dis->py_disasm_info ();
|
||
|
||
/* If a previous read attempt resulted in an exception, then we don't
|
||
allow any further reads to succeed. We only do this check for the
|
||
read_memory_func as this is the only one the user can hook into,
|
||
thus, this check prevents us calling back into user code if a
|
||
previous call has already thrown an error. */
|
||
if (dis->has_stored_exception ())
|
||
return -1;
|
||
|
||
/* 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. */
|
||
dis->store_exception (gdbpy_err_fetch ());
|
||
return -1;
|
||
}
|
||
|
||
/* 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"));
|
||
dis->store_exception (gdbpy_err_fetch ());
|
||
return -1;
|
||
}
|
||
|
||
/* 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);
|
||
dis->store_exception (gdbpy_err_fetch ());
|
||
return -1;
|
||
}
|
||
|
||
/* 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 __str__ for the DisassemblerResult type. */
|
||
|
||
static PyObject *
|
||
disasmpy_result_str (PyObject *self)
|
||
{
|
||
disasm_result_object *obj = (disasm_result_object *) self;
|
||
|
||
/* These conditions are all enforced when the DisassemblerResult object
|
||
is created. */
|
||
gdb_assert (obj->parts != nullptr);
|
||
gdb_assert (obj->parts->size () > 0);
|
||
gdb_assert (obj->length > 0);
|
||
|
||
std::string str;
|
||
|
||
try
|
||
{
|
||
str = disasmpy_parts_list_to_string (*obj->parts);
|
||
}
|
||
catch (const gdb_exception &except)
|
||
{
|
||
GDB_PY_HANDLE_EXCEPTION (except);
|
||
}
|
||
|
||
return PyUnicode_Decode (str.c_str (), str.size (),
|
||
host_charset (), nullptr);
|
||
}
|
||
|
||
/* 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)
|
||
{
|
||
return disasmpy_result_str (self);
|
||
}
|
||
|
||
/* Implement DisassemblerResult.parts method. Returns a list of all the
|
||
parts that make up this result. There should always be at least one
|
||
part, so the returned list should never be empty. */
|
||
|
||
static PyObject *
|
||
disasmpy_result_parts (PyObject *self, void *closure)
|
||
{
|
||
disasm_result_object *obj = (disasm_result_object *) self;
|
||
|
||
/* These conditions are all enforced when the DisassemblerResult object
|
||
is created. */
|
||
gdb_assert (obj->parts != nullptr);
|
||
gdb_assert (obj->parts->size () > 0);
|
||
gdb_assert (obj->length > 0);
|
||
|
||
gdbpy_ref<> result_list (PyList_New (obj->parts->size ()));
|
||
if (result_list == nullptr)
|
||
return nullptr;
|
||
Py_ssize_t idx = 0;
|
||
for (auto p : *obj->parts)
|
||
{
|
||
gdbpy_ref<> item = gdbpy_ref<>::new_reference (p.get ());
|
||
PyList_SET_ITEM (result_list.get (), idx, item.release ());
|
||
++idx;
|
||
}
|
||
|
||
/* This should follow naturally from the obj->parts list being
|
||
non-empty. */
|
||
gdb_assert (PyList_Size (result_list.get()) > 0);
|
||
|
||
return result_list.release ();
|
||
}
|
||
|
||
/* 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", "parts", NULL };
|
||
int length;
|
||
const char *string = nullptr;
|
||
PyObject *parts_list = nullptr;
|
||
if (!gdb_PyArg_ParseTupleAndKeywords (args, kwargs, "i|zO", keywords,
|
||
&length, &string, &parts_list))
|
||
return -1;
|
||
|
||
if (length <= 0)
|
||
{
|
||
PyErr_SetString (PyExc_ValueError,
|
||
_("Length must be greater than 0."));
|
||
return -1;
|
||
}
|
||
|
||
if (parts_list == Py_None)
|
||
parts_list = nullptr;
|
||
|
||
if (string != nullptr && parts_list != nullptr)
|
||
{
|
||
PyErr_Format (PyExc_ValueError,
|
||
_("Cannot use 'string' and 'parts' when creating %s."),
|
||
Py_TYPE (self)->tp_name);
|
||
return -1;
|
||
}
|
||
|
||
if (string != nullptr)
|
||
{
|
||
if (strlen (string) == 0)
|
||
{
|
||
PyErr_SetString (PyExc_ValueError,
|
||
_("String must not be empty."));
|
||
return -1;
|
||
}
|
||
|
||
disasm_result_object *obj = (disasm_result_object *) self;
|
||
std::vector<gdbpy_ref<>> content;
|
||
gdbpy_ref<> text_part
|
||
= make_disasm_text_part (std::string (string), dis_style_text);
|
||
content.emplace_back (text_part.release ());
|
||
disasmpy_init_disassembler_result (obj, length, std::move (content));
|
||
}
|
||
else
|
||
{
|
||
if (!PySequence_Check (parts_list))
|
||
{
|
||
PyErr_SetString (PyExc_TypeError,
|
||
_("'parts' argument is not a sequence"));
|
||
return -1;
|
||
}
|
||
|
||
Py_ssize_t parts_count = PySequence_Size (parts_list);
|
||
if (parts_count <= 0)
|
||
{
|
||
PyErr_SetString (PyExc_ValueError,
|
||
_("'parts' list must not be empty."));
|
||
return -1;
|
||
}
|
||
|
||
disasm_result_object *obj = (disasm_result_object *) self;
|
||
std::vector<gdbpy_ref<>> content (parts_count);
|
||
|
||
struct gdbarch *gdbarch = nullptr;
|
||
for (Py_ssize_t i = 0; i < parts_count; ++i)
|
||
{
|
||
gdbpy_ref<> part (PySequence_GetItem (parts_list, i));
|
||
|
||
if (part == nullptr)
|
||
return -1;
|
||
|
||
if (Py_TYPE (part.get ()) == &disasm_addr_part_object_type)
|
||
{
|
||
disasm_addr_part_object *addr_part
|
||
= (disasm_addr_part_object *) part.get ();
|
||
gdb_assert (addr_part->gdbarch != nullptr);
|
||
if (gdbarch == nullptr)
|
||
gdbarch = addr_part->gdbarch;
|
||
else if (addr_part->gdbarch != gdbarch)
|
||
{
|
||
PyErr_SetString (PyExc_ValueError,
|
||
_("Inconsistent gdb.Architectures used "
|
||
"in 'parts' sequence."));
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
content[i] = std::move (part);
|
||
}
|
||
|
||
disasmpy_init_disassembler_result (obj, length, std::move (content));
|
||
}
|
||
|
||
return 0;
|
||
|
||
}
|
||
|
||
/* Implement __repr__ for the DisassemblerResult type. */
|
||
|
||
static PyObject *
|
||
disasmpy_result_repr (PyObject *self)
|
||
{
|
||
disasm_result_object *obj = (disasm_result_object *) self;
|
||
|
||
gdb_assert (obj->parts != nullptr);
|
||
|
||
return PyUnicode_FromFormat ("<%s length=%d string=\"%U\">",
|
||
Py_TYPE (obj)->tp_name,
|
||
obj->length,
|
||
disasmpy_result_str (self));
|
||
}
|
||
|
||
/* 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) noexcept
|
||
{
|
||
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) noexcept
|
||
{
|
||
gdbpy_disassembler *dis
|
||
= static_cast<gdbpy_disassembler *> (info->application_data);
|
||
|
||
gdbpy_ref<> addr_part
|
||
= make_disasm_addr_part (dis->arch (), addr);
|
||
dis->m_parts.emplace_back (std::move (addr_part));
|
||
}
|
||
|
||
/* constructor. */
|
||
|
||
gdbpy_disassembler::gdbpy_disassembler (disasm_info_object *obj,
|
||
PyObject *memory_source)
|
||
: gdb_disassemble_info (obj->gdbarch,
|
||
read_memory_func,
|
||
memory_error_func,
|
||
print_address_func,
|
||
fprintf_func,
|
||
fprintf_styled_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_disasm_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 result from the Python disassembler has the correct type. Convert
|
||
this back to the underlying C++ object and read the state directly
|
||
from this object. */
|
||
struct disasm_result_object *result_obj
|
||
= (struct disasm_result_object *) result.get ();
|
||
|
||
/* Validate the length of the disassembled instruction. */
|
||
long length = result_obj->length;
|
||
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);
|
||
}
|
||
|
||
/* It is impossible to create a DisassemblerResult object with an empty
|
||
parts list. We know that each part results in a non-empty string, so
|
||
we know that the instruction disassembly will not be the empty
|
||
string. */
|
||
gdb_assert (result_obj->parts->size () > 0);
|
||
|
||
/* Now print out the parts that make up this instruction. */
|
||
for (auto &p : *result_obj->parts)
|
||
{
|
||
if (Py_TYPE (p.get ()) == &disasm_text_part_object_type)
|
||
{
|
||
disasm_text_part_object *text_part
|
||
= (disasm_text_part_object *) p.get ();
|
||
gdb_assert (text_part->string != nullptr);
|
||
info->fprintf_styled_func (info->stream, text_part->style,
|
||
"%s", text_part->string->c_str ());
|
||
}
|
||
else
|
||
{
|
||
gdb_assert (Py_TYPE (p.get ()) == &disasm_addr_part_object_type);
|
||
disasm_addr_part_object *addr_part
|
||
= (disasm_addr_part_object *) p.get ();
|
||
/* A DisassemblerAddressPart can only be created by calling a
|
||
method on DisassembleInfo, and the gdbarch is copied from the
|
||
DisassembleInfo into the DisassemblerAddressPart. As the
|
||
DisassembleInfo has its gdbarch initialised from GDBARCH in
|
||
this scope, and this architecture can't be changed, then the
|
||
following assert should hold. */
|
||
gdb_assert (addr_part->gdbarch == gdbarch);
|
||
info->print_address_func (addr_part->address, info);
|
||
}
|
||
}
|
||
|
||
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->parts;
|
||
Py_TYPE (self)->tp_free (self);
|
||
}
|
||
|
||
/* The tp_init callback for the DisassemblerPart type. This just raises an
|
||
exception, which prevents the user from creating objects of this type.
|
||
Instead the user should create instances of a sub-class. */
|
||
|
||
static int
|
||
disasmpy_part_init (PyObject *self, PyObject *args, PyObject *kwargs)
|
||
{
|
||
PyErr_SetString (PyExc_RuntimeError,
|
||
_("Cannot create instances of DisassemblerPart."));
|
||
return -1;
|
||
}
|
||
|
||
/* Return a string representing STYLE. The returned string is used as a
|
||
constant defined in the gdb.disassembler module. */
|
||
|
||
static const char *
|
||
get_style_name (enum disassembler_style style)
|
||
{
|
||
switch (style)
|
||
{
|
||
case dis_style_text: return "STYLE_TEXT";
|
||
case dis_style_mnemonic: return "STYLE_MNEMONIC";
|
||
case dis_style_sub_mnemonic: return "STYLE_SUB_MNEMONIC";
|
||
case dis_style_assembler_directive: return "STYLE_ASSEMBLER_DIRECTIVE";
|
||
case dis_style_register: return "STYLE_REGISTER";
|
||
case dis_style_immediate: return "STYLE_IMMEDIATE";
|
||
case dis_style_address: return "STYLE_ADDRESS";
|
||
case dis_style_address_offset: return "STYLE_ADDRESS_OFFSET";
|
||
case dis_style_symbol: return "STYLE_SYMBOL";
|
||
case dis_style_comment_start: return "STYLE_COMMENT_START";
|
||
}
|
||
|
||
gdb_assert_not_reached ("unknown disassembler style");
|
||
}
|
||
|
||
/* Implement DisassemblerTextPart.__repr__ method. */
|
||
|
||
static PyObject *
|
||
disasmpy_text_part_repr (PyObject *self)
|
||
{
|
||
disasm_text_part_object *obj = (disasm_text_part_object *) self;
|
||
|
||
gdb_assert (obj->string != nullptr);
|
||
|
||
return PyUnicode_FromFormat ("<%s string='%s', style='%s'>",
|
||
Py_TYPE (obj)->tp_name,
|
||
obj->string->c_str (),
|
||
get_style_name (obj->style));
|
||
}
|
||
|
||
/* Implement DisassemblerTextPart.__str__ attribute. */
|
||
|
||
static PyObject *
|
||
disasmpy_text_part_str (PyObject *self)
|
||
{
|
||
disasm_text_part_object *obj = (disasm_text_part_object *) self;
|
||
|
||
return PyUnicode_Decode (obj->string->c_str (), obj->string->size (),
|
||
host_charset (), nullptr);
|
||
}
|
||
|
||
/* Implement DisassemblerTextPart.string attribute. */
|
||
|
||
static PyObject *
|
||
disasmpy_text_part_string (PyObject *self, void *closure)
|
||
{
|
||
return disasmpy_text_part_str (self);
|
||
}
|
||
|
||
/* Implement DisassemblerTextPart.style attribute. */
|
||
|
||
static PyObject *
|
||
disasmpy_text_part_style (PyObject *self, void *closure)
|
||
{
|
||
disasm_text_part_object *obj = (disasm_text_part_object *) self;
|
||
|
||
LONGEST style_val = (LONGEST) obj->style;
|
||
return gdb_py_object_from_longest (style_val).release ();
|
||
}
|
||
|
||
/* Implement DisassemblerAddressPart.__repr__ method. */
|
||
|
||
static PyObject *
|
||
disasmpy_addr_part_repr (PyObject *self)
|
||
{
|
||
disasm_addr_part_object *obj = (disasm_addr_part_object *) self;
|
||
|
||
return PyUnicode_FromFormat ("<%s address='%s'>",
|
||
Py_TYPE (obj)->tp_name,
|
||
core_addr_to_string_nz (obj->address));
|
||
}
|
||
|
||
/* Implement DisassemblerAddressPart.__str__ attribute. */
|
||
|
||
static PyObject *
|
||
disasmpy_addr_part_str (PyObject *self)
|
||
{
|
||
disasm_addr_part_object *obj = (disasm_addr_part_object *) self;
|
||
|
||
std::string str;
|
||
try
|
||
{
|
||
string_file buf;
|
||
print_address (obj->gdbarch, obj->address, &buf);
|
||
str = buf.release ();
|
||
}
|
||
catch (const gdb_exception &except)
|
||
{
|
||
GDB_PY_HANDLE_EXCEPTION (except);
|
||
}
|
||
|
||
return PyUnicode_Decode (str.c_str (), str.size (),
|
||
host_charset (), nullptr);
|
||
}
|
||
|
||
/* Implement DisassemblerAddressPart.string attribute. */
|
||
|
||
static PyObject *
|
||
disasmpy_addr_part_string (PyObject *self, void *closure)
|
||
{
|
||
return disasmpy_addr_part_str (self);
|
||
}
|
||
|
||
/* Implement DisassemblerAddressPart.address attribute. */
|
||
|
||
static PyObject *
|
||
disasmpy_addr_part_address (PyObject *self, void *closure)
|
||
{
|
||
disasm_addr_part_object *obj = (disasm_addr_part_object *) self;
|
||
|
||
return gdb_py_object_from_longest (obj->address).release ();
|
||
}
|
||
|
||
/* 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." },
|
||
{ "text_part", (PyCFunction) disasmpy_info_make_text_part,
|
||
METH_VARARGS | METH_KEYWORDS,
|
||
"text_part (STRING, STYLE) -> DisassemblerTextPart\n\
|
||
Create a new text part, with contents STRING styled with STYLE." },
|
||
{ "address_part", (PyCFunction) disasmpy_info_make_address_part,
|
||
METH_VARARGS | METH_KEYWORDS,
|
||
"address_part (ADDRESS) -> DisassemblerAddressPart\n\
|
||
Create a new address part representing ADDRESS." },
|
||
{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 },
|
||
{ "parts", disasmpy_result_parts, nullptr,
|
||
"List of all the separate disassembly parts", nullptr },
|
||
{ nullptr } /* Sentinel */
|
||
};
|
||
|
||
/* The get/set attributes of the gdb.disassembler.DisassemblerTextPart type. */
|
||
|
||
static gdb_PyGetSetDef disasmpy_text_part_getset[] = {
|
||
{ "string", disasmpy_text_part_string, nullptr,
|
||
"String representing a text part.", nullptr },
|
||
{ "style", disasmpy_text_part_style, nullptr,
|
||
"The style of this text part.", nullptr },
|
||
{ nullptr } /* Sentinel */
|
||
};
|
||
|
||
/* The get/set attributes of the gdb.disassembler.DisassemblerAddressPart type. */
|
||
|
||
static gdb_PyGetSetDef disasmpy_addr_part_getset[] = {
|
||
{ "string", disasmpy_addr_part_string, nullptr,
|
||
"String representing an address part.", nullptr },
|
||
{ "address", disasmpy_addr_part_address, nullptr,
|
||
"The address of this address part.", 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. */
|
||
|
||
static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
|
||
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;
|
||
if (gdb_pymodule_addobject (gdb_module, "disassembler",
|
||
gdb_disassembler_module) < 0)
|
||
return -1;
|
||
|
||
/* This is needed so that 'import _gdb.disassembler' will work. */
|
||
PyObject *dict = PyImport_GetModuleDict ();
|
||
if (PyDict_SetItemString (dict, "_gdb.disassembler",
|
||
gdb_disassembler_module) < 0)
|
||
return -1;
|
||
|
||
for (int i = 0; i <= (int) dis_style_comment_start; ++i)
|
||
{
|
||
const char *style_name = get_style_name ((enum disassembler_style) i);
|
||
if (PyModule_AddIntConstant (gdb_disassembler_module, style_name, i) < 0)
|
||
return -1;
|
||
}
|
||
|
||
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;
|
||
|
||
disasm_part_object_type.tp_new = PyType_GenericNew;
|
||
if (PyType_Ready (&disasm_part_object_type) < 0)
|
||
return -1;
|
||
|
||
if (gdb_pymodule_addobject (gdb_disassembler_module, "DisassemblerPart",
|
||
(PyObject *) &disasm_part_object_type) < 0)
|
||
return -1;
|
||
|
||
disasm_addr_part_object_type.tp_new = PyType_GenericNew;
|
||
if (PyType_Ready (&disasm_addr_part_object_type) < 0)
|
||
return -1;
|
||
|
||
if (gdb_pymodule_addobject (gdb_disassembler_module,
|
||
"DisassemblerAddressPart",
|
||
(PyObject *) &disasm_addr_part_object_type) < 0)
|
||
return -1;
|
||
|
||
disasm_text_part_object_type.tp_new = PyType_GenericNew;
|
||
if (PyType_Ready (&disasm_text_part_object_type) < 0)
|
||
return -1;
|
||
|
||
if (gdb_pymodule_addobject (gdb_disassembler_module,
|
||
"DisassemblerTextPart",
|
||
(PyObject *) &disasm_text_part_object_type) < 0)
|
||
return -1;
|
||
|
||
return 0;
|
||
}
|
||
|
||
GDBPY_INITIALIZE_FILE (gdbpy_initialize_disasm);
|
||
|
||
|
||
|
||
/* 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*/
|
||
disasmpy_info_repr, /*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*/
|
||
disasmpy_result_repr, /*tp_repr*/
|
||
0, /*tp_as_number*/
|
||
0, /*tp_as_sequence*/
|
||
0, /*tp_as_mapping*/
|
||
0, /*tp_hash */
|
||
0, /*tp_call*/
|
||
disasmpy_result_str, /*tp_str*/
|
||
0, /*tp_getattro*/
|
||
0, /*tp_setattro*/
|
||
0, /*tp_as_buffer*/
|
||
Py_TPFLAGS_DEFAULT, /*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 */
|
||
};
|
||
|
||
/* Describe the gdb.disassembler.DisassemblerPart type. This type exists
|
||
only as an abstract base-class for the various part sub-types. The
|
||
init method for this type throws an error. As such we don't both to
|
||
provide a tp_repr method for this parent class. */
|
||
|
||
PyTypeObject disasm_part_object_type = {
|
||
PyVarObject_HEAD_INIT (nullptr, 0)
|
||
"gdb.disassembler.DisassemblerPart", /*tp_name*/
|
||
sizeof (PyObject), /*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 object, representing part of a disassembled instruction", /* 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 */
|
||
disasmpy_part_init, /* tp_init */
|
||
0, /* tp_alloc */
|
||
};
|
||
|
||
/* Describe the gdb.disassembler.DisassemblerTextPart type. */
|
||
|
||
PyTypeObject disasm_text_part_object_type = {
|
||
PyVarObject_HEAD_INIT (nullptr, 0)
|
||
"gdb.disassembler.DisassemblerTextPart", /*tp_name*/
|
||
sizeof (disasm_text_part_object_type), /*tp_basicsize*/
|
||
0, /*tp_itemsize*/
|
||
0, /*tp_dealloc*/
|
||
0, /*tp_print*/
|
||
0, /*tp_getattr*/
|
||
0, /*tp_setattr*/
|
||
0, /*tp_compare*/
|
||
disasmpy_text_part_repr, /*tp_repr*/
|
||
0, /*tp_as_number*/
|
||
0, /*tp_as_sequence*/
|
||
0, /*tp_as_mapping*/
|
||
0, /*tp_hash */
|
||
0, /*tp_call*/
|
||
disasmpy_text_part_str, /*tp_str*/
|
||
0, /*tp_getattro*/
|
||
0, /*tp_setattro*/
|
||
0, /*tp_as_buffer*/
|
||
Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
||
"GDB object, representing a text part of an instruction", /* 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 */
|
||
disasmpy_text_part_getset, /* tp_getset */
|
||
&disasm_part_object_type, /* tp_base */
|
||
0, /* tp_dict */
|
||
0, /* tp_descr_get */
|
||
0, /* tp_descr_set */
|
||
0, /* tp_dictoffset */
|
||
0, /* tp_init */
|
||
0, /* tp_alloc */
|
||
};
|
||
|
||
/* Describe the gdb.disassembler.DisassemblerAddressPart type. */
|
||
|
||
PyTypeObject disasm_addr_part_object_type = {
|
||
PyVarObject_HEAD_INIT (nullptr, 0)
|
||
"gdb.disassembler.DisassemblerAddressPart", /*tp_name*/
|
||
sizeof (disasm_addr_part_object), /*tp_basicsize*/
|
||
0, /*tp_itemsize*/
|
||
0, /*tp_dealloc*/
|
||
0, /*tp_print*/
|
||
0, /*tp_getattr*/
|
||
0, /*tp_setattr*/
|
||
0, /*tp_compare*/
|
||
disasmpy_addr_part_repr, /*tp_repr*/
|
||
0, /*tp_as_number*/
|
||
0, /*tp_as_sequence*/
|
||
0, /*tp_as_mapping*/
|
||
0, /*tp_hash */
|
||
0, /*tp_call*/
|
||
disasmpy_addr_part_str, /*tp_str*/
|
||
0, /*tp_getattro*/
|
||
0, /*tp_setattro*/
|
||
0, /*tp_as_buffer*/
|
||
Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
||
"GDB object, representing an address part of an instruction", /* 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 */
|
||
disasmpy_addr_part_getset, /* tp_getset */
|
||
&disasm_part_object_type, /* tp_base */
|
||
0, /* tp_dict */
|
||
0, /* tp_descr_get */
|
||
0, /* tp_descr_set */
|
||
0, /* tp_dictoffset */
|
||
0, /* tp_init */
|
||
0, /* tp_alloc */
|
||
};
|