|  | /* 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, dis->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 */ | 
|  | }; |