[1/3] Add support for DW_OP_GNU_variable_value

Message ID 20180802211754.40a529c2@pinnacle.lan
State New
Headers show
Series
  • Add support for DW_OP_GNU_variable_value
Related show

Commit Message

Kevin Buettner Aug. 3, 2018, 4:17 a.m.
This patch adds support for DW_OP_GNU_variable_value to GDB.
    
Jakub Jelinek provides a fairly expansive discussion of this DWARF
expression opcode in his GCC patch...

    https://gcc.gnu.org/ml/gcc-patches/2017-02/msg01499.html

It has also been proposed for addition to the DWARF Standard:

    http://www.dwarfstd.org/ShowIssue.php?issue=161109.2

If compiled with a suitable version of GCC, the test case associated
with GCC Bug 77589 uses DW_OP_GNU_variable_value in a DW_AT_byte_stride
expression.  Here's a link to the bug:

    https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77589

This is what the DWARF looks like.  Look at the last line, which has
the DW_AT_byte_stride expression:

 <2><e1>: Abbrev Number: 12 (DW_TAG_variable)
    <e2>   DW_AT_name        : (indirect string, offset: 0x115): span.0
    <e6>   DW_AT_type        : <0x2e>
    <ea>   DW_AT_artificial  : 1
    <ea>   DW_AT_location    : 3 byte block: 91 b0 7f 	(DW_OP_fbreg: -80)
 ...
 <2><178>: Abbrev Number: 18 (DW_TAG_subrange_type)
    <179>   DW_AT_lower_bound : 4 byte block: 97 23 20 6 	(DW_OP_push_object_address; DW_OP_plus_uconst: 32; DW_OP_deref)
    <17e>   DW_AT_upper_bound : 4 byte block: 97 23 28 6 	(DW_OP_push_object_address; DW_OP_plus_uconst: 40; DW_OP_deref)
    <183>   DW_AT_byte_stride : 10 byte block: 97 23 18 6 fd e1 0 0 0 1e 	(DW_OP_push_object_address; DW_OP_plus_uconst: 24; DW_OP_deref; DW_OP_GNU_variable_value: <0xe1>; DW_OP_mul)

A patch to readelf, which I'm also submitting, is required to do this
decoding.

I found that GDB gave me the correct answer for "p c40pt(2)" once I
(correctly) implemented DW_OP_GNU_variable_value.

I also have test case (later in this series) which uses the DWARF
assembler and, therefore, does not rely on having a compiler with this
support.

gdb/ChangeLog:
    
    	* dwarf2expr.h (struct dwarf_expr_context): Add virtual method
    	dwarf_variable_value.
    	* dwarf2-frame.c (class dwarf_expr_executor):
    	Add override for dwarf_variable_value.
    	* dwarf2loc.c (class dwarf_evaluate_loc_desc): Likewise.
    	(class symbol_needs_eval_context): Likewise.
    	(indirect_synthetic_pointer): Add forward declaration.
    	(sect_variable_value): New function.
    	(dwarf2_compile_expr_to_ax): Add case for DW_OP_GNU_variable_value.
    	* dwarf2expr.c (dwarf_expr_context::execute_stack_op): Add case
    	for DW_OP_GNU_variable_value.
---
 gdb/dwarf2-frame.c |  5 +++++
 gdb/dwarf2expr.c   | 11 +++++++++++
 gdb/dwarf2expr.h   |  3 +++
 gdb/dwarf2loc.c    | 44 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 63 insertions(+)

Comments

Tom Tromey Aug. 3, 2018, 6:36 p.m. | #1
>>>>> "Kevin" == Kevin Buettner <kevinb@redhat.com> writes:


Kevin> This patch adds support for DW_OP_GNU_variable_value to GDB.
Kevin> Jakub Jelinek provides a fairly expansive discussion of this DWARF
Kevin> expression opcode in his GCC patch...

Thank you for doing this.  A few minor comments below.

Kevin> I also have test case (later in this series) which uses the DWARF
Kevin> assembler and, therefore, does not rely on having a compiler with this
Kevin> support.

Very nice.
    
Kevin> +  /* Execute "variable value" operation on DIED at SECT_OFF.  */

I think that should probably read "... on the DIE at ...".

Kevin> +struct value *
Kevin> +sect_variable_value (struct dwarf_expr_context *ctx, sect_offset sect_off,
Kevin> +		     struct dwarf2_per_cu_data *per_cu)

A new function should have an introductory comment explaining the
arguments, return value, and semantics.

Kevin> @@ -3655,6 +3696,9 @@ dwarf2_compile_expr_to_ax (struct agent_expr *expr, struct axs_value *loc,
Kevin>  	case DW_OP_call_ref:
Kevin>  	  unimplemented (op);
 
Kevin> +	case DW_OP_GNU_variable_value:
Kevin> +	  unimplemented (op);

Is it possible to implement this?  Just curious, it doesn't seem super
important to me.  In fact, is it ever even useful in an agent
expression?

I think the patch should also update disassemble_dwarf_expression.
It's an oddity that there are two DWARF disassembler in the tree, but
not your problem :)

Tom
Tom Tromey Aug. 3, 2018, 6:42 p.m. | #2
>>>>> "Kevin" == Kevin Buettner <kevinb@redhat.com> writes:


I forgot a note earlier...

Kevin> +  /* Note: Things still work when the following test is removed.  This
Kevin> +     test and error is here to conform to the proposed specification.  */
Kevin> +  if (!is_scalar_type (die_type) || TYPE_CODE (die_type) == TYPE_CODE_VOID)
Kevin> +    error (_("Type of DW_OP_GNU_variable_value DIE must be an integer or pointer."));

I suspect is_scalar_type isn't what you want here.
Maybe instead either "is_integral_type || TYPE_CODE == TYPE_CODE_PTR",
or just checking for TYPE_CODE_PTR and TYPE_CODE_INT (since, bizarrely
to me at least, is_integral_type includes range types).

Tom
Kevin Buettner Aug. 18, 2018, 8:31 p.m. | #3
On Fri, 03 Aug 2018 12:36:35 -0600
Tom Tromey <tom@tromey.com> wrote:

> Kevin> @@ -3655,6 +3696,9 @@ dwarf2_compile_expr_to_ax (struct agent_expr *expr, struct axs_value *loc,

> Kevin>  	case DW_OP_call_ref:

> Kevin>  	  unimplemented (op);  

>  

> Kevin> +	case DW_OP_GNU_variable_value:

> Kevin> +	  unimplemented (op);  

> 

> Is it possible to implement this?  Just curious, it doesn't seem super

> important to me.  In fact, is it ever even useful in an agent

> expression?


When I first started looking at it, it seemed to me that
DW_OP_GNU_variable_value was similar to DW_OP_call_ref.  Implementing
the latter using existing machinery should be possible by compiling an
agent expression.  It might be possible to do something similar for
DW_OP_GNU_variable_value, but it's more complicated since the
referenced DIE might not have DW_AT_location, but instead have
DW_AT_const_value instead.

In any case, my understanding of agent expressions isn't good enough
to attempt an implementation.

> I think the patch should also update disassemble_dwarf_expression.

> It's an oddity that there are two DWARF disassembler in the tree, but

> not your problem :)


I fixed this, plus the other things that you mentioned.  (Thanks for the
quick review!)

This is what I pushed...

commit a6b786da4e3353bf634ec7d9c7bffbd7569e73c6
Author: Kevin Buettner <kevinb@redhat.com>
Date:   Mon Jul 30 15:41:56 2018 -0700

    Add support for DW_OP_GNU_variable_value
    
    This patch adds support for DW_OP_GNU_variable_value to GDB.
    
    Jakub Jelinek provides a fairly expansive discussion of this DWARF
    expression opcode in his GCC patch...
    
        https://gcc.gnu.org/ml/gcc-patches/2017-02/msg01499.html
    
    It has also been proposed for addition to the DWARF Standard:
    
        http://www.dwarfstd.org/ShowIssue.php?issue=161109.2
    
    If compiled with a suitable version of GCC, the test case associated
    with GCC Bug 77589 uses DW_OP_GNU_variable_value in a DW_AT_byte_stride
    expression.  Here's a link to the bug:
    
        https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77589
    
    This is what the DWARF looks like.  Look at the last line, which has
    the DW_AT_byte_stride expression:
    
     <2><e1>: Abbrev Number: 12 (DW_TAG_variable)
        <e2>   DW_AT_name        : (indirect string, offset: 0x115): span.0
        <e6>   DW_AT_type        : <0x2e>
        <ea>   DW_AT_artificial  : 1
        <ea>   DW_AT_location    : 3 byte block: 91 b0 7f 	(DW_OP_fbreg: -80)
     ...
     <2><178>: Abbrev Number: 18 (DW_TAG_subrange_type)
        <179>   DW_AT_lower_bound : 4 byte block: 97 23 20 6 	(DW_OP_push_object_address; DW_OP_plus_uconst: 32; DW_OP_deref)
        <17e>   DW_AT_upper_bound : 4 byte block: 97 23 28 6 	(DW_OP_push_object_address; DW_OP_plus_uconst: 40; DW_OP_deref)
        <183>   DW_AT_byte_stride : 10 byte block: 97 23 18 6 fd e1 0 0 0 1e 	(DW_OP_push_object_address; DW_OP_plus_uconst: 24; DW_OP_deref; DW_OP_GNU_variable_value: <0xe1>; DW_OP_mul)
    
    A patch to readelf, which I'm also submitting, is required to do this
    decoding.
    
    I found that GDB gave me the correct answer for "p c40pt(2)" once I
    (correctly) implemented DW_OP_GNU_variable_value.
    
    I also have test case (later in this series) which uses the DWARF
    assembler and, therefore, do not rely on having a compiler with this
    support.
    
    gdb/ChangeLog:
    
    	* dwarf2expr.h (struct dwarf_expr_context): Add virtual method
    	dwarf_variable_value.
    	* dwarf2-frame.c (class dwarf_expr_executor):
    	Add override for dwarf_variable_value.
    	* dwarf2loc.c (class dwarf_evaluate_loc_desc): Likewise.
    	(class symbol_needs_eval_context): Likewise.
    	(indirect_synthetic_pointer): Add forward declaration.
    	(sect_variable_value): New function.
    	(dwarf2_compile_expr_to_ax): Add case for DW_OP_GNU_variable_value.
    	* dwarf2expr.c (dwarf_expr_context::execute_stack_op): Add case
    	for DW_OP_GNU_variable_value.
---
 gdb/ChangeLog      | 14 ++++++++++++++
 gdb/dwarf2-frame.c |  5 +++++
 gdb/dwarf2expr.c   | 11 +++++++++++
 gdb/dwarf2expr.h   |  3 +++
 gdb/dwarf2loc.c    | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 89 insertions(+)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 9ae0810..c75c0e1 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,17 @@
+2018-08-18  Kevin Buettner  <kevinb@redhat.com>
+
+	* dwarf2expr.h (struct dwarf_expr_context): Add virtual method
+	dwarf_variable_value.
+	* dwarf2-frame.c (class dwarf_expr_executor):
+	Add override for dwarf_variable_value.
+	* dwarf2loc.c (class dwarf_evaluate_loc_desc): Likewise.
+	(class symbol_needs_eval_context): Likewise.
+	(indirect_synthetic_pointer): Add forward declaration.
+	(sect_variable_value): New function.
+	(dwarf2_compile_expr_to_ax): Add case for DW_OP_GNU_variable_value.
+	* dwarf2expr.c (dwarf_expr_context::execute_stack_op): Add case
+	for DW_OP_GNU_variable_value.
+
 2018-08-16  Tom Tromey  <tom@tromey.com>
 
 	* top.c (read_command_file): Update.
diff --git a/gdb/dwarf2-frame.c b/gdb/dwarf2-frame.c
index 58f1ba4..f7dc820 100644
--- a/gdb/dwarf2-frame.c
+++ b/gdb/dwarf2-frame.c
@@ -284,6 +284,11 @@ class dwarf_expr_executor : public dwarf_expr_context
     invalid ("DW_OP_call*");
   }
 
+  struct value *dwarf_variable_value (sect_offset sect_off) override
+  {
+    invalid ("DW_OP_GNU_variable_value");
+  }
+
   CORE_ADDR get_addr_index (unsigned int index) override
   {
     invalid ("DW_OP_GNU_addr_index");
diff --git a/gdb/dwarf2expr.c b/gdb/dwarf2expr.c
index 445f857..f1ca033 100644
--- a/gdb/dwarf2expr.c
+++ b/gdb/dwarf2expr.c
@@ -1259,6 +1259,17 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	    this->dwarf_call (cu_off);
 	  }
 	  goto no_push;
+
+	case DW_OP_GNU_variable_value:
+	  {
+	    sect_offset sect_off
+	      = (sect_offset) extract_unsigned_integer (op_ptr,
+	                                                this->ref_addr_size,
+							byte_order);
+	    op_ptr += this->ref_addr_size;
+	    result_val = this->dwarf_variable_value (sect_off);
+	  }
+	  break;
 	
 	case DW_OP_entry_value:
 	case DW_OP_GNU_entry_value:
diff --git a/gdb/dwarf2expr.h b/gdb/dwarf2expr.h
index c746bfe..a98edc9 100644
--- a/gdb/dwarf2expr.h
+++ b/gdb/dwarf2expr.h
@@ -221,6 +221,9 @@ struct dwarf_expr_context
      subroutine.  */
   virtual void dwarf_call (cu_offset die_cu_off) = 0;
 
+  /* Execute "variable value" operation on the DIE at SECT_OFF.  */
+  virtual struct value *dwarf_variable_value (sect_offset sect_off) = 0;
+
   /* Return the base type given by the indicated DIE at DIE_CU_OFF.
      This can throw an exception if the DIE is invalid or does not
      represent a base type.  SIZE is non-zero if this function should
diff --git a/gdb/dwarf2loc.c b/gdb/dwarf2loc.c
index a98b676..df2f231 100644
--- a/gdb/dwarf2loc.c
+++ b/gdb/dwarf2loc.c
@@ -61,6 +61,12 @@ static struct call_site_parameter *dwarf_expr_reg_to_entry_parameter
      union call_site_parameter_u kind_u,
      struct dwarf2_per_cu_data **per_cu_return);
 
+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);
+
 /* Until these have formal names, we define these here.
    ref: http://gcc.gnu.org/wiki/DebugFission
    Each entry in .debug_loc.dwo begins with a byte that describes the entry,
@@ -546,6 +552,30 @@ per_cu_dwarf_call (struct dwarf_expr_context *ctx, cu_offset die_offset,
   ctx->eval (block.data, block.size);
 }
 
+/* Given context CTX, section offset SECT_OFF, and compilation unit
+   data PER_CU, execute the "variable value" operation on the DIE
+   found at SECT_OFF.  */
+
+static struct value *
+sect_variable_value (struct dwarf_expr_context *ctx, sect_offset sect_off,
+		     struct dwarf2_per_cu_data *per_cu)
+{
+  struct type *die_type = dwarf2_fetch_die_type_sect_off (sect_off, per_cu);
+
+  if (die_type == NULL)
+    error (_("Bad DW_OP_GNU_variable_value DIE."));
+
+  /* Note: Things still work when the following test is removed.  This
+     test and error is here to conform to the proposed specification.  */
+  if (TYPE_CODE (die_type) != TYPE_CODE_INT
+      && TYPE_CODE (die_type) != TYPE_CODE_PTR)
+    error (_("Type of DW_OP_GNU_variable_value DIE must be an integer or pointer."));
+
+  struct type *type = lookup_pointer_type (die_type);
+  struct frame_info *frame = get_selected_frame (_("No frame selected."));
+  return indirect_synthetic_pointer (sect_off, 0, per_cu, frame, type);
+}
+
 class dwarf_evaluate_loc_desc : public dwarf_expr_context
 {
  public:
@@ -587,6 +617,14 @@ class dwarf_evaluate_loc_desc : public dwarf_expr_context
     per_cu_dwarf_call (this, die_offset, per_cu);
   }
 
+  /* Helper interface of sect_variable_value for
+     dwarf2_evaluate_loc_desc.  */
+
+  struct value *dwarf_variable_value (sect_offset sect_off) override
+  {
+    return sect_variable_value (this, sect_off, per_cu);
+  }
+
   struct type *get_base_type (cu_offset die_offset, int size) override
   {
     struct type *result = dwarf2_get_die_type (die_offset, per_cu);
@@ -2815,6 +2853,14 @@ class symbol_needs_eval_context : public dwarf_expr_context
     per_cu_dwarf_call (this, die_offset, per_cu);
   }
 
+  /* Helper interface of sect_variable_value for
+     dwarf2_loc_desc_get_symbol_read_needs.  */
+
+  struct value *dwarf_variable_value (sect_offset sect_off) override
+  {
+    return sect_variable_value (this, sect_off, per_cu);
+  }
+
   /* DW_OP_entry_value accesses require a caller, therefore a
      frame.  */
 
@@ -3655,6 +3701,9 @@ dwarf2_compile_expr_to_ax (struct agent_expr *expr, struct axs_value *loc,
 	case DW_OP_call_ref:
 	  unimplemented (op);
 
+	case DW_OP_GNU_variable_value:
+	  unimplemented (op);
+
 	default:
 	  unimplemented (op);
 	}
@@ -4280,6 +4329,13 @@ disassemble_dwarf_expression (struct ui_file *stream,
 	  ul = dwarf2_read_addr_index (per_cu, ul);
 	  fprintf_filtered (stream, " %s", pulongest (ul));
 	  break;
+
+	case DW_OP_GNU_variable_value:
+	  ul = extract_unsigned_integer (data, offset_size,
+					 gdbarch_byte_order (arch));
+	  data += offset_size;
+	  fprintf_filtered (stream, " offset %s", phex_nz (ul, offset_size));
+	  break;
 	}
 
       fprintf_filtered (stream, "\n");
Tom de Vries Aug. 22, 2018, 3:35 p.m. | #4
On 08/18/2018 10:31 PM, Kevin Buettner wrote:
>     This patch adds support for DW_OP_GNU_variable_value to GDB.

>     

>     Jakub Jelinek provides a fairly expansive discussion of this DWARF

>     expression opcode in his GCC patch...

>     

>         https://gcc.gnu.org/ml/gcc-patches/2017-02/msg01499.html

>     

>     It has also been proposed for addition to the DWARF Standard:

>     

>         http://www.dwarfstd.org/ShowIssue.php?issue=161109.2


Hi,

AFAIU from the discussion here (
https://gcc.gnu.org/ml/gcc-patches/2018-08/msg01351.html ) if:
- a DW_OP_GNU_variable_value refers to a die 'a', and
- there's a die 'b' with abstract_origin 'a' that does have a
  DW_AT_location, and
- die 'b' is 'in scope' in an evaluation context,
then the evaluation of DW_OP_GNU_variable_value 'a' should return the
value found at the DW_AT_location of die 'b'.

I've written a gcc demonstrator patch to generate code like this for
VLAs, and found that gdb master (containing this patch series) does not
support this.

Is this further support of DW_OP_GNU_variable_value something you're
currently working on, or plan to work on?

Thanks,
- Tom
Kevin Buettner Aug. 23, 2018, 9:12 p.m. | #5
On Wed, 22 Aug 2018 17:35:23 +0200
Tom de Vries <tdevries@suse.de> wrote:

> On 08/18/2018 10:31 PM, Kevin Buettner wrote:

> >     This patch adds support for DW_OP_GNU_variable_value to GDB.

> >     

> >     Jakub Jelinek provides a fairly expansive discussion of this DWARF

> >     expression opcode in his GCC patch...

> >     

> >         https://gcc.gnu.org/ml/gcc-patches/2017-02/msg01499.html

> >     

> >     It has also been proposed for addition to the DWARF Standard:

> >     

> >         http://www.dwarfstd.org/ShowIssue.php?issue=161109.2  

> 

> Hi,

> 

> AFAIU from the discussion here (

> https://gcc.gnu.org/ml/gcc-patches/2018-08/msg01351.html ) if:

> - a DW_OP_GNU_variable_value refers to a die 'a', and

> - there's a die 'b' with abstract_origin 'a' that does have a

>   DW_AT_location, and

> - die 'b' is 'in scope' in an evaluation context,

> then the evaluation of DW_OP_GNU_variable_value 'a' should return the

> value found at the DW_AT_location of die 'b'.

> 

> I've written a gcc demonstrator patch to generate code like this for

> VLAs, and found that gdb master (containing this patch series) does not

> support this.

> 

> Is this further support of DW_OP_GNU_variable_value something you're

> currently working on, or plan to work on?


Tom and I discussed this briefly on IRC.  Tom says that he'll take
a look at it...

Kevin

Patch

diff --git a/gdb/dwarf2-frame.c b/gdb/dwarf2-frame.c
index 58f1ba4..f7dc820 100644
--- a/gdb/dwarf2-frame.c
+++ b/gdb/dwarf2-frame.c
@@ -284,6 +284,11 @@  class dwarf_expr_executor : public dwarf_expr_context
     invalid ("DW_OP_call*");
   }
 
+  struct value *dwarf_variable_value (sect_offset sect_off) override
+  {
+    invalid ("DW_OP_GNU_variable_value");
+  }
+
   CORE_ADDR get_addr_index (unsigned int index) override
   {
     invalid ("DW_OP_GNU_addr_index");
diff --git a/gdb/dwarf2expr.c b/gdb/dwarf2expr.c
index 445f857..f1ca033 100644
--- a/gdb/dwarf2expr.c
+++ b/gdb/dwarf2expr.c
@@ -1259,6 +1259,17 @@  dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	    this->dwarf_call (cu_off);
 	  }
 	  goto no_push;
+
+	case DW_OP_GNU_variable_value:
+	  {
+	    sect_offset sect_off
+	      = (sect_offset) extract_unsigned_integer (op_ptr,
+	                                                this->ref_addr_size,
+							byte_order);
+	    op_ptr += this->ref_addr_size;
+	    result_val = this->dwarf_variable_value (sect_off);
+	  }
+	  break;
 	
 	case DW_OP_entry_value:
 	case DW_OP_GNU_entry_value:
diff --git a/gdb/dwarf2expr.h b/gdb/dwarf2expr.h
index c746bfe..c702a19 100644
--- a/gdb/dwarf2expr.h
+++ b/gdb/dwarf2expr.h
@@ -221,6 +221,9 @@  struct dwarf_expr_context
      subroutine.  */
   virtual void dwarf_call (cu_offset die_cu_off) = 0;
 
+  /* Execute "variable value" operation on DIED at SECT_OFF.  */
+  virtual struct value *dwarf_variable_value (sect_offset sect_off) = 0;
+
   /* Return the base type given by the indicated DIE at DIE_CU_OFF.
      This can throw an exception if the DIE is invalid or does not
      represent a base type.  SIZE is non-zero if this function should
diff --git a/gdb/dwarf2loc.c b/gdb/dwarf2loc.c
index a98b676..5c7c4ba 100644
--- a/gdb/dwarf2loc.c
+++ b/gdb/dwarf2loc.c
@@ -61,6 +61,12 @@  static struct call_site_parameter *dwarf_expr_reg_to_entry_parameter
      union call_site_parameter_u kind_u,
      struct dwarf2_per_cu_data **per_cu_return);
 
+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);
+
 /* Until these have formal names, we define these here.
    ref: http://gcc.gnu.org/wiki/DebugFission
    Each entry in .debug_loc.dwo begins with a byte that describes the entry,
@@ -546,6 +552,25 @@  per_cu_dwarf_call (struct dwarf_expr_context *ctx, cu_offset die_offset,
   ctx->eval (block.data, block.size);
 }
 
+struct value *
+sect_variable_value (struct dwarf_expr_context *ctx, sect_offset sect_off,
+		     struct dwarf2_per_cu_data *per_cu)
+{
+  struct type *die_type = dwarf2_fetch_die_type_sect_off (sect_off, per_cu);
+
+  if (die_type == NULL)
+    error (_("Bad DW_OP_GNU_variable_value DIE."));
+
+  /* Note: Things still work when the following test is removed.  This
+     test and error is here to conform to the proposed specification.  */
+  if (!is_scalar_type (die_type) || TYPE_CODE (die_type) == TYPE_CODE_VOID)
+    error (_("Type of DW_OP_GNU_variable_value DIE must be an integer or pointer."));
+
+  struct type *type = lookup_pointer_type (die_type);
+  struct frame_info *frame = get_selected_frame (_("No frame selected."));
+  return indirect_synthetic_pointer (sect_off, 0, per_cu, frame, type);
+}
+
 class dwarf_evaluate_loc_desc : public dwarf_expr_context
 {
  public:
@@ -587,6 +612,14 @@  class dwarf_evaluate_loc_desc : public dwarf_expr_context
     per_cu_dwarf_call (this, die_offset, per_cu);
   }
 
+  /* Helper interface of sect_variable_value for
+     dwarf2_evaluate_loc_desc.  */
+
+  struct value *dwarf_variable_value (sect_offset sect_off) override
+  {
+    return sect_variable_value (this, sect_off, per_cu);
+  }
+
   struct type *get_base_type (cu_offset die_offset, int size) override
   {
     struct type *result = dwarf2_get_die_type (die_offset, per_cu);
@@ -2815,6 +2848,14 @@  class symbol_needs_eval_context : public dwarf_expr_context
     per_cu_dwarf_call (this, die_offset, per_cu);
   }
 
+  /* Helper interface of sect_variable_value for
+     dwarf2_loc_desc_get_symbol_read_needs.  */
+
+  struct value *dwarf_variable_value (sect_offset sect_off) override
+  {
+    return sect_variable_value (this, sect_off, per_cu);
+  }
+
   /* DW_OP_entry_value accesses require a caller, therefore a
      frame.  */
 
@@ -3655,6 +3696,9 @@  dwarf2_compile_expr_to_ax (struct agent_expr *expr, struct axs_value *loc,
 	case DW_OP_call_ref:
 	  unimplemented (op);
 
+	case DW_OP_GNU_variable_value:
+	  unimplemented (op);
+
 	default:
 	  unimplemented (op);
 	}