diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index e3f336e..417ea66 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -26040,6 +26040,42 @@ A @code{gdb.Architecture} class has the following methods: Return the name (string value) of the architecture. @end defun +@defun Architecture.disassemble (@var{start_pc} @r{[}, @var{end_pc} @r{[}, @var{count}@r{]]}) +Return a list of disassembled instructions starting from the memory +address @var{start_pc}. The optional arguments @var{end_pc} and +@var{count} determine the number of instructions in the returned list. +If both the optional arguments @var{end_pc} and @var{count} are +specified, then a list of at most @var{count} disassembled instructions +whose start address falls in the closed memory address interval from +@var{start_pc} to @var{end_pc} are returned. If @var{end_pc} is not +specified, but @var{count} is specified, then @var{count} number of +instructions starting from the address @var{start_pc} are returned. If +@var{count} is not specified but @var{end_pc} is specified, then all +instructions whose start address falls in the closed memory address +interval from @var{start_pc} to @var{end_pc} are returned. If neither +@var{end_pc} nor @var{count} are specified, then a single instruction at +@var{start_pc} is returned. For all of these cases, each element of the +returned list is a Python @code{dict} with the following string keys: + +@table @code + +@item addr +The value corresponding to this key is a Python long integer capturing +the memory address of the instruction. + +@item asm +The value corresponding to this key is a string value which represents +the instruction with assembly language mnemonics. The assembly +language flavor used is the same as that specified by the current CLI +variable @code{disassembly-flavor}. @xref{Machine Code}. + +@item length +The value corresponding to this key is the length (integer value) of the +instruction in bytes. + +@end table +@end defun + @node Python Auto-loading @subsection Python Auto-loading @cindex Python auto-loading diff --git a/gdb/python/py-arch.c b/gdb/python/py-arch.c index edd508f..b41de06 100644 --- a/gdb/python/py-arch.c +++ b/gdb/python/py-arch.c @@ -20,6 +20,7 @@ #include "defs.h" #include "gdbarch.h" #include "arch-utils.h" +#include "disasm.h" #include "python-internal.h" typedef struct arch_object_type_object { @@ -86,6 +87,145 @@ archpy_name (PyObject *self, PyObject *args) return py_name; } +/* Implementation of + gdb.Architecture.disassemble (self, start_pc [, end_pc [,count]]) -> List. + Returns a list of instructions in a memory address range. Each instruction + in the list is a Python dict object. +*/ + +static PyObject * +archpy_disassemble (PyObject *self, PyObject *args, PyObject *kw) +{ + static char *keywords[] = { "start_pc", "end_pc", "count", NULL }; + CORE_ADDR start, end = 0; + CORE_ADDR pc; + gdb_py_ulongest start_temp; + long count = 0, i; + PyObject *result_list, *end_obj = NULL, *count_obj = NULL; + struct gdbarch *gdbarch = arch_object_to_gdbarch (self); + + if (!PyArg_ParseTupleAndKeywords (args, kw, GDB_PY_LLU_ARG "|OO", keywords, + &start_temp, &end_obj, &count_obj)) + return NULL; + + start = start_temp; + if (end_obj) + { + if (PyObject_TypeCheck (end_obj, &PyInt_Type)) + /* If the end_pc value is specified without a trailing 'L', end_obj will + be an integer and not a long integer. */ + end = PyInt_AsLong (end_obj); + else if (PyObject_TypeCheck (end_obj, &PyLong_Type)) + end = PyLong_AsUnsignedLongLong (end_obj); + else + { + Py_DECREF (end_obj); + Py_XDECREF (count_obj); + PyErr_SetString (PyExc_TypeError, + _("Argument 'end_pc' should be a (long) integer.")); + + return NULL; + } + + if (end < start) + { + Py_DECREF (end_obj); + Py_XDECREF (count_obj); + PyErr_SetString (PyExc_ValueError, + _("Argument 'end_pc' should be greater than or " + "equal to the argument 'start_pc'.")); + + return NULL; + } + } + if (count_obj) + { + count = PyInt_AsLong (count_obj); + if (PyErr_Occurred () || count < 0) + { + Py_DECREF (count_obj); + Py_XDECREF (end_obj); + PyErr_SetString (PyExc_TypeError, + _("Argument 'count' should be an non-negative " + "integer.")); + + return NULL; + } + } + + result_list = PyList_New (0); + if (result_list == NULL) + return NULL; + + for (pc = start, i = 0; + /* All args are specified. */ + (end_obj && count_obj && pc <= end && i < count) + /* end_pc is specified, but no count. */ + || (end_obj && count_obj == NULL && pc <= end) + /* end_pc is not specified, but a count is. */ + || (end_obj == NULL && count_obj && i < count) + /* Both end_pc and count are not specified. */ + || (end_obj == NULL && count_obj == NULL && pc == start);) + { + int insn_len = 0; + char *as = NULL; + struct ui_file *memfile = mem_fileopen (); + PyObject *insn_dict = PyDict_New (); + volatile struct gdb_exception except; + + if (insn_dict == NULL) + { + Py_DECREF (result_list); + ui_file_delete (memfile); + + return NULL; + } + if (PyList_Append (result_list, insn_dict)) + { + Py_DECREF (result_list); + Py_DECREF (insn_dict); + ui_file_delete (memfile); + + return NULL; /* PyList_Append Sets the exception. */ + } + + TRY_CATCH (except, RETURN_MASK_ALL) + { + insn_len = gdb_print_insn (gdbarch, pc, memfile, NULL); + } + if (except.reason < 0) + { + Py_DECREF (result_list); + ui_file_delete (memfile); + + return gdbpy_convert_exception (except); + } + + as = ui_file_xstrdup (memfile, NULL); + if (PyDict_SetItemString (insn_dict, "addr", + gdb_py_long_from_ulongest (pc)) + || PyDict_SetItemString (insn_dict, "asm", + PyString_FromString (*as ? as : "")) + || PyDict_SetItemString (insn_dict, "length", + PyInt_FromLong (insn_len))) + { + Py_DECREF (result_list); + + ui_file_delete (memfile); + xfree (as); + + return NULL; + } + + pc += insn_len; + i++; + ui_file_delete (memfile); + xfree (as); + } + + return result_list; +} + /* Initializes the Architecture class in the gdb module. */ void @@ -105,6 +245,11 @@ static PyMethodDef arch_object_methods [] = { { "name", archpy_name, METH_NOARGS, "name () -> String.\n\ Return the name of the architecture as a string value." }, + { "disassemble", (PyCFunction) archpy_disassemble, + METH_VARARGS | METH_KEYWORDS, + "disassemble (start_pc [, end_pc [, count]]) -> List.\n\ +Return a list of at most COUNT disassembled instructions from START_PC to\n\ +END_PC." }, {NULL} /* Sentinel */ }; diff --git a/gdb/testsuite/gdb.python/Makefile.in b/gdb/testsuite/gdb.python/Makefile.in index 4e286b5..0b81507 100644 --- a/gdb/testsuite/gdb.python/Makefile.in +++ b/gdb/testsuite/gdb.python/Makefile.in @@ -6,7 +6,7 @@ EXECUTABLES = py-type py-value py-prettyprint py-template py-block \ py-shared python lib-types py-events py-evthreads py-frame \ py-mi py-pp-maint py-progspace py-section-script py-objfile \ py-finish-breakpoint py-finish-breakpoint2 py-value-cc py-explore \ - py-explore-cc + py-explore-cc py-arch MISCELLANEOUS = py-shared-sl.sl py-events-shlib.so py-events-shlib-nodebug.so diff --git a/gdb/testsuite/gdb.python/py-arch.c b/gdb/testsuite/gdb.python/py-arch.c new file mode 100644 index 0000000..e2fe55c --- /dev/null +++ b/gdb/testsuite/gdb.python/py-arch.c @@ -0,0 +1,23 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2013 Free Software Foundation, Inc. + + 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 . +*/ + +int +main (void) +{ + return 0; +} diff --git a/gdb/testsuite/gdb.python/py-arch.exp b/gdb/testsuite/gdb.python/py-arch.exp new file mode 100644 index 0000000..4e736b8 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-arch.exp @@ -0,0 +1,54 @@ +# Copyright 2013 Free Software Foundation, Inc. + +# 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 . + +standard_testfile + +if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } { + return -1 +} + +# Skip all tests if Python scripting is not enabled. +if { [skip_python_tests] } { continue } + +if ![runto_main] { + return -1 +} + +gdb_py_test_silent_cmd "python frame = gdb.selected_frame()" "get frame" 0 +gdb_py_test_silent_cmd "python arch = frame.architecture()" "get arch" 0 +gdb_py_test_silent_cmd "python pc = frame.pc()" "get pc" 0 +gdb_py_test_silent_cmd "python insn_list1 = arch.disassemble(pc, pc, 1)" \ + "disassemble" 0 +gdb_py_test_silent_cmd "python insn_list2 = arch.disassemble(pc, pc)" \ + "disassemble no count" 0 +gdb_py_test_silent_cmd "python insn_list3 = arch.disassemble(pc, count=1)" \ + "disassemble no end" 0 +gdb_py_test_silent_cmd "python insn_list4 = arch.disassemble(pc)" \ + "disassemble no end no count" 0 + +gdb_test "python print len(insn_list1)" "1" "test number of instructions 1" +gdb_test "python print len(insn_list2)" "1" "test number of instructions 2" +gdb_test "python print len(insn_list3)" "1" "test number of instructions 3" +gdb_test "python print len(insn_list4)" "1" "test number of instructions 4" + +gdb_py_test_silent_cmd "python insn = insn_list1\[0\]" "get instruction" 0 + +gdb_test "python print \"addr\" in insn" "True" "test key addr" +gdb_test "python print \"asm\" in insn" "True" "test key asm" +gdb_test "python print \"length\" in insn" "True" "test key length" + +# Negative test +gdb_test "python arch.disassemble(0, 0)" ".*gdb\.MemoryError.*" \ + "test exception"