return symbol->is_artificial ();
}
+ /* 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
}
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))
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;
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");
{
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:
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");
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. */
/* 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 "(...)"; }
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;
}
/* 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)
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.
#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;
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 ();
--- /dev/null
+/* 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;
+}
--- /dev/null
+# 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
--- /dev/null
+/* 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;
+}
--- /dev/null
+# 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"
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"
"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 \
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;
}
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:
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);