I've just realized that my comment isn't entirely clear. The
"@address" string is shown only for structure types, not for regular
variables, arrays or such. It makes sense because only those are
affected by set print object, but the comment doesn't explicitly say
so.
I don't know whether it'd be preferable to have <synthetic pointer>
everywhere as opposed to @address. I guess @address is more consistent
with non-synthetic references, and it also hides the fact they're
synthetic from the user, but there are cases (such as
DW_AT_const_value) where @address wouldn't work/make sense.
So I think before proceeding we should decide which output is better.
Perhaps we could show @address whenever possible, and <synthetic
pointer> for the corner cases?
On Tue, May 24, 2016 at 11:51 AM, Pedro Alves <palves@redhat.com> wrote:
> But normal pointers don't print @address either, only references do.
Yeah, I was referring to references :)
> Not printing "@address" with "set print object off" seems like
> hiding information from the user, information that we could show.
> We always print it for non-synthetic references, AFAICS.
Yes, we do.
> Your comment in the patch, in generic_val_print_ref, reads:
>
> + if options->objectprint is true, c_value_print will call value_addr
> + on the reference, which coerces synthetic references and returns a
> + 'not_lval'. */
>
> So if that works, I don't understand -- wouldn't calling value_addr
> or coerce_ref in generic_val_print_ref if you have a synthetic
> reference, or any reference even, be what you'd want?
If I'm not mistaken, doing that would cause us to always print
"@address". Which again, may be in fact the right thing to do.
>> I *could*, however, manually call
>> value->location.computed.funcs->check_synthetic_pointer in
>> generic_val_print_ref instead of using value_bits_synthetic_pointer,
>> thus avoiding the check for lval_computed. But that's a bit ugly IMHO.
>
> I don't understand this one. Only lval_computed values have a
> "location.computed.funcs" to call.
Yeah, you're right. For a moment I thought all values had an
lval_funcs member, but only computed ones do.
> So is the problem that this bit:
>
> if (options->addressprint)
> {
> CORE_ADDR addr
> = extract_typed_address (valaddr + embedded_offset, type);
>
> doesn't work / doesn't make sense with synthetic pointers?
Exactly. IIRC extract_typed_address would return zero (or maybe just
garbage?), because valaddr would actually be a pointer to the
synthetic pointer's lval_funcs instead of a target address.
On a related note, it'd be great (for debugging at least!) if unions
such as this had at least a discriminant of sorts.
> Should we be calling value_addr instead?
Perhaps. I stuck to calling extract_typed_address because I tried to
change as little code as possible. I'm seeing that
generic_val_print_ref has a path which coerces the reference anyway,
so we could always coerce it, get its address, and only print the
referenced value when required. I'd have to test it thoroughly to be
sure, though.
> Or are we perhaps missing a lval_funcs method? (Ideally, all
> value properties/methods would go through a vtable like
> lval_funcs; think "making struct value a proper C++ class" going
> forward.)
Right now I don't think so. The existing methods should be enough to
handle these cases. Speaking of which, are there any plans to rewrite
this sort of object-oriented code in C++? I'd love to take a shot at
this in the future.
> I don't know where, but I think this should indeed be covered by
> tests somewhere.
Ok.
@@ -2061,6 +2061,66 @@ get_frame_address_in_block_wrapper (void *baton)
return get_frame_address_in_block ((struct frame_info *) baton);
}
+/* Fetch a DW_AT_const_value through a synthetic pointer. */
+
+static struct value *
+fetch_const_value_from_synthetic_pointer (sect_offset die, LONGEST byte_offset,
+ struct dwarf2_per_cu_data *per_cu,
+ struct type *type)
+{
+ struct value *result = NULL;
+ struct obstack temp_obstack;
+ struct cleanup *cleanup;
+ const gdb_byte *bytes;
+ LONGEST len;
+
+ obstack_init (&temp_obstack);
+ cleanup = make_cleanup_obstack_free (&temp_obstack);
+ bytes = dwarf2_fetch_constant_bytes (die, per_cu, &temp_obstack, &len);
+
+ if (bytes != NULL)
+ {
+ if (byte_offset >= 0
+ && byte_offset + TYPE_LENGTH (TYPE_TARGET_TYPE (type)) <= len)
+ {
+ bytes += byte_offset;
+ result = value_from_contents (TYPE_TARGET_TYPE (type), bytes);
+ }
+ else
+ invalid_synthetic_pointer ();
+ }
+ else
+ result = allocate_optimized_out_value (TYPE_TARGET_TYPE (type));
+
+ do_cleanups (cleanup);
+
+ return result;
+}
+
+/* Fetch the value pointed to by a synthetic pointer. */
+
+static struct value *
+indirect_synthetic_pointer (sect_offset die, LONGEST byte_offset,
+ struct dwarf2_per_cu_data *per_cu,
+ struct frame_info *frame, struct type *type)
+{
+ /* Fetch the location expression of the DIE we're pointing to. */
+ struct dwarf2_locexpr_baton baton
+ = dwarf2_fetch_die_loc_sect_off (die, per_cu,
+ get_frame_address_in_block_wrapper, frame);
+
+ /* If pointed-to DIE has a DW_AT_location, evaluate it and return the
+ resulting value. Otherwise, it may have a DW_AT_const_value instead,
+ or it may've been optimized out. */
+ if (baton.data != NULL)
+ return dwarf2_evaluate_loc_desc_full (TYPE_TARGET_TYPE (type), frame,
+ baton.data, baton.size, baton.per_cu,
+ byte_offset);
+ else
+ return fetch_const_value_from_synthetic_pointer (die, byte_offset, per_cu,
+ type);
+}
+
/* An implementation of an lval_funcs method to indirect through a
pointer. This handles the synthetic pointer case when needed. */
@@ -2115,6 +2175,7 @@ indirect_pieced_value (struct value *value)
break;
}
+ gdb_assert (piece != NULL);
frame = get_selected_frame (_("No frame selected."));
/* This is an offset requested by GDB, such as value subscripts.
@@ -2132,43 +2193,40 @@ indirect_pieced_value (struct value *value)
TYPE_LENGTH (type), byte_order);
byte_offset += piece->v.ptr.offset;
- gdb_assert (piece);
- baton
- = dwarf2_fetch_die_loc_sect_off (piece->v.ptr.die, c->per_cu,
- get_frame_address_in_block_wrapper,
- frame);
+ return indirect_synthetic_pointer (piece->v.ptr.die, byte_offset, c->per_cu,
+ frame, type);
+}
- if (baton.data != NULL)
- return dwarf2_evaluate_loc_desc_full (TYPE_TARGET_TYPE (type), frame,
- baton.data, baton.size, baton.per_cu,
- byte_offset);
+/* Implementation of the coerce_ref method of lval_funcs for synthetic C++
+ references. */
- {
- struct obstack temp_obstack;
- struct cleanup *cleanup;
- const gdb_byte *bytes;
- LONGEST len;
- struct value *result;
-
- obstack_init (&temp_obstack);
- cleanup = make_cleanup_obstack_free (&temp_obstack);
-
- bytes = dwarf2_fetch_constant_bytes (piece->v.ptr.die, c->per_cu,
- &temp_obstack, &len);
- if (bytes == NULL)
- result = allocate_optimized_out_value (TYPE_TARGET_TYPE (type));
- else
- {
- if (byte_offset < 0
- || byte_offset + TYPE_LENGTH (TYPE_TARGET_TYPE (type)) > len)
- invalid_synthetic_pointer ();
- bytes += byte_offset;
- result = value_from_contents (TYPE_TARGET_TYPE (type), bytes);
- }
+static struct value *
+coerce_pieced_ref (const struct value *value)
+{
+ struct type *type = check_typedef (value_type (value));
- do_cleanups (cleanup);
- return result;
- }
+ if (value_bits_synthetic_pointer (value, value_embedded_offset (value),
+ TARGET_CHAR_BIT * TYPE_LENGTH (type)))
+ {
+ const struct piece_closure *closure
+ = (struct piece_closure *) value_computed_closure (value);
+ struct frame_info *frame
+ = get_selected_frame (_("No frame selected."));
+
+ /* gdb represents synthetic pointers as pieced values with a single
+ piece. */
+ gdb_assert (closure != NULL);
+ gdb_assert (closure->n_pieces == 1);
+
+ return indirect_synthetic_pointer (closure->pieces->v.ptr.die,
+ closure->pieces->v.ptr.offset,
+ closure->per_cu, frame, type);
+ }
+ else
+ {
+ /* Else: not a synthetic reference; do nothing. */
+ return NULL;
+ }
}
static void *
@@ -2206,7 +2264,7 @@ static const struct lval_funcs pieced_value_funcs = {
read_pieced_value,
write_pieced_value,
indirect_pieced_value,
- NULL, /* coerce_ref */
+ coerce_pieced_ref,
check_pieced_synthetic_pointer,
copy_pieced_value_closure,
free_pieced_value_closure
new file mode 100644
@@ -0,0 +1,27 @@
+/* Copyright (C) 2016 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/>. */
+
+/* Test program for synthetic C++ references to arrays. */
+
+int array[5] = {0, 1, 2, 3, 4};
+
+int
+main (void)
+{
+ asm ("main_label: .globl main_label");
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,168 @@
+# Copyright 2016 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 a C++ reference marked with DW_OP_GNU_implicit_pointer.
+# The referenced value is a global array whose location is a DW_OP_addr.
+
+if [skip_cplus_tests] {
+ continue
+}
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if ![dwarf2_support] {
+ return 0
+}
+
+# We'll place the output of Dwarf::assemble in implref-array.S.
+standard_testfile .c .S
+
+# ${testfile} is now "implref-array". srcfile2 is "implref-array.S".
+set executable ${testfile}
+set asm_file [standard_output_file ${srcfile2}]
+
+# We need to know the size of integer and address types in order
+# to write some of the debugging info we'd like to generate.
+#
+# For that, we ask GDB by debugging our implref-array program.
+# Any program would do, but since we already have implref-array
+# specifically for this testcase, might as well use that.
+if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } {
+ untested ${testfile}.exp
+ return -1
+}
+
+set array_length [get_valueof "/u" "sizeof(array) / sizeof(array\[0\])" -1]
+
+# Create the DWARF. We need a regular variable which represents the array, and
+# a reference to it that'll be marked with DW_OP_GNU_implicit_pointer.
+# The variable must be global so that its name is an exported symbol that we
+# can reference from the DWARF using gdb_target_symbol.
+Dwarf::assemble ${asm_file} {
+ global srcdir subdir srcfile array_length
+
+ cu {} {
+ DW_TAG_compile_unit {
+ {DW_AT_language @DW_LANG_C_plus_plus}
+ } {
+ declare_labels int_label sizetype_label array_label variable_label ref_label
+ set int_size [get_sizeof "int" -1]
+ set upper_bound [expr ${array_length} - 1]
+
+ # gdb always assumes references are implemented as pointers.
+ set addr_size [get_sizeof "void *" -1]
+
+ int_label: DW_TAG_base_type {
+ {DW_AT_byte_size ${int_size} DW_FORM_udata}
+ {DW_AT_encoding @DW_ATE_signed}
+ {DW_AT_name "int"}
+ }
+
+ sizetype_label: DW_TAG_base_type {
+ {DW_AT_byte_size ${int_size} DW_FORM_udata}
+ {DW_AT_encoding @DW_ATE_unsigned}
+ {DW_AT_name "sizetype"}
+ }
+
+ array_label: DW_TAG_array_type {
+ {DW_AT_type :${int_label}}
+ } {
+ DW_TAG_subrange_type {
+ {DW_AT_type :${sizetype_label}}
+ {DW_AT_lower_bound 0 DW_FORM_udata}
+ {DW_AT_upper_bound ${upper_bound} DW_FORM_udata}
+ }
+ }
+
+ ref_label: DW_TAG_reference_type {
+ {DW_AT_byte_size ${addr_size} DW_FORM_udata}
+ {DW_AT_type :${array_label}}
+ }
+
+ variable_label: DW_TAG_variable {
+ {DW_AT_name "array"}
+ {DW_AT_type :${array_label}}
+ {DW_AT_external 1 DW_FORM_flag}
+ {DW_AT_location {DW_OP_addr [gdb_target_symbol "array"]} SPECIAL_expr}
+ }
+
+ DW_TAG_subprogram {
+ {MACRO_AT_func { "main" "${srcdir}/${subdir}/${srcfile}" }}
+ {DW_AT_type :${int_label}}
+ {DW_AT_external 1 DW_FORM_flag}
+ } {
+ DW_TAG_variable {
+ {DW_AT_name "ref"}
+ {DW_AT_type :${ref_label}}
+ {DW_AT_location {DW_OP_GNU_implicit_pointer ${variable_label} 0} SPECIAL_expr}
+ }
+ }
+ }
+ }
+}
+
+if [prepare_for_testing ${testfile}.exp ${executable} [list ${asm_file} ${srcfile}] {}] {
+ return -1
+}
+
+# DW_OP_GNU_implicit_pointer implementation requires a valid frame.
+if ![runto_main] {
+ return -1
+}
+
+# This matches e.g. '(int (&)[5])'
+set ref_type [format {\(int \(&\)\[%d\]\)} ${array_length}]
+
+# This matches e.g. '(int (*)[5])'
+set ptr_type [format {\(int \(\*\)\[%d\]\)} ${array_length}]
+
+# Contents of the array. Trim leading/trailing whitespace, '{' and '}'
+# since they confuse TCL to no end.
+set contents [get_valueof "" "array" ""]
+set contents [string trim ${contents}]
+set contents [string trim ${contents} "{}"]
+
+# Doing 'print ref' should show us e.g. '(int (&)[5]) <synthetic pointer>: {0, 1, 2, 3, 4}'.
+gdb_test "print ref" " = ${ref_type} <synthetic pointer>: \\{${contents}\\}"
+
+# Doing 'print &ref' should show us e.g. '(int (*)[5]) 0xdeadbeef <array>'.
+gdb_test "print &ref" " = ${ptr_type} \[\[:xdigit:\]x\]+ <array>"
+
+# gdb assumes C++ references are implemented as pointers, and print &(&ref)
+# shows us the underlying pointer's address. Since in this case there's no
+# physical pointer, gdb should tell us so.
+gdb_test "print &(&ref)" "Attempt to take address of value not located in memory."
+
+# Test assignment through the synthetic reference.
+set first_value 10
+gdb_test_no_output "set (ref\[0\] = ${first_value})"
+
+# This matches '{10, 1, 2, 3, 4}'.
+set new_contents [format {\{%d, 1, 2, 3, 4\}} ${first_value}]
+
+# Doing 'print ref' should now show us e.g.
+# '(int (&)[5]) <synthetic pointer>: {10, 1, 2, 3, 4}'.
+gdb_test "print ref" " = ${ref_type} <synthetic pointer>: ${new_contents}" "print ref after assignment"
+gdb_test "print array" " = ${new_contents}" "print array after assignment"
+
+# Test treating the array as a pointer.
+set second_value 20
+set new_contents [format {\{%d, %d, 3, 4\}} ${first_value} ${second_value}]
+
+gdb_test "print *ref" " = ${first_value}"
+gdb_test_no_output "set (*(ref + 1) = ${second_value})"
+gdb_test "print ref\[1\]" " = ${second_value}"
+gdb_test "print array" " = ${new_contents}" "print array after second assignment"
similarity index 57%
rename from gdb/testsuite/gdb.dwarf2/implref.exp
rename to gdb/testsuite/gdb.dwarf2/implref-const.exp
@@ -13,45 +13,62 @@
# 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 C++ references marked with DW_OP_GNU_implicit_pointer.
+# Test a C++ reference marked with DW_OP_GNU_implicit_pointer.
+# The referenced value is a DW_AT_const_value.
-# TODO: Add more test statements after fixing bug #19893:
-# https://sourceware.org/bugzilla/show_bug.cgi?id=19893.
+if [skip_cplus_tests] {
+ continue
+}
load_lib dwarf.exp
# This test can only be run on targets which support DWARF-2 and use gas.
-if {![dwarf2_support]} {
+if ![dwarf2_support] {
return 0
}
-# We'll place the output of Dwarf::assemble in implref.S.
+# We'll place the output of Dwarf::assemble in implref-const.S.
standard_testfile main.c .S
-# ${testfile} is now "implref". srcfile2 is "implref.S".
+# ${testfile} is now "implref-const". srcfile2 is "implref-const.S".
set executable ${testfile}
set asm_file [standard_output_file ${srcfile2}]
+# We need to know the size of integer and address types in order
+# to write some of the debugging info we'd like to generate.
+#
+# For that, we ask GDB by debugging our implref-const program.
+# Any program would do, but since we already have implref-const
+# specifically for this testcase, might as well use that.
+if [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] {
+ untested ${testfile}.exp
+ return -1
+}
+
# Create the DWARF. We need a regular variable and a reference to it that'll
# be marked with DW_OP_GNU_implicit_pointer.
-Dwarf::assemble $asm_file {
+Dwarf::assemble ${asm_file} {
global srcdir subdir srcfile
- cu { version 3 addr_size 4 } {
+ cu {} {
DW_TAG_compile_unit {
- {DW_AT_producer "GNU C++ 4.8.4"}
{DW_AT_language @DW_LANG_C_plus_plus}
} {
declare_labels int_label const_label variable_label ref_label
+ set int_size [get_sizeof "int" -1]
+
+ # gdb always assumes references are implemented as pointers.
+ set addr_size [get_sizeof "void *" -1]
+ set var_value 42
int_label: DW_TAG_base_type {
- {DW_AT_byte_size 4 DW_FORM_udata}
+ {DW_AT_byte_size ${int_size} DW_FORM_udata}
{DW_AT_encoding @DW_ATE_signed}
{DW_AT_name "int"}
}
ref_label: DW_TAG_reference_type {
- {DW_AT_byte_size 4 DW_FORM_udata}
+ {DW_AT_byte_size ${addr_size} DW_FORM_udata}
{DW_AT_type :${int_label}}
}
@@ -67,7 +84,7 @@ Dwarf::assemble $asm_file {
variable_label: DW_TAG_variable {
{DW_AT_name "var"}
{DW_AT_type :${int_label}}
- {DW_AT_const_value 42 DW_FORM_udata}
+ {DW_AT_const_value ${var_value} DW_FORM_udata}
}
DW_TAG_variable {
@@ -80,7 +97,7 @@ Dwarf::assemble $asm_file {
}
}
-if [prepare_for_testing ${testfile}.exp ${executable} "${asm_file} ${srcfile}" {}] {
+if [prepare_for_testing ${testfile}.exp ${executable} [list ${asm_file} ${srcfile}] {}] {
return -1
}
@@ -89,10 +106,15 @@ if ![runto_main] {
return -1
}
-gdb_test "print ref" " = \\(int &\\) <synthetic pointer>" "print ref"
-gdb_test "print &ref" " = \\(int \\*\\) <synthetic pointer>" "print &ref"
+# Doing 'print ref' should show us e.g. '(int &) <synthetic pointer>: 42'.
+set value_output {\(int &\) <synthetic pointer>: \d+}
+gdb_test "print ref" " = ${value_output}"
+
+# The variable isn't located in memory, thus we can't take its address.
+gdb_test "print &var" "Can't take address of \"var\" which isn't an lvalue."
+gdb_test "print &ref" "Attempt to take address of value not located in memory."
# gdb assumes C++ references are implemented as pointers, and print &(&ref)
# shows us the underlying pointer's address.
# Since in this case there's no physical pointer, gdb should tell us so.
-gdb_test "print &\(&ref\)" "Attempt to take address of value not located in memory." "print &(&ref)"
+gdb_test "print &(&ref)" "Attempt to take address of value not located in memory."
new file mode 100644
@@ -0,0 +1,27 @@
+/* Copyright (C) 2016 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/>. */
+
+/* Test program for synthetic C++ references to global variables. */
+
+int global_var = 42;
+
+int
+main (void)
+{
+ asm ("main_label: .globl main_label");
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,124 @@
+# Copyright 2016 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 a C++ reference marked with DW_OP_GNU_implicit_pointer.
+# The referenced value is a global variable whose location is a DW_OP_addr.
+
+if [skip_cplus_tests] {
+ continue
+}
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if ![dwarf2_support] {
+ return 0
+}
+
+# We'll place the output of Dwarf::assemble in implref-global.S.
+standard_testfile .c .S
+
+# ${testfile} is now "implref-global". srcfile2 is "implref-global.S".
+set executable ${testfile}
+set asm_file [standard_output_file ${srcfile2}]
+
+# We need to know the size of integer and address types in order
+# to write some of the debugging info we'd like to generate.
+#
+# For that, we ask GDB by debugging our implref-global program.
+# Any program would do, but since we already have implref-global
+# specifically for this testcase, might as well use that.
+if [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] {
+ untested ${testfile}.exp
+ return -1
+}
+
+# Create the DWARF. We need a regular variable and a reference to it that'll
+# be marked with DW_OP_GNU_implicit_pointer. The variable must be global so
+# that its name is an exported symbol that we can reference from the DWARF
+# using gdb_target_symbol.
+Dwarf::assemble ${asm_file} {
+ global srcdir subdir srcfile
+
+ cu {} {
+ DW_TAG_compile_unit {
+ {DW_AT_language @DW_LANG_C_plus_plus}
+ } {
+ declare_labels int_label variable_label ref_label
+ set int_size [get_sizeof "int" -1]
+
+ # gdb always assumes references are implemented as pointers.
+ set addr_size [get_sizeof "void *" -1]
+
+ int_label: DW_TAG_base_type {
+ {DW_AT_byte_size ${int_size} DW_FORM_udata}
+ {DW_AT_encoding @DW_ATE_signed}
+ {DW_AT_name "int"}
+ }
+
+ ref_label: DW_TAG_reference_type {
+ {DW_AT_byte_size ${addr_size} DW_FORM_udata}
+ {DW_AT_type :${int_label}}
+ }
+
+ variable_label: DW_TAG_variable {
+ {DW_AT_name "global_var"}
+ {DW_AT_type :${int_label}}
+ {DW_AT_external 1 DW_FORM_flag}
+ {DW_AT_location {DW_OP_addr [gdb_target_symbol "global_var"]} SPECIAL_expr}
+ }
+
+ DW_TAG_subprogram {
+ {MACRO_AT_func { "main" "${srcdir}/${subdir}/${srcfile}" }}
+ {DW_AT_type :${int_label}}
+ {DW_AT_external 1 DW_FORM_flag}
+ } {
+ DW_TAG_variable {
+ {DW_AT_name "ref"}
+ {DW_AT_type :${ref_label}}
+ {DW_AT_location {DW_OP_GNU_implicit_pointer ${variable_label} 0} SPECIAL_expr}
+ }
+ }
+ }
+ }
+}
+
+if [prepare_for_testing ${testfile}.exp ${executable} [list ${asm_file} ${srcfile}] {}] {
+ return -1
+}
+
+# DW_OP_GNU_implicit_pointer implementation requires a valid frame.
+if ![runto_main] {
+ return -1
+}
+
+# Doing 'print ref' should show us e.g. '(int &) <synthetic pointer>: 42'.
+set value_output {\(int &\) <synthetic pointer>: \d+}
+gdb_test "print ref" " = ${value_output}"
+
+# Doing 'print &ref' should show us e.g. '(int *) 0xdeadbeef <global_var>'.
+set addr_output {\(int \*\) [[:xdigit:]x]+ <global_var>}
+gdb_test "print &ref" " = ${addr_output}"
+
+# gdb assumes C++ references are implemented as pointers, and print &(&ref)
+# shows us the underlying pointer's address. Since in this case there's no
+# physical pointer, gdb should tell us so.
+gdb_test "print &(&ref)" "Attempt to take address of value not located in memory."
+
+# Test assignment through the synthetic reference.
+set new_value 10
+gdb_test_no_output "set (ref = ${new_value})"
+gdb_test "print ref" " = \\(int &\\) <synthetic pointer>: ${new_value}" "print ref after assignment"
+gdb_test "print global_var" " = ${new_value}" "print global_var after assignment"
new file mode 100644
@@ -0,0 +1,43 @@
+/* Copyright (C) 2016 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/>. */
+
+/* Test program for synthetic C++ references to structs. */
+
+struct S {
+ int a;
+ int b;
+ int c;
+};
+
+struct S s1 = {
+ 0,
+ 1,
+ 2
+};
+
+struct S s2 = {
+ 10,
+ 11,
+ 12
+};
+
+int
+main (void)
+{
+ asm ("main_label: .globl main_label");
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,173 @@
+# Copyright 2016 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 a C++ reference marked with DW_OP_GNU_implicit_pointer.
+# The referenced value is a global struct whose location is a DW_OP_addr.
+
+if [skip_cplus_tests] {
+ continue
+}
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if ![dwarf2_support] {
+ return 0
+}
+
+# We'll place the output of Dwarf::assemble in implref-struct.S.
+standard_testfile .c .S
+
+# ${testfile} is now "implref-struct". srcfile2 is "implref-struct.S".
+set executable ${testfile}
+set asm_file [standard_output_file ${srcfile2}]
+
+# We need to know the size of integer and address types in order
+# to write some of the debugging info we'd like to generate.
+#
+# For that, we ask GDB by debugging our implref-struct program.
+# Any program would do, but since we already have implref-struct
+# specifically for this testcase, might as well use that.
+if [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile} {debug c++}] {
+ untested ${testfile}.exp
+ return -1
+}
+
+# Create the DWARF. We need a regular variable for the struct and a reference
+# to it that'll be marked with DW_OP_GNU_implicit_pointer. The variable must be
+# global so that its name is an exported symbol that can we can reference from
+# the DWARF using gdb_target_symbol.
+Dwarf::assemble ${asm_file} {
+ global srcdir subdir srcfile
+
+ cu {} {
+ DW_TAG_compile_unit {
+ {DW_AT_language @DW_LANG_C_plus_plus}
+ } {
+ declare_labels int_label struct_label variable_label ref_label
+ set int_size [get_sizeof "int" -1]
+
+ # gdb always assumes references are implemented as pointers.
+ set addr_size [get_sizeof "void *" -1]
+ set S_size [get_sizeof "S" -1]
+
+ # The compiler shouldn't introduce structure padding here.
+ set b_offset 4
+ set c_offset 8
+
+ int_label: DW_TAG_base_type {
+ {DW_AT_byte_size ${int_size} DW_FORM_udata}
+ {DW_AT_encoding @DW_ATE_signed}
+ {DW_AT_name "int"}
+ }
+
+ struct_label: DW_TAG_structure_type {
+ {DW_AT_name "S"}
+ {DW_AT_byte_size ${S_size} DW_FORM_udata}
+ } {
+ DW_TAG_member {
+ {DW_AT_name "a"}
+ {DW_AT_type :${int_label}}
+ {DW_AT_data_member_location 0 DW_FORM_udata}
+ }
+
+ DW_TAG_member {
+ {DW_AT_name "b"}
+ {DW_AT_type :${int_label}}
+ {DW_AT_data_member_location ${b_offset} DW_FORM_udata}
+ }
+
+ DW_TAG_member {
+ {DW_AT_name "c"}
+ {DW_AT_type :${int_label}}
+ {DW_AT_data_member_location ${c_offset} DW_FORM_udata}
+ }
+ }
+
+ ref_label: DW_TAG_reference_type {
+ {DW_AT_byte_size ${addr_size} DW_FORM_udata}
+ {DW_AT_type :${struct_label}}
+ }
+
+ variable_label: DW_TAG_variable {
+ {DW_AT_name "s1"}
+ {DW_AT_type :${struct_label}}
+ {DW_AT_external 1 DW_FORM_flag}
+ {DW_AT_location {DW_OP_addr [gdb_target_symbol "s1"]} SPECIAL_expr}
+ }
+
+ DW_TAG_variable {
+ {DW_AT_name "s2"}
+ {DW_AT_type :${struct_label}}
+ {DW_AT_external 1 DW_FORM_flag}
+ {DW_AT_location {DW_OP_addr [gdb_target_symbol "s2"]} SPECIAL_expr}
+ }
+
+ DW_TAG_subprogram {
+ {MACRO_AT_func { "main" "${srcdir}/${subdir}/${srcfile}" }}
+ {DW_AT_type :${int_label}}
+ {DW_AT_external 1 DW_FORM_flag}
+ } {
+ DW_TAG_variable {
+ {DW_AT_name "ref"}
+ {DW_AT_type :${ref_label}}
+ {DW_AT_location {DW_OP_GNU_implicit_pointer ${variable_label} 0} SPECIAL_expr}
+ }
+ }
+ }
+ }
+}
+
+if [prepare_for_testing ${testfile}.exp ${executable} [list ${asm_file} ${srcfile}] {}] {
+ return -1
+}
+
+# DW_OP_GNU_implicit_pointer implementation requires a valid frame.
+if ![runto_main] {
+ return -1
+}
+
+# Returns the struct members.
+proc get_members {var default} {
+ set members [get_valueof "" ${var} ${default}]
+
+ # Trim leading/trailing whitespace, '{' and '}' since they confuse TCL
+ # to no end.
+ set members [string trim ${members}]
+ set members [string trim ${members} "{}"]
+
+ return ${members}
+}
+
+# Values of the struct members.
+set s1_members [get_members "s1" "\{a = 0, b = 1, c = 2\}"]
+set s2_members [get_members "s2" "\{a = 10, b = 11, c = 12\}"]
+
+# Doing 'print ref' should show us e.g. '(S &) <synthetic pointer>: {a = 0, b = 1, c = 2}'.
+gdb_test "print ref" " = \\(S &\\) <synthetic pointer>: \\{${s1_members}\\}"
+
+# Doing 'print &ref' should show us e.g. '(S *) 0xdeadbeef <s1>'.
+set addr_output {\(S \*\) [[:xdigit:]x]+ <s1>}
+gdb_test "print &ref" " = ${addr_output}"
+
+# gdb assumes C++ references are implemented as pointers, and print &(&ref)
+# shows us the underlying pointer's address. Since in this case there's no
+# physical pointer, gdb should tell us so.
+gdb_test "print &(&ref)" "Attempt to take address of value not located in memory."
+
+# Test assignment through the synthetic reference.
+gdb_test_no_output "set (ref = s2)"
+gdb_test "print ref" " = \\(S &\\) <synthetic pointer>: \\{${s2_members}\\}" "print ref after assignment"
+gdb_test "print s1" " = \\{${s2_members}\\}" "print s1 after assignment"
@@ -1465,22 +1465,28 @@ value_addr (struct value *arg1)
if (TYPE_CODE (type) == TYPE_CODE_REF)
{
- /* Copy the value, but change the type from (T&) to (T*). We
- keep the same location information, which is efficient, and
- allows &(&X) to get the location containing the reference.
- Do the same to its enclosing type for consistency. */
- struct type *type_ptr
- = lookup_pointer_type (TYPE_TARGET_TYPE (type));
- struct type *enclosing_type
- = check_typedef (value_enclosing_type (arg1));
- struct type *enclosing_type_ptr
- = lookup_pointer_type (TYPE_TARGET_TYPE (enclosing_type));
-
- arg2 = value_copy (arg1);
- deprecated_set_value_type (arg2, type_ptr);
- set_value_enclosing_type (arg2, enclosing_type_ptr);
+ if (value_bits_synthetic_pointer (arg1, value_embedded_offset (arg1),
+ TARGET_CHAR_BIT * TYPE_LENGTH (type)))
+ arg1 = coerce_ref (arg1);
+ else
+ {
+ /* Copy the value, but change the type from (T&) to (T*). We
+ keep the same location information, which is efficient, and
+ allows &(&X) to get the location containing the reference.
+ Do the same to its enclosing type for consistency. */
+ struct type *type_ptr
+ = lookup_pointer_type (TYPE_TARGET_TYPE (type));
+ struct type *enclosing_type
+ = check_typedef (value_enclosing_type (arg1));
+ struct type *enclosing_type_ptr
+ = lookup_pointer_type (TYPE_TARGET_TYPE (enclosing_type));
+
+ arg2 = value_copy (arg1);
+ deprecated_set_value_type (arg2, type_ptr);
+ set_value_enclosing_type (arg2, enclosing_type_ptr);
- return arg2;
+ return arg2;
+ }
}
if (TYPE_CODE (type) == TYPE_CODE_FUNC)
return value_coerce_function (arg1);
@@ -336,7 +336,9 @@ valprint_check_validity (struct ui_file *stream,
TARGET_CHAR_BIT * TYPE_LENGTH (type)))
{
fputs_filtered (_("<synthetic pointer>"), stream);
- return 0;
+
+ /* C++ references should be valid even if they're synthetic. */
+ return TYPE_CODE (type) == TYPE_CODE_REF;
}
if (!value_bytes_available (val, embedded_offset, TYPE_LENGTH (type)))
@@ -497,13 +499,29 @@ generic_val_print_ref (struct type *type, const gdb_byte *valaddr,
if (options->addressprint)
{
- CORE_ADDR addr
- = extract_typed_address (valaddr + embedded_offset, type);
+ const int is_synthetic
+ = value_bits_synthetic_pointer (original_value,
+ TARGET_CHAR_BIT * embedded_offset,
+ TARGET_CHAR_BIT * TYPE_LENGTH (type));
+ if (!is_synthetic)
+ {
+ CORE_ADDR addr
+ = extract_typed_address (valaddr + embedded_offset, type);
- fprintf_filtered (stream, "@");
- fputs_filtered (paddress (gdbarch, addr), stream);
- if (options->deref_ref)
- fputs_filtered (": ", stream);
+ fprintf_filtered (stream, "@");
+ fputs_filtered (paddress (gdbarch, addr), stream);
+ }
+ /* Else, we have a synthetic reference. Don't print '@address'; we'll
+ show '<synthetic pointer>' instead through valprint_check_validity.
+ Notice however that 'set print object on' will still show '@address'.
+ This happens because value_bits_synthetic_pointer returns false if
+ original value is not 'lval_computed'. While that's usually the case,
+ if options->objectprint is true, c_value_print will call value_addr
+ on the reference, which coerces synthetic references and returns a
+ 'not_lval'. */
+
+ if (options->deref_ref)
+ fputs_filtered (": ", stream);
}
/* De-reference the reference. */
if (options->deref_ref)