From: Doug Evans Date: Wed, 15 Mar 2017 22:35:13 +0000 (-0700) Subject: Fix various python lazy string bugs. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=34b433203b5f56149c27a8dfea21a921392cb158;p=binutils-gdb.git Fix various python lazy string bugs. gdb/ChangeLog: PR python/17728, python/18439, python/18779 * python/py-lazy-string.c (lazy_string_object): Clarify use of LENGTH member. Change type of TYPE member to PyObject *. All uses updated. (stpy_convert_to_value): Fix handling of TYPE_CODE_PTR. (gdbpy_create_lazy_string_object): Flag bad length values. Handle TYPE_CODE_ARRAY with possibly different user-provided length. Handle typedefs in incoming type. (stpy_lazy_string_elt_type): New function. (gdbpy_extract_lazy_string): Call it. * python/py-value.c (valpy_lazy_string): Flag bad length values. Fix handling of TYPE_CODE_PTR. Handle TYPE_CODE_ARRAY. Handle typedefs in incoming type. gdb/testsuite/ChangeLog: PR python/17728, python/18439, python/18779 * gdb.python/py-value.c (main) Delete locals sptr, sn. * gdb.python/py-lazy-string.c (pointer): New typedef. (main): New locals ptr, array, typedef_ptr. * gdb.python/py-value.exp: Move lazy string tests to ... * gdb.python/py-lazy-string.exp: ... here. Add more tests for pointer, array, typedef lazy strings. --- diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 14c9561ee33..7541627fd7f 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,18 @@ +2017-03-16 Doug Evans + + PR python/17728, python/18439, python/18779 + * python/py-lazy-string.c (lazy_string_object): Clarify use of LENGTH + member. Change type of TYPE member to PyObject *. All uses updated. + (stpy_convert_to_value): Fix handling of TYPE_CODE_PTR. + (gdbpy_create_lazy_string_object): Flag bad length values. + Handle TYPE_CODE_ARRAY with possibly different user-provided length. + Handle typedefs in incoming type. + (stpy_lazy_string_elt_type): New function. + (gdbpy_extract_lazy_string): Call it. + * python/py-value.c (valpy_lazy_string): Flag bad length values. + Fix handling of TYPE_CODE_PTR. Handle TYPE_CODE_ARRAY. Handle + typedefs in incoming type. + 2017-03-16 Doug Evans * guile/guile-internal.h (tyscm_scm_to_type): Declare. diff --git a/gdb/python/py-lazy-string.c b/gdb/python/py-lazy-string.c index 4af4566ef64..ab3f4117439 100644 --- a/gdb/python/py-lazy-string.c +++ b/gdb/python/py-lazy-string.c @@ -26,6 +26,7 @@ typedef struct { PyObject_HEAD + /* Holds the address of the lazy string. */ CORE_ADDR address; @@ -35,14 +36,21 @@ typedef struct { encoding when the sting is printed. */ char *encoding; - /* Holds the length of the string in characters. If the - length is -1, then the string will be fetched and encoded up to - the first null of appropriate width. */ + /* If TYPE is an array: If the length is known, then this value is the + array's length, otherwise it is -1. + If TYPE is not an array: Then this value represents the string's length. + In either case, if the value is -1 then the string will be fetched and + encoded up to the first null of appropriate width. */ long length; - /* This attribute holds the type that is represented by the lazy - string's type. */ - struct type *type; + /* This attribute holds the type of the string. + For example if the lazy string was created from a C "char*" then TYPE + represents a C "char*". + To get the type of the character in the string call + stpy_lazy_string_elt_type. + This is recorded as a PyObject so that we take advantage of support for + preserving the type should its owning objfile go away. */ + PyObject *type; } lazy_string_object; extern PyTypeObject lazy_string_object_type @@ -88,11 +96,12 @@ stpy_get_type (PyObject *self, void *closure) { lazy_string_object *str_obj = (lazy_string_object *) self; - return type_to_type_object (str_obj->type); + Py_INCREF (str_obj->type); + return str_obj->type; } static PyObject * -stpy_convert_to_value (PyObject *self, PyObject *args) +stpy_convert_to_value (PyObject *self, PyObject *args) { lazy_string_object *self_string = (lazy_string_object *) self; struct value *val = NULL; @@ -106,7 +115,32 @@ stpy_convert_to_value (PyObject *self, PyObject *args) TRY { - val = value_at_lazy (self_string->type, self_string->address); + struct type *type = type_object_to_type (self_string->type); + struct type *realtype; + + gdb_assert (type != NULL); + realtype = check_typedef (type); + switch (TYPE_CODE (realtype)) + { + case TYPE_CODE_PTR: + /* If a length is specified we need to convert this to an array + of the specified size. */ + if (self_string->length != -1) + { + /* PR 20786: There's no way to specify an array of length zero. + Record a length of [0,-1] which is how Ada does it. Anything + we do is broken, but this is one possible solution. */ + type = lookup_array_range_type (TYPE_TARGET_TYPE (realtype), + 0, self_string->length - 1); + val = value_at_lazy (type, self_string->address); + } + else + val = value_from_pointer (type, self_string->address); + break; + default: + val = value_at_lazy (type, self_string->address); + break; + } } CATCH (except, RETURN_MASK_ALL) { @@ -125,11 +159,24 @@ stpy_dealloc (PyObject *self) xfree (self_string->encoding); } +/* Low level routine to create a object. + + Note: If TYPE is an array, LENGTH either must be -1 (meaning to use the + size of the array, which may itself be unknown in which case a length of + -1 is still used) or must be the length of the array. */ + PyObject * gdbpy_create_lazy_string_object (CORE_ADDR address, long length, - const char *encoding, struct type *type) + const char *encoding, struct type *type) { lazy_string_object *str_obj = NULL; + struct type *realtype; + + if (length < -1) + { + PyErr_SetString (PyExc_ValueError, _("Invalid length.")); + return NULL; + } if (address == 0 && length != 0) { @@ -146,6 +193,27 @@ gdbpy_create_lazy_string_object (CORE_ADDR address, long length, return NULL; } + realtype = check_typedef (type); + switch (TYPE_CODE (realtype)) + { + case TYPE_CODE_ARRAY: + { + LONGEST array_length = -1; + LONGEST low_bound, high_bound; + + if (get_array_bounds (realtype, &low_bound, &high_bound)) + array_length = high_bound - low_bound + 1; + if (length == -1) + length = array_length; + else if (length != array_length) + { + PyErr_SetString (PyExc_ValueError, _("Invalid length.")); + return NULL; + } + break; + } + } + str_obj = PyObject_New (lazy_string_object, &lazy_string_object_type); if (!str_obj) return NULL; @@ -156,7 +224,7 @@ gdbpy_create_lazy_string_object (CORE_ADDR address, long length, str_obj->encoding = NULL; else str_obj->encoding = xstrdup (encoding); - str_obj->type = type; + str_obj->type = type_to_type_object (type); return (PyObject *) str_obj; } @@ -179,12 +247,35 @@ gdbpy_is_lazy_string (PyObject *result) return PyObject_TypeCheck (result, &lazy_string_object_type); } +/* Return the type of a character in lazy string LAZY. */ + +static struct type * +stpy_lazy_string_elt_type (lazy_string_object *lazy) +{ + struct type *type = type_object_to_type (lazy->type); + struct type *realtype; + + gdb_assert (type != NULL); + realtype = check_typedef (type); + + switch (TYPE_CODE (realtype)) + { + case TYPE_CODE_PTR: + case TYPE_CODE_ARRAY: + return TYPE_TARGET_TYPE (realtype); + default: + /* This is done to preserve existing behaviour. PR 20769. + E.g., gdb.parse_and_eval("my_int_variable").lazy_string().type. */ + return realtype; + } +} + /* Extract the parameters from the lazy string object STRING. ENCODING may be set to NULL, if no encoding is found. */ void gdbpy_extract_lazy_string (PyObject *string, CORE_ADDR *addr, - struct type **str_type, + struct type **str_elt_type, long *length, gdb::unique_xmalloc_ptr *encoding) { @@ -195,7 +286,7 @@ gdbpy_extract_lazy_string (PyObject *string, CORE_ADDR *addr, lazy = (lazy_string_object *) string; *addr = lazy->address; - *str_type = lazy->type; + *str_elt_type = stpy_lazy_string_elt_type (lazy); *length = lazy->length; encoding->reset (lazy->encoding ? xstrdup (lazy->encoding) : NULL); } diff --git a/gdb/python/py-value.c b/gdb/python/py-value.c index eb3d307b19b..124d3980139 100644 --- a/gdb/python/py-value.c +++ b/gdb/python/py-value.c @@ -400,9 +400,19 @@ valpy_get_dynamic_type (PyObject *self, void *closure) A lazy string is a pointer to a string with an optional encoding and length. If ENCODING is not given, encoding is set to None. If an ENCODING is provided the encoding parameter is set to ENCODING, but - the string is not encoded. If LENGTH is provided then the length - parameter is set to LENGTH, otherwise length will be set to -1 (first - null of appropriate with). */ + the string is not encoded. + If LENGTH is provided then the length parameter is set to LENGTH. + Otherwise if the value is an array of known length then the array's length + is used. Otherwise the length will be set to -1 (meaning first null of + appropriate with). + + Note: In order to not break any existing uses this allows creating + lazy strings from anything. PR 20769. E.g., + gdb.parse_and_eval("my_int_variable").lazy_string(). + "It's easier to relax restrictions than it is to impose them after the + fact." So we should be flagging any unintended uses as errors, but it's + perhaps too late for that. */ + static PyObject * valpy_lazy_string (PyObject *self, PyObject *args, PyObject *kw) { @@ -416,16 +426,66 @@ valpy_lazy_string (PyObject *self, PyObject *args, PyObject *kw) &user_encoding, &length)) return NULL; + if (length < -1) + { + PyErr_SetString (PyExc_ValueError, _("Invalid length.")); + return NULL; + } + TRY { scoped_value_mark free_values; + struct type *type, *realtype; + CORE_ADDR addr; - if (TYPE_CODE (value_type (value)) == TYPE_CODE_PTR) - value = value_ind (value); + type = value_type (value); + realtype = check_typedef (type); + + switch (TYPE_CODE (realtype)) + { + case TYPE_CODE_ARRAY: + { + LONGEST array_length = -1; + LONGEST low_bound, high_bound; + + /* PR 20786: There's no way to specify an array of length zero. + Record a length of [0,-1] which is how Ada does it. Anything + we do is broken, but this one possible solution. */ + if (get_array_bounds (realtype, &low_bound, &high_bound)) + array_length = high_bound - low_bound + 1; + if (length == -1) + length = array_length; + else if (array_length == -1) + { + type = lookup_array_range_type (TYPE_TARGET_TYPE (realtype), + 0, length - 1); + } + else if (length != array_length) + { + /* We need to create a new array type with the + specified length. */ + if (length > array_length) + error (_("Length is larger than array size.")); + type = lookup_array_range_type (TYPE_TARGET_TYPE (realtype), + low_bound, + low_bound + length - 1); + } + addr = value_address (value); + break; + } + case TYPE_CODE_PTR: + /* If a length is specified we defer creating an array of the + specified width until we need to. */ + addr = value_as_address (value); + break; + default: + /* Should flag an error here. PR 20769. */ + addr = value_address (value); + break; + } - str_obj = gdbpy_create_lazy_string_object (value_address (value), length, - user_encoding, - value_type (value)); + str_obj = gdbpy_create_lazy_string_object (addr, length, user_encoding, + type); } CATCH (except, RETURN_MASK_ALL) { diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 713031cfe1f..92138a4a4eb 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,7 +1,19 @@ +<<<<<<< HEAD 2017-03-16 Thomas Preud'homme * gdb.cp/m-static.exp: Fix expectation for prototype of test5.single_constructor and single_constructor::single_constructor. +======= +2017-03-15 Doug Evans + + PR python/17728, python/18439, python/18779 + * gdb.python/py-value.c (main) Delete locals sptr, sn. + * gdb.python/py-lazy-string.c (pointer): New typedef. + (main): New locals ptr, array, typedef_ptr. + * gdb.python/py-value.exp: Move lazy string tests to ... + * gdb.python/py-lazy-string.exp: ... here. Add more tests for pointer, + array, typedef lazy strings. +>>>>>>> Fix various python lazy string bugs. 2017-03-14 Anton Kolesov diff --git a/gdb/testsuite/gdb.python/py-lazy-string.c b/gdb/testsuite/gdb.python/py-lazy-string.c index 105c559663a..41011ce99c3 100644 --- a/gdb/testsuite/gdb.python/py-lazy-string.c +++ b/gdb/testsuite/gdb.python/py-lazy-string.c @@ -18,6 +18,9 @@ int main () { + const char *ptr = "pointer"; + const char array[] = "array"; + pointer typedef_ptr = "typedef pointer"; const char *null = 0; return 0; /* break here */ diff --git a/gdb/testsuite/gdb.python/py-lazy-string.exp b/gdb/testsuite/gdb.python/py-lazy-string.exp index e3054f629df..ff95a2e12b7 100644 --- a/gdb/testsuite/gdb.python/py-lazy-string.exp +++ b/gdb/testsuite/gdb.python/py-lazy-string.exp @@ -34,9 +34,43 @@ if ![runto_main ] { gdb_breakpoint [gdb_get_line_number "break here"] gdb_continue_to_breakpoint "break here" -gdb_test_no_output "python null = gdb.parse_and_eval(\"null\")" +gdb_py_test_silent_cmd "python null = gdb.parse_and_eval(\"null\")" "get null value" 1 -gdb_test "python print(null.lazy_string(length=0).value())" \ - "gdb.MemoryError: Cannot create a value from NULL.*Error while executing Python code." +gdb_py_test_silent_cmd "python nullstr = null.lazy_string(length=0)" "create a null lazy string" 1 +gdb_test "python print (nullstr.length)" "0" "null lazy string length" +gdb_test "python print (nullstr.address)" "0" "null lazy string address" +gdb_test "python print (nullstr.type)" "const char \\*" "null lazy string type" +gdb_test "python print(nullstr.value())" \ + "gdb.MemoryError: Cannot create a value from NULL.*Error while executing Python code." \ + "create value from NULL" gdb_test "python print(null.lazy_string(length=3).value())" \ - "gdb.MemoryError: Cannot create a lazy string with address 0x0, and a non-zero length.*Error while executing Python code." + "gdb.MemoryError: Cannot create a lazy string with address 0x0, and a non-zero length.*Error while executing Python code." \ + "null lazy string with non-zero length" +gdb_test "python print(null.lazy_string(length=-2))" \ + "ValueError: Invalid length.*Error while executing Python code." \ + "bad length" + +foreach var_spec { { "ptr" "pointer" "const char \\*" -1 } \ + { "array" "array" "const char \\[6\\]" 6 } \ + { "typedef_ptr" "typedef pointer" "pointer" -1 } } { + set var [lindex $var_spec 0] + set value [lindex $var_spec 1] + set type [lindex $var_spec 2] + set length [lindex $var_spec 3] + with_test_prefix $var { + gdb_test "print $var" "\"$value\"" + gdb_py_test_silent_cmd "python $var = gdb.history (0)" "get value from history" 1 + gdb_py_test_silent_cmd "python l$var = $var.lazy_string()" "acquire lazy string" 1 + gdb_test "python print ($var.type)" "$type" "string type name equality" + gdb_test "python print (l$var.type)" "$type" "lazy-string type name equality" + gdb_test "python print (l$var.length)" "$length" "lazy string length" + gdb_test "python print (l$var.value())" "\"$value\"" "lazy string value" + gdb_py_test_silent_cmd "python l2$var = $var.lazy_string(length=2)" "acquire lazy string, length 2" 1 + gdb_test "python print (l2$var.length)" "2" "lazy string length 2" + gdb_test "python print (l2$var.value())" "\"[string range $value 0 1]\"" "lazy string length 2 value" + # This test will have to wait until gdb can handle it. There's no way, + # currently, to internally specify an array of length zero in the C + # language support. PR 20786 + #gdb_test "python print ($var.lazy_string(length=0).value())" "\"\"" "empty lazy string value" + } +} diff --git a/gdb/testsuite/gdb.python/py-value.c b/gdb/testsuite/gdb.python/py-value.c index d90b15e7eea..b25b8a6c73a 100644 --- a/gdb/testsuite/gdb.python/py-value.c +++ b/gdb/testsuite/gdb.python/py-value.c @@ -90,13 +90,11 @@ main (int argc, char *argv[]) char nullst[17] = "divide\0et\0impera"; void (*fp1) (void) = &func1; int (*fp2) (int, int) = &func2; - const char *sptr = "pointer"; const char *embed = "embedded x\201\202\203\204"; int a[3] = {1,2,3}; int *p = a; int i = 2; int *ptr_i = &i; - const char *sn = 0; struct str *xstr; /* Prevent gcc from optimizing argv[] out. */ diff --git a/gdb/testsuite/gdb.python/py-value.exp b/gdb/testsuite/gdb.python/py-value.exp index d9a4d20badb..1781887c4a6 100644 --- a/gdb/testsuite/gdb.python/py-value.exp +++ b/gdb/testsuite/gdb.python/py-value.exp @@ -317,29 +317,6 @@ proc test_value_in_inferior {} { "read string beyond declared size" } -proc test_lazy_strings {} { - - global hex - - gdb_test "print sptr" "\"pointer\"" - gdb_py_test_silent_cmd "python sptr = gdb.history (0)" "Get value from history" 1 - - gdb_py_test_silent_cmd "python lstr = sptr.lazy_string()" "Aquire lazy string" 1 - gdb_test "python print (lstr.type)" "const char \*." "test lazy-string type name equality" - gdb_test "python print (sptr.type)" "const char \*." "test string type name equality" - - # Prevent symbol on address 0x0 being printed. - gdb_test_no_output "set print symbol off" - gdb_test "print sn" "0x0" - - gdb_py_test_silent_cmd "python snptr = gdb.history (0)" "Get value from history" 1 - gdb_test "python snstr = snptr.lazy_string(length=5)" ".*Cannot create a lazy string with address.*" "test lazy string" - gdb_py_test_silent_cmd "python snstr = snptr.lazy_string(length=0)" "Succesfully create a lazy string" 1 - gdb_test "python print (snstr.length)" "0" "test lazy string length" - gdb_test "python print (snstr.address)" "0" "test lazy string address" -} - - proc test_inferior_function_call {} { global gdb_prompt hex decimal @@ -534,7 +511,6 @@ if ![runto_main] then { test_value_in_inferior test_inferior_function_call -test_lazy_strings test_value_after_death # Test either C or C++ values.