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