diff options
-rw-r--r-- | gdb/ada-lang.c | 13 | ||||
-rw-r--r-- | gdb/c-lang.c | 14 | ||||
-rw-r--r-- | gdb/cli/cli-cmds.c | 18 | ||||
-rw-r--r-- | gdb/f-lang.c | 10 | ||||
-rw-r--r-- | gdb/f-lang.h | 5 | ||||
-rw-r--r-- | gdb/guile/scm-math.c | 4 | ||||
-rw-r--r-- | gdb/language.c | 10 | ||||
-rw-r--r-- | gdb/language.h | 6 | ||||
-rw-r--r-- | gdb/python/py-value.c | 8 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/internal-string-values.c | 32 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/internal-string-values.exp | 279 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/print-internal-string.c | 56 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/print-internal-string.exp | 64 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/settings.exp | 2 | ||||
-rw-r--r-- | gdb/testsuite/gdb.python/py-mi.exp | 2 | ||||
-rw-r--r-- | gdb/valops.c | 23 | ||||
-rw-r--r-- | gdb/value.c | 5 | ||||
-rw-r--r-- | gdb/value.h | 41 |
18 files changed, 544 insertions, 48 deletions
diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c index 61e477b..3c86c31 100644 --- a/gdb/ada-lang.c +++ b/gdb/ada-lang.c @@ -13525,6 +13525,19 @@ public: } /* See language.h. */ + struct value *value_string (struct gdbarch *gdbarch, + const char *ptr, ssize_t len) const override + { + struct type *type = language_string_char_type (this, gdbarch); + value *val = ::value_string (ptr, len, type); + /* VAL will be a TYPE_CODE_STRING, but Ada only knows how to print + strings that are arrays of characters, so fix the type now. */ + gdb_assert (val->type ()->code () == TYPE_CODE_STRING); + val->type ()->set_code (TYPE_CODE_ARRAY); + return val; + } + + /* See language.h. */ void language_arch_info (struct gdbarch *gdbarch, struct language_arch_info *lai) const override { diff --git a/gdb/c-lang.c b/gdb/c-lang.c index 484f81e..b71457b 100644 --- a/gdb/c-lang.c +++ b/gdb/c-lang.c @@ -652,16 +652,11 @@ c_string_operation::evaluate (struct type *expect_type, } else { - int i; - - /* Write the terminating character. */ - for (i = 0; i < type->length (); ++i) - obstack_1grow (&output, 0); + int element_size = type->length (); if (satisfy_expected) { LONGEST low_bound, high_bound; - int element_size = type->length (); if (!get_discrete_bounds (expect_type->index_type (), &low_bound, &high_bound)) @@ -676,10 +671,13 @@ c_string_operation::evaluate (struct type *expect_type, result = value::allocate (expect_type); memcpy (result->contents_raw ().data (), obstack_base (&output), obstack_object_size (&output)); + /* Write the terminating character. */ + memset (result->contents_raw ().data () + obstack_object_size (&output), + 0, element_size); } else - result = value_cstring ((const char *) obstack_base (&output), - obstack_object_size (&output), + result = value_cstring ((const gdb_byte *) obstack_base (&output), + obstack_object_size (&output) / element_size, type); } return result; diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c index b7b6530..8994cc5 100644 --- a/gdb/cli/cli-cmds.c +++ b/gdb/cli/cli-cmds.c @@ -2315,12 +2315,7 @@ value_from_setting (const setting &var, struct gdbarch *gdbarch) len = st.length (); } - if (len > 0) - return value_cstring (value, len, - builtin_type (gdbarch)->builtin_char); - else - return value_cstring ("", 1, - builtin_type (gdbarch)->builtin_char); + return current_language->value_string (gdbarch, value, len); } default: gdb_assert_not_reached ("bad var_type"); @@ -2372,8 +2367,8 @@ str_value_from_setting (const setting &var, struct gdbarch *gdbarch) { std::string cmd_val = get_setshow_command_value_string (var); - return value_cstring (cmd_val.c_str (), cmd_val.size (), - builtin_type (gdbarch)->builtin_char); + return current_language->value_string (gdbarch, cmd_val.c_str (), + cmd_val.size ()); } case var_string: @@ -2400,12 +2395,7 @@ str_value_from_setting (const setting &var, struct gdbarch *gdbarch) len = st.length (); } - if (len > 0) - return value_cstring (value, len, - builtin_type (gdbarch)->builtin_char); - else - return value_cstring ("", 1, - builtin_type (gdbarch)->builtin_char); + return current_language->value_string (gdbarch, value, len); } default: gdb_assert_not_reached ("bad var_type"); diff --git a/gdb/f-lang.c b/gdb/f-lang.c index 365e0c0..7ab2a7b 100644 --- a/gdb/f-lang.c +++ b/gdb/f-lang.c @@ -101,6 +101,16 @@ f_language::get_encoding (struct type *type) return encoding; } +/* See language.h. */ + +struct value * +f_language::value_string (struct gdbarch *gdbarch, + const char *ptr, ssize_t len) const +{ + struct type *type = language_string_char_type (this, gdbarch); + return ::value_string (ptr, len, type); +} + /* A helper function for the "bound" intrinsics that checks that TYPE is an array. LBOUND_P is true for lower bound; this is used for the error message, if any. */ diff --git a/gdb/f-lang.h b/gdb/f-lang.h index 673e273..0a29380 100644 --- a/gdb/f-lang.h +++ b/gdb/f-lang.h @@ -203,6 +203,11 @@ public: /* See language.h. */ + struct value *value_string (struct gdbarch *gdbarch, + const char *ptr, ssize_t len) const override; + + /* See language.h. */ + const char *struct_too_deep_ellipsis () const override { return "(...)"; } diff --git a/gdb/guile/scm-math.c b/gdb/guile/scm-math.c index 49fe93b..32595cf 100644 --- a/gdb/guile/scm-math.c +++ b/gdb/guile/scm-math.c @@ -803,9 +803,7 @@ vlscm_convert_typed_value_from_scheme (const char *func_name, 0 /*non-strict*/, &except_scm); if (s != NULL) - value = value_cstring (s.get (), len, - language_string_char_type (language, - gdbarch)); + value = language->value_string (gdbarch, s.get (), len); else value = NULL; } diff --git a/gdb/language.c b/gdb/language.c index f82c5b1..c768971 100644 --- a/gdb/language.c +++ b/gdb/language.c @@ -874,6 +874,16 @@ language_string_char_type (const struct language_defn *la, /* See language.h. */ +struct value * +language_defn::value_string (struct gdbarch *gdbarch, + const char *ptr, ssize_t len) const +{ + struct type *type = language_string_char_type (this, gdbarch); + return value_cstring (ptr, len, type); +} + +/* See language.h. */ + struct type * language_bool_type (const struct language_defn *la, struct gdbarch *gdbarch) diff --git a/gdb/language.h b/gdb/language.h index 2b44ff6..9fd2fb6 100644 --- a/gdb/language.h +++ b/gdb/language.h @@ -602,6 +602,12 @@ struct language_defn virtual char string_lower_bound () const { return c_style_arrays_p () ? 0 : 1; } + /* Return the LEN characters long string at PTR as a value suitable for + this language. GDBARCH is used to infer the character type. The + default implementation returns a null-terminated C string. */ + virtual struct value *value_string (struct gdbarch *gdbarch, + const char *ptr, ssize_t len) const; + /* Returns true if the symbols names should be stored in GDB's data structures for minimal/partial/full symbols using their linkage (aka mangled) form; false if the symbol names should be demangled first. diff --git a/gdb/python/py-value.c b/gdb/python/py-value.c index a3d80ad..933af92 100644 --- a/gdb/python/py-value.c +++ b/gdb/python/py-value.c @@ -54,9 +54,6 @@ #define builtin_type_pybool \ language_bool_type (current_language, gdbpy_enter::get_gdbarch ()) -#define builtin_type_pychar \ - language_string_char_type (current_language, gdbpy_enter::get_gdbarch ()) - struct value_object { PyObject_HEAD struct value_object *next; @@ -1881,8 +1878,9 @@ convert_value_from_python (PyObject *obj) gdb::unique_xmalloc_ptr<char> s = python_string_to_target_string (obj); if (s != NULL) - value = value_cstring (s.get (), strlen (s.get ()), - builtin_type_pychar); + value + = current_language->value_string (gdbpy_enter::get_gdbarch (), + s.get (), strlen (s.get ())); } else if (PyObject_TypeCheck (obj, &value_object_type)) value = ((value_object *) obj)->value->copy (); diff --git a/gdb/testsuite/gdb.base/internal-string-values.c b/gdb/testsuite/gdb.base/internal-string-values.c new file mode 100644 index 0000000..e89f717 --- /dev/null +++ b/gdb/testsuite/gdb.base/internal-string-values.c @@ -0,0 +1,32 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2021-2023 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 <http://www.gnu.org/licenses/>. */ + +static void +trace_me (void) +{} + +static void +end (void) +{} + +int +main (void) +{ + trace_me (); + end (); + return 0; +} diff --git a/gdb/testsuite/gdb.base/internal-string-values.exp b/gdb/testsuite/gdb.base/internal-string-values.exp new file mode 100644 index 0000000..ddf38a6 --- /dev/null +++ b/gdb/testsuite/gdb.base/internal-string-values.exp @@ -0,0 +1,279 @@ +# Copyright 2021-2023 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 <http://www.gnu.org/licenses/>. + +# Test that string values are correctly allocated inside GDB when doing +# various operations that yield strings. +# +# The issue that lead to this test was a missing NULL terminator in the +# C-string values. We verify that we can print the null terminator of these +# strings. + +load_lib "trace-support.exp" +load_lib "gdb-guile.exp" + +standard_testfile + +if {[build_executable "failed to prepare" $testfile $srcfile ]} { + return +} + +set user_conv_funcs {$_gdb_setting $_gdb_setting_str} +set maint_conv_funcs {$_gdb_maint_setting $_gdb_maint_setting_str} + +# Add language (LANG) appropriate quotation marks around string STR. +proc quote_for_lang {lang str} { + if {$lang == "fortran"} { + return "'$str'" + } else { + return "\"$str\"" + } +} + +# Check that the string contained in the convenienced variable $v is +# EXPECTED_STR. +# +# In particular, check that the null terminator is there and that we can't +# access a character past the end of the string. + +proc check_v_string { expected_str } { + set len [string length $expected_str] + + for { set i 0 } { $i < $len } { incr i } { + set c [string index $expected_str $i] + gdb_test "print \$v\[$i\]" "= $::decimal '$c'" + } + + # Check that the string ends with a null terminator. + gdb_test "print \$v\[$i\]" {= 0 '\\000'} + + # Check that we can't access a character after the end of the string. + incr i + gdb_test "print \$v\[$i\]" "no such vector element" +} + +# Test with string values made by $_gdb_setting & co. + +proc_with_prefix test_setting { } { + clean_restart + + # This is an internal GDB implementation detail, but the variable backing + # a string setting starts as nullptr (unless explicitly initialized at + # startup). When assigning an empty value, the variable then points to an + # empty string. Test both cases, as it triggers different code paths (in + # addition to a non-empty value). + # + # Use "set trace-user" and "maintenance set test-settings string" as they + # are both not initialized at startup. + with_test_prefix "user setting" { + with_test_prefix "not set" { + foreach_with_prefix conv_func $::user_conv_funcs { + gdb_test_no_output "set \$v = ${conv_func}(\"trace-user\")" + check_v_string "" + } + } + + with_test_prefix "set to empty" { + gdb_test "set trace-user" + foreach_with_prefix conv_func $::user_conv_funcs { + gdb_test_no_output "set \$v = ${conv_func}(\"trace-user\")" + check_v_string "" + } + } + + with_test_prefix "set" { + gdb_test "set trace-user poulet" + foreach_with_prefix conv_func $::user_conv_funcs { + gdb_test_no_output {set $v = $_gdb_setting("trace-user")} + check_v_string "poulet" + } + } + } + + with_test_prefix "maintenance setting" { + with_test_prefix "not set" { + foreach_with_prefix conv_func $::maint_conv_funcs { + gdb_test_no_output \ + "set \$v = ${conv_func}(\"test-settings string\")" + check_v_string "" + } + } + + with_test_prefix "set to empty" { + gdb_test "maintenance set test-settings string" + foreach_with_prefix conv_func $::maint_conv_funcs { + gdb_test_no_output \ + "set \$v = ${conv_func}(\"test-settings string\")" + check_v_string "" + } + } + + with_test_prefix "set" { + gdb_test "maintenance set test-settings string perchaude" + foreach_with_prefix conv_func $::maint_conv_funcs { + gdb_test_no_output \ + "set \$v = ${conv_func}(\"test-settings string\")" + check_v_string "perchaude" + } + } + } + + # Test with a non-string setting, this tests yet another code path. + with_test_prefix "integer setting" { + gdb_test_no_output {set $v = $_gdb_setting_str("remotetimeout")} + check_v_string "2" + } + + # Test string values made by $_gdb_setting & co. in all languages. + with_test_prefix "all langs" { + # Get list of supported languages. + set langs [gdb_supported_languages] + + gdb_test "maintenance set test-settings string foo" + foreach_with_prefix lang $langs { + gdb_test_no_output "set language $lang" + + if {$lang == "modula-2"} { + # The Modula-2 parser doesn't know how to build a + # suitable string expression. + gdb_test "print \"foo\"" "strings are not implemented" + continue + } + + if {$lang == "rust"} { + # Rust strings are actually structs, without a running + # inferior into which the string data can be pushed + # GDB can't print anything. + gdb_test "print \"foo\"" \ + "evaluation of this expression requires the target program to be active" + gdb_test "print \$_gdb_maint_setting(\"test-settings string\")" \ + "evaluation of this expression requires the target program to be active" + continue + } + + if {$lang == "unknown"} { + # Skipped because expression parsing is not supported + # for the "unknown" language. See gdb/28093 for more + # details. + continue + } + + set print_output "" + set ptype_output "" + + set foo_str [quote_for_lang $lang foo] + gdb_test_multiple "print $foo_str" "" { + -wrap -re " = (.*)" { + set print_output $expect_out(1,string) + pass $gdb_test_name + } + } + + gdb_test_multiple "ptype $foo_str" "" { + -wrap -re " = (.*)" { + set ptype_output $expect_out(1,string) + pass $gdb_test_name + } + } + + set cmd_str [quote_for_lang $lang "test-settings string"] + set ptype_output_re [string_to_regexp $ptype_output] + set print_output_re [string_to_regexp $print_output] + + foreach_with_prefix conv_func $::maint_conv_funcs { + gdb_test "print ${conv_func}($cmd_str)" \ + " = $print_output_re" + gdb_test "ptype \$" \ + " = $ptype_output_re" + } + } + } +} + +# Test with a string value created by gdb.Value in Python. + +proc_with_prefix test_python_value { } { + clean_restart + + if {![allow_python_tests]} { + untested "skipping test_python_value" + return + } + + gdb_test_no_output "python gdb.set_convenience_variable(\"v\", \"bar\")" \ + "set convenience var" + check_v_string "bar" +} + +# Test with a string value created by make-value in Guile. + +proc_with_prefix test_guile_value { } { + clean_restart + + if {![allow_guile_tests]} { + untested "skipping test_guile_value" + return + } + + # We can't set a convenience var from Guile, but we can append to history. + # Do that, then transfer to a convenience var with a CLI command. + gdb_test_no_output "guile (use-modules (gdb))" + gdb_test_multiple "guile (history-append! (make-value \"foo\"))" "make value" { + -re -wrap "($::decimal)" { + set histnum $expect_out(1,string) + } + } + + gdb_test_no_output "set \$v = \$$histnum" + check_v_string "foo" +} + +# Test with a string value coming from a string internal var. The only internal +# vars of this type, at the time of writing, are $trace_func and $trace_file. +# They both require inspecting a trace frame. So if the target is capable start +# tracing, record one trace frame, and use $trace_func. + +proc_with_prefix test_internal_var { } { + if {![gdb_trace_common_supports_arch]} { + unsupported "arch does not support trace" + return + } + + clean_restart $::binfile + + if {![runto_main]} { + fail "could not run to main" + return + } + + if {![gdb_target_supports_trace]} { + unsupported "target does not support trace" + return + } + + gdb_breakpoint "end" + gdb_test "trace trace_me" "Tracepoint $::decimal at $::hex.*" + gdb_test_no_output "tstart" + gdb_continue_to_breakpoint "breakpoint at end" + gdb_test_no_output "tstop" + gdb_test "tfind" "Found trace frame 0, tracepoint $::decimal.*" + gdb_test_no_output "set \$v = \$trace_func" + gdb_test "tfind none" "No longer looking at any trace frame.*" + check_v_string "trace_me" +} + +test_setting +test_python_value +test_guile_value +test_internal_var diff --git a/gdb/testsuite/gdb.base/print-internal-string.c b/gdb/testsuite/gdb.base/print-internal-string.c new file mode 100644 index 0000000..09dab88 --- /dev/null +++ b/gdb/testsuite/gdb.base/print-internal-string.c @@ -0,0 +1,56 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2023 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 <http://www.gnu.org/licenses/>. */ + +#include <stddef.h> +#include <string.h> + +/* A memory area used as the malloc memory buffer. */ + +static char arena[256]; + +/* Override malloc(). When GDB tries to push strings into the inferior we + will always return the same pointer (to arena). This does mean we can't + have multiple strings in use at the same time, but that's fine for this + simple test. On each malloc call the contents of arena are reset, which + should make it more obvious if GDB tried to print memory that it + shouldn't. */ + +void * +malloc (size_t size) +{ + /* Reset the contents of arena, and ensure there's a null-character at + the end just in case GDB tries to print memory that it shouldn't. */ + memset (arena, 'X', sizeof (arena)); + arena [sizeof (arena) - 1] = '\0'; + if (size > sizeof (arena)) + return NULL; + return arena; +} + +/* This function is called from GDB. */ + +void +take_string (const char *str) +{ + /* Nothing. */ +} + +int +main (void) +{ + return 0; +} diff --git a/gdb/testsuite/gdb.base/print-internal-string.exp b/gdb/testsuite/gdb.base/print-internal-string.exp new file mode 100644 index 0000000..be5b2f3 --- /dev/null +++ b/gdb/testsuite/gdb.base/print-internal-string.exp @@ -0,0 +1,64 @@ +# Copyright 2023 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 <http://www.gnu.org/licenses/>. + +# Test different ways in which GDB can create a string and then push +# that string into the inferior before reading it back. Check that +# the thing that is read back is correctly interpreted as a string. + +standard_testfile + +if { [prepare_for_testing "failed to prepare" $testfile $srcfile] } { + return -1 +} + +if {![runto_main]} { + return 0 +} + +if [allow_python_tests] { + # The $_as_string convenience function is implemented in Python. + gdb_test {printf "%s\n", $_as_string("aabbcc")} "\"aabbcc\"" + + # Create a gdb.Value object for a string. Take its address (which + # forces it into the inferior), and then print the address as a + # string. + gdb_test_no_output {python addr = gdb.Value("ccbbaa").address} + gdb_test {python gdb.execute("x/1s 0x%x" % addr)} \ + "$hex <arena>:\\s+\"ccbbaa\"" + + # Call an inferior function through a gdb.Value object, pass a + # string to the inferior function and ensure it arrives correctly. + gdb_test "p/x take_string" " = $hex.*" + gdb_test_no_output "python func_ptr = gdb.history (0)" \ + "place address of take_string into Python variable" + gdb_test "python func_value = func_ptr.dereference()" "" + gdb_breakpoint "take_string" + gdb_test {python result = func_value("qqaazz")} \ + "Breakpoint $decimal, take_string \\(str=$hex <arena> \"qqaazz\"\\) at .*" + gdb_test "continue" "Continuing\\." +} + +# Use printf on a string parsed by the C expression parser. +gdb_test {printf "%s\n", "ddeeff"} "ddeeff" + +# Parse a string in the C expression parser, force it into the +# inferior by taking its address, then print it as a string. +gdb_test {x/1s &"gghhii"} "$hex <arena>:\\s+\"gghhii\"" + +# Use $_gdb_setting_str and $_gdb_maint_setting_str to create a string +# value, and then print using printf, which forces the string into the +# inferior. +gdb_test {printf "%s\n", $_gdb_setting_str("arch")} "auto" +gdb_test {printf "%s\n", $_gdb_maint_setting_str("bfd-sharing")} "on" diff --git a/gdb/testsuite/gdb.base/settings.exp b/gdb/testsuite/gdb.base/settings.exp index ac885d8..dc96f85 100644 --- a/gdb/testsuite/gdb.base/settings.exp +++ b/gdb/testsuite/gdb.base/settings.exp @@ -504,7 +504,7 @@ proc_with_prefix test-enum {} { gdb_test_no_output "$set_cmd zzz" show_setting "$show_cmd" "zzz" 0 "yyy" - check_type "test-settings enum" "type = char \\\[3\\\]" + check_type "test-settings enum" "type = char \\\[4\\\]" test_gdb_complete_multiple "$set_cmd " "" "" { "xxx" diff --git a/gdb/testsuite/gdb.python/py-mi.exp b/gdb/testsuite/gdb.python/py-mi.exp index 97b8a42..c65c835 100644 --- a/gdb/testsuite/gdb.python/py-mi.exp +++ b/gdb/testsuite/gdb.python/py-mi.exp @@ -288,7 +288,7 @@ mi_create_dynamic_varobj nstype2 nstype2 ".*" 1 \ "create nstype2 varobj" mi_list_varobj_children nstype2 { - { {nstype2.<error at 0>} {<error at 0>} 6 {char \[6\]} } + { {nstype2.<error at 0>} {<error at 0>} 7 {char \[7\]} } } "list children after setting exception flag" mi_create_varobj me me \ diff --git a/gdb/valops.c b/gdb/valops.c index be34b76..ea9d9b3 100644 --- a/gdb/valops.c +++ b/gdb/valops.c @@ -1738,39 +1738,38 @@ value_array (int lowbound, int highbound, struct value **elemvec) return val; } +/* See value.h. */ + struct value * -value_cstring (const char *ptr, ssize_t len, struct type *char_type) +value_cstring (const gdb_byte *ptr, ssize_t count, struct type *char_type) { struct value *val; int lowbound = current_language->string_lower_bound (); - ssize_t highbound = len / char_type->length (); + ssize_t highbound = count + 1; struct type *stringtype = lookup_array_range_type (char_type, lowbound, highbound + lowbound - 1); val = value::allocate (stringtype); + ssize_t len = count * char_type->length (); memcpy (val->contents_raw ().data (), ptr, len); + /* Write the terminating null-character. */ + memset (val->contents_raw ().data () + len, 0, char_type->length ()); return val; } -/* Create a value for a string constant by allocating space in the - inferior, copying the data into that space, and returning the - address with type TYPE_CODE_STRING. PTR points to the string - constant data; LEN is number of characters. - - Note that string types are like array of char types with a lower - bound of zero and an upper bound of LEN - 1. Also note that the - string may contain embedded null bytes. */ +/* See value.h. */ struct value * -value_string (const char *ptr, ssize_t len, struct type *char_type) +value_string (const gdb_byte *ptr, ssize_t count, struct type *char_type) { struct value *val; int lowbound = current_language->string_lower_bound (); - ssize_t highbound = len / char_type->length (); + ssize_t highbound = count; struct type *stringtype = lookup_string_range_type (char_type, lowbound, highbound + lowbound - 1); val = value::allocate (stringtype); + ssize_t len = count * char_type->length (); memcpy (val->contents_raw ().data (), ptr, len); return val; } diff --git a/gdb/value.c b/gdb/value.c index 980722a..9517868 100644 --- a/gdb/value.c +++ b/gdb/value.c @@ -2044,8 +2044,9 @@ value_of_internalvar (struct gdbarch *gdbarch, struct internalvar *var) break; case INTERNALVAR_STRING: - val = value_cstring (var->u.string, strlen (var->u.string), - builtin_type (gdbarch)->builtin_char); + val = current_language->value_string (gdbarch, + var->u.string, + strlen (var->u.string)); break; case INTERNALVAR_VALUE: diff --git a/gdb/value.h b/gdb/value.h index 508367a..512d05e 100644 --- a/gdb/value.h +++ b/gdb/value.h @@ -1183,11 +1183,48 @@ class scoped_value_mark bool m_freed = false; }; -extern struct value *value_cstring (const char *ptr, ssize_t len, +/* Create not_lval value representing a NULL-terminated C string. The + resulting value has type TYPE_CODE_ARRAY. The string passed in should + not include embedded null characters. + + PTR points to the string data; COUNT is number of characters (does + not include the NULL terminator) pointed to by PTR, each character is of + type (and size of) CHAR_TYPE. */ + +extern struct value *value_cstring (const gdb_byte *ptr, ssize_t count, struct type *char_type); -extern struct value *value_string (const char *ptr, ssize_t len, + +/* Specialisation of value_cstring above. In this case PTR points to + single byte characters. CHAR_TYPE must have a length of 1. */ +inline struct value *value_cstring (const char *ptr, ssize_t count, + struct type *char_type) +{ + gdb_assert (char_type->length () == 1); + return value_cstring ((const gdb_byte *) ptr, count, char_type); +} + +/* Create a not_lval value with type TYPE_CODE_STRING, the resulting value + has type TYPE_CODE_STRING. + + PTR points to the string data; COUNT is number of characters pointed to + by PTR, each character has the type (and size of) CHAR_TYPE. + + Note that string types are like array of char types with a lower bound + defined by the language (usually zero or one). Also the string may + contain embedded null characters. */ + +extern struct value *value_string (const gdb_byte *ptr, ssize_t count, struct type *char_type); +/* Specialisation of value_string above. In this case PTR points to + single byte characters. CHAR_TYPE must have a length of 1. */ +inline struct value *value_string (const char *ptr, ssize_t count, + struct type *char_type) +{ + gdb_assert (char_type->length () == 1); + return value_string ((const gdb_byte *) ptr, count, char_type); +} + extern struct value *value_array (int lowbound, int highbound, struct value **elemvec); |