public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [patch 02/12] entryval: Basic parameter values recovery
@ 2011-07-18 20:17 Jan Kratochvil
  2011-07-19 14:43 ` Tom Tromey
  0 siblings, 1 reply; 6+ messages in thread
From: Jan Kratochvil @ 2011-07-18 20:17 UTC (permalink / raw)
  To: gdb-patches

Hi,

this patch reads in the DWARF info for entry values and recovers the current
values from it.

the testcase:
#0  d (i=<optimized out>) at ./gdb.arch/amd64-entry-value.cc:33^M
#1  0x00000000004003ba in main () at ./gdb.arch/amd64-entry-value.cc:39^M
->
#0  d (i=31) at ./gdb.arch/amd64-entry-value.cc:33^M
#1  0x00000000004003da in main () at ./gdb.arch/amd64-entry-value.cc:39^M

It uses entry values internally, user cannot yet access them directly.

NOT_FOUND_ERROR is probably wrong, I can change it to some new *_ERROR
probably, I do not have a reproducer but NOT_FOUND_ERROR overloading can
probably regress something.


Thanks,
Jan


gdb/
2011-07-18  Jan Kratochvil  <jan.kratochvil@redhat.com>

	Implement basic support for DW_TAG_GNU_call_site.
	* block.c: Include gdbtypes.h.
	(call_site_for_pc): New function.
	* block.h (call_site_for_pc): New declaration.
	* defs.h: Include hashtab.h.
	(make_cleanup_htab_delete, core_addr_hash, core_addr_eq): New
	declarations.
	* dwarf2-frame.c (no_push_dwarf_reg_entry_value): New function.
	(dwarf2_frame_ctx_funcs): Install it.
	* dwarf2expr.c (dwarf_block_to_dwarf_reg)
	(dwarf_block_to_dwarf_reg_deref, dwarf_block_to_sp_offset): New
	functions.
	(execute_stack_op) <DW_OP_GNU_entry_value>: Implement it.
	* dwarf2expr.h (struct dwarf_expr_context_funcs): New field
	push_dwarf_reg_entry_value.
	(dwarf_block_to_dwarf_reg, dwarf_block_to_sp_offset)
	(dwarf_block_to_dwarf_reg_deref): New declarations.
	* dwarf2loc.c (dwarf_expr_ctx_funcs): New forward declaration.
	(call_site_to_target_addr, dwarf_expr_dwarf_reg_entry_value)
	(dwarf_entry_parameter_to_block, dwarf_expr_push_dwarf_reg_entry_value):
	New functions.
	(dwarf_expr_ctx_funcs): Install dwarf_expr_push_dwarf_reg_entry_value.
	(dwarf2_evaluate_loc_desc_full): Do old_chain in advance in the case of
	EX.  Handle NOT_FOUND_ERROR.
	(needs_dwarf_reg_entry_value): New function.
	(needs_frame_ctx_funcs): Install it.
	* dwarf2read.c (struct dwarf2_cu): New field call_site_htab.
	(read_call_site_scope): New forward declaration.
	(process_full_comp_unit): Copy call_site_htab.
	(process_die): Support DW_TAG_GNU_call_site.
	(dlbaton_obstack_copy, read_call_site_scope): New functions.
	(dwarf2_get_pc_bounds): Support NULL HIGHPC.
	(dwarf_tag_name): Support DW_TAG_GNU_call_site.
	* gdb-gdb.py (StructMainTypePrettyPrinter): Support
	FIELD_LOC_KIND_DWARF_BLOCK.
	* gdbtypes.h (enum field_loc_kind): New entry
	FIELD_LOC_KIND_DWARF_BLOCK.
	(struct main_type): New loc entry dwarf_block.
	(struct call_site, FIELD_DWARF_BLOCK, SET_FIELD_DWARF_BLOCK)
	(TYPE_FIELD_DWARF_BLOCK): New.
	* symtab.h (struct symtab): New field call_site_htab.
	* utils.c (do_htab_delete_cleanup, make_cleanup_htab_delete)
	(core_addr_hash, core_addr_eq): New functions.

gdb/testsuite/
2011-07-18  Jan Kratochvil  <jan.kratochvil@redhat.com>

	Implement basic support for DW_TAG_GNU_call_site.
	* gdb.arch/Makefile.in (EXECUTABLES): Add amd64-entry-value.
	* gdb.arch/amd64-entry-value.cc: New file.
	* gdb.arch/amd64-entry-value.exp: New file.

--- a/gdb/block.c
+++ b/gdb/block.c
@@ -25,6 +25,7 @@
 #include "gdb_obstack.h"
 #include "cp-support.h"
 #include "addrmap.h"
+#include "gdbtypes.h"
 
 /* This is used by struct block to store namespace-related info for
    C++ files, namely using declarations and the current namespace in
@@ -160,6 +161,29 @@ blockvector_for_pc_sect (CORE_ADDR pc, struct obj_section *section,
   return 0;
 }
 
+/* Return call_site for specified PC.  PC must match exactly, it must be the
+   next instruction after call (or after tail call jump).  Return NULL if it
+   cannot be found.  */
+
+struct call_site *
+call_site_for_pc (CORE_ADDR pc)
+{
+  struct symtab *symtab;
+  void **slot;
+
+  /* -1 as tail call PC can be already after the compilation unit range.  */
+  symtab = find_pc_symtab (pc - 1);
+
+  if (symtab == NULL || symtab->call_site_htab == NULL)
+    return NULL;
+
+  slot = htab_find_slot (symtab->call_site_htab, &pc, NO_INSERT);
+  if (slot == NULL)
+    return NULL;
+
+  return *slot;
+}
+
 /* Return the blockvector immediately containing the innermost lexical block
    containing the specified pc value, or 0 if there is none.
    Backward compatibility, no section.  */
--- a/gdb/block.h
+++ b/gdb/block.h
@@ -142,6 +142,8 @@ extern struct blockvector *blockvector_for_pc_sect (CORE_ADDR,
 						    struct block **,
                                                     struct symtab *);
 
+extern struct call_site *call_site_for_pc (CORE_ADDR pc);
+
 extern struct block *block_for_pc (CORE_ADDR);
 
 extern struct block *block_for_pc_sect (CORE_ADDR, struct obj_section *);
--- a/gdb/defs.h
+++ b/gdb/defs.h
@@ -89,6 +89,7 @@
 #include <stdarg.h>		/* For va_list.  */
 
 #include "libiberty.h"
+#include "hashtab.h"
 
 /* Rather than duplicate all the logic in BFD for figuring out what
    types to use (which can be pretty complicated), symply define them
@@ -370,6 +371,8 @@ extern struct cleanup *make_final_cleanup (make_cleanup_ftype *, void *);
 extern struct cleanup *make_my_cleanup (struct cleanup **,
 					make_cleanup_ftype *, void *);
 
+extern struct cleanup *make_cleanup_htab_delete (htab_t htab);
+
 extern struct cleanup *make_my_cleanup2 (struct cleanup **,
 					 make_cleanup_ftype *, void *,
 					 void (*free_arg) (void *));
@@ -540,6 +543,11 @@ extern const char *paddress (struct gdbarch *gdbarch, CORE_ADDR addr);
 extern const char *print_core_address (struct gdbarch *gdbarch,
 				       CORE_ADDR address);
 
+/* Callback hash_f and eq_f for htab_create_alloc or htab_create_alloc_ex.  */
+
+extern hashval_t core_addr_hash (const void *ap);
+extern int core_addr_eq (const void *ap, const void *bp);
+
 /* %d for LONGEST */
 extern char *plongest (LONGEST l);
 /* %u for ULONGEST */
--- a/gdb/dwarf2-frame.c
+++ b/gdb/dwarf2-frame.c
@@ -361,6 +361,16 @@ no_base_type (struct dwarf_expr_context *ctx, size_t die)
   error (_("Support for typed DWARF is not supported in CFI"));
 }
 
+/* Helper function for execute_stack_op.  */
+
+static void
+no_push_dwarf_reg_entry_value (struct dwarf_expr_context *ctx, int dwarf_reg,
+			       CORE_ADDR fb_offset, CORE_ADDR deref_size)
+{
+  internal_error (__FILE__, __LINE__,
+		  _("Support for DW_OP_GNU_entry_value is unimplemented"));
+}
+
 /* Execute the required actions for both the DW_CFA_restore and
 DW_CFA_restore_extended instructions.  */
 static void
@@ -402,7 +412,8 @@ static const struct dwarf_expr_context_funcs dwarf2_frame_ctx_funcs =
   no_get_frame_pc,
   no_get_tls_address,
   no_dwarf_call,
-  no_base_type
+  no_base_type,
+  no_push_dwarf_reg_entry_value
 };
 
 static CORE_ADDR
--- a/gdb/dwarf2expr.c
+++ b/gdb/dwarf2expr.c
@@ -481,6 +481,127 @@ dwarf_get_base_type (struct dwarf_expr_context *ctx, ULONGEST die, int size)
   return result;
 }
 
+/* If <BUF..BUF_END] contains DW_FORM_block* with single DW_OP_reg* return the
+   DWARF register number.  Otherwise return -1.  */
+
+int
+dwarf_block_to_dwarf_reg (const gdb_byte *buf, const gdb_byte *buf_end)
+{
+  ULONGEST dwarf_reg;
+
+  if (buf_end <= buf)
+    return -1;
+  if (*buf >= DW_OP_reg0 && *buf <= DW_OP_reg31)
+    {
+      if (buf_end - buf != 1)
+	return -1;
+      return *buf - DW_OP_reg0;
+    }
+
+  if (*buf != DW_OP_regx)
+    return -1;
+  buf++;
+  buf = read_uleb128 (buf, buf_end, &dwarf_reg);
+  if (buf != buf_end || (int) dwarf_reg != dwarf_reg)
+    return -1;
+  return dwarf_reg;
+}
+
+/* If <BUF..BUF_END] contains DW_FORM_block* with just DW_OP_breg*(0) and
+   DW_OP_deref* return the DWARF register number.  Otherwise return -1.
+   DEREF_SIZE_RETURN contains -1 for DW_OP_deref; otherwise it contains the
+   size from DW_OP_deref_size.  */
+
+int
+dwarf_block_to_dwarf_reg_deref (const gdb_byte *buf, const gdb_byte *buf_end,
+				CORE_ADDR *deref_size_return)
+{
+  ULONGEST dwarf_reg;
+  LONGEST offset;
+
+  if (buf_end <= buf)
+    return -1;
+  if (*buf >= DW_OP_breg0 && *buf <= DW_OP_breg31)
+    {
+      dwarf_reg = *buf - DW_OP_breg0;
+      buf++;
+    }
+  else if (*buf == DW_OP_bregx)
+    {
+      buf++;
+      buf = read_uleb128 (buf, buf_end, &dwarf_reg);
+      if ((int) dwarf_reg != dwarf_reg)
+	return -1;
+    }
+  else
+    return -1;
+
+  buf = read_sleb128 (buf, buf_end, &offset);
+  if (offset != 0)
+    return -1;
+
+  if (buf >= buf_end)
+    return -1;
+
+  if (*buf == DW_OP_deref)
+    {
+      buf++;
+      *deref_size_return = -1;
+    }
+  else if (*buf == DW_OP_deref_size)
+    {
+      buf++;
+      if (buf >= buf_end)
+	return -1;
+      *deref_size_return = *buf++;
+    }
+  else
+    return -1;
+
+  if (buf != buf_end)
+    return -1;
+
+  return dwarf_reg;
+}
+
+/* If <BUF..BUF_END] contains DW_FORM_block* with single DW_OP_bregSP(X) fill
+   in SP_OFFSET_RETURN with the X offset and return 1.  Otherwise return 0.
+   The matched SP register number depends on GDBARCH.  */
+
+int
+dwarf_block_to_sp_offset (struct gdbarch *gdbarch, const gdb_byte *buf,
+			  const gdb_byte *buf_end, CORE_ADDR *sp_offset_return)
+{
+  ULONGEST dwarf_reg;
+  LONGEST sp_offset;
+
+  if (buf_end <= buf)
+    return 0;
+  if (*buf >= DW_OP_breg0 && *buf <= DW_OP_breg31)
+    {
+      dwarf_reg = *buf - DW_OP_breg0;
+      buf++;
+    }
+  else
+    {
+      if (*buf != DW_OP_bregx)
+	return 0;
+      buf++;
+      buf = read_uleb128 (buf, buf_end, &dwarf_reg);
+    }
+
+  if (gdbarch_dwarf2_reg_to_regnum (gdbarch, dwarf_reg)
+      != gdbarch_sp_regnum (gdbarch))
+    return 0;
+
+  buf = read_sleb128 (buf, buf_end, &sp_offset);
+  *sp_offset_return = sp_offset;
+  if (buf != buf_end || sp_offset != (LONGEST) *sp_offset_return)
+    return 0;
+
+  return 1;
+}
+
 /* The engine for the expression evaluator.  Using the context in CTX,
    evaluate the expression between OP_PTR and OP_END.  */
 
@@ -1187,11 +1308,41 @@ execute_stack_op (struct dwarf_expr_context *ctx,
 	  goto no_push;
 	
 	case DW_OP_GNU_entry_value:
-	  /* This operation is not yet supported by GDB.  */
-	  ctx->location = DWARF_VALUE_OPTIMIZED_OUT;
-	  ctx->stack_len = 0;
-	  ctx->num_pieces = 0;
-	  goto abort_expression;
+	  {
+	    ULONGEST len;
+	    int dwarf_reg;
+	    CORE_ADDR deref_size;
+
+	    op_ptr = read_uleb128 (op_ptr, op_end, &len);
+	    if (op_ptr + len > op_end)
+	      error (_("DW_OP_GNU_entry_value: too few bytes available."));
+
+	    dwarf_reg = dwarf_block_to_dwarf_reg (op_ptr, op_ptr + len);
+	    if (dwarf_reg != -1)
+	      {
+		op_ptr += len;
+		ctx->funcs->push_dwarf_reg_entry_value (ctx, dwarf_reg,
+							0 /* unused */, -1);
+		goto no_push;
+	      }
+
+	    dwarf_reg = dwarf_block_to_dwarf_reg_deref (op_ptr, op_ptr + len,
+							&deref_size);
+	    if (dwarf_reg != -1)
+	      {
+		if (deref_size == -1)
+		  deref_size = ctx->addr_size;
+		op_ptr += len;
+		ctx->funcs->push_dwarf_reg_entry_value (ctx, dwarf_reg,
+							0 /* unused */,
+							deref_size);
+		goto no_push;
+	      }
+
+	    error (_("DWARF-2 expression error: DW_OP_GNU_entry_value is "
+		     "supported only for single DW_OP_reg* "
+		     "or for DW_OP_breg*(0)+DW_OP_deref*"));
+	  }
 
 	case DW_OP_GNU_const_type:
 	  {
--- a/gdb/dwarf2expr.h
+++ b/gdb/dwarf2expr.h
@@ -62,6 +62,17 @@ struct dwarf_expr_context_funcs
      meaningful to substitute a stub type of the correct size.  */
   struct type *(*get_base_type) (struct dwarf_expr_context *ctx, size_t die);
 
+  /* Push on DWARF stack an entry evaluated for DW_TAG_GNU_call_site's
+     DWARF_REG/FB_OFFSET at the caller of specified BATON.  If DWARF register
+     number DWARF_REG specifying the push_dwarf_reg_entry_value parameter is
+     not -1 FB_OFFSET is ignored.  Otherwise FB_OFFSET specifies stack
+     parameter offset against caller's stack pointer (which equals the callee's
+     frame base).  If DEREF_SIZE is not -1 then use
+     DW_AT_GNU_call_site_data_value instead of DW_AT_GNU_call_site_value.  */
+  void (*push_dwarf_reg_entry_value) (struct dwarf_expr_context *ctx,
+				      int dwarf_reg, CORE_ADDR fb_offset,
+				      CORE_ADDR deref_size);
+
 #if 0
   /* Not yet implemented.  */
 
@@ -253,4 +264,14 @@ const char *dwarf_stack_op_name (unsigned int);
 void dwarf_expr_require_composition (const gdb_byte *, const gdb_byte *,
 				     const char *);
 
+int dwarf_block_to_dwarf_reg (const gdb_byte *buf, const gdb_byte *buf_end);
+
+int dwarf_block_to_sp_offset (struct gdbarch *gdbarch, const gdb_byte *buf,
+			      const gdb_byte *buf_end,
+			      CORE_ADDR *sp_offset_return);
+
+int dwarf_block_to_dwarf_reg_deref (const gdb_byte *buf,
+				    const gdb_byte *buf_end,
+				    CORE_ADDR *deref_size_return);
+
 #endif /* dwarf2expr.h */
--- a/gdb/dwarf2loc.c
+++ b/gdb/dwarf2loc.c
@@ -47,6 +47,8 @@ extern int dwarf2_always_disassemble;
 static void dwarf_expr_frame_base_1 (struct symbol *framefunc, CORE_ADDR pc,
 				     const gdb_byte **start, size_t *length);
 
+static const struct dwarf_expr_context_funcs dwarf_expr_ctx_funcs;
+
 static struct value *dwarf2_evaluate_loc_desc_full (struct type *type,
 						    struct frame_info *frame,
 						    const gdb_byte *data,
@@ -296,6 +298,232 @@ dwarf_expr_get_base_type (struct dwarf_expr_context *ctx, size_t die_offset)
   return dwarf2_get_die_type (die_offset, debaton->per_cu);
 }
 
+/* Find DW_TAG_GNU_call_site's DW_AT_GNU_call_site_target address.
+   CALLER_FRAME (for registers) can be NULL if it is not known.  This function
+   always returns valid address or it throws NOT_FOUND_ERROR.  */
+
+static CORE_ADDR
+call_site_to_target_addr (struct call_site *call_site,
+			  struct frame_info *caller_frame)
+{
+  switch (FIELD_LOC_KIND (call_site->target))
+    {
+    case FIELD_LOC_KIND_DWARF_BLOCK:
+      {
+	struct dwarf2_locexpr_baton *dwarf_block;
+	struct value *val;
+	struct type *caller_core_addr_type;
+	struct gdbarch *caller_arch;
+
+	dwarf_block = FIELD_DWARF_BLOCK (call_site->target);
+	if (dwarf_block == NULL)
+	  throw_error (NOT_FOUND_ERROR,
+		       _("DW_AT_GNU_call_site_target is not specified"));
+	if (caller_frame == NULL)
+	  throw_error (NOT_FOUND_ERROR,
+		       _("DW_AT_GNU_call_site_target DWARF block resolving "
+			 "requires known frame which is currently not "
+			 "available"));
+	caller_arch = get_frame_arch (caller_frame);
+	caller_core_addr_type = builtin_type (caller_arch)->builtin_func_ptr;
+	val = dwarf2_evaluate_loc_desc (caller_core_addr_type, caller_frame,
+					dwarf_block->data, dwarf_block->size,
+					dwarf_block->per_cu);
+	/* DW_AT_GNU_call_site_target is a DWARF expression, not a DWARF
+	   location.  */
+	if (VALUE_LVAL (val) == lval_memory)
+	  return value_address (val);
+	else
+	  return value_as_address (val);
+      }
+
+    case FIELD_LOC_KIND_PHYSNAME:
+      {
+	const char *physname;
+	struct minimal_symbol *msym;
+
+	physname = FIELD_STATIC_PHYSNAME (call_site->target);
+	msym = lookup_minimal_symbol_text (physname, NULL);
+	if (msym == NULL)
+	  throw_error (NOT_FOUND_ERROR,
+		       _("Cannot find function \"%s\" for a call site target"),
+		       physname);
+	return SYMBOL_VALUE_ADDRESS (msym);
+      }
+
+    case FIELD_LOC_KIND_PHYSADDR:
+      return FIELD_STATIC_PHYSADDR (call_site->target);
+
+    default:
+      internal_error (__FILE__, __LINE__, _("invalid call site target kind"));
+    }
+}
+
+/* Fetch call_site_parameter from caller matching the parameters.  FRAME is for
+   callee.  See DWARF_REG and FB_OFFSET description at struct
+   dwarf_expr_context_funcs->push_dwarf_reg_entry_value.
+
+   Function always returns non-NULL, it throws NOT_FOUND_ERROR otherwise.  */
+
+static struct call_site_parameter *
+dwarf_expr_dwarf_reg_entry_value (struct frame_info *frame, int dwarf_reg,
+				  CORE_ADDR fb_offset)
+{
+  CORE_ADDR func_addr = get_frame_func (frame);
+  CORE_ADDR caller_pc;
+  struct gdbarch *caller_gdbarch = frame_unwind_arch (frame);
+  struct frame_info *caller_frame = get_prev_frame (frame);
+  struct call_site *call_site;
+  int iparams;
+  struct value *val;
+  struct dwarf2_locexpr_baton *dwarf_block;
+  struct call_site_parameter *parameter;
+  CORE_ADDR target_addr;
+
+  if (caller_frame == NULL)
+    {
+      struct minimal_symbol *msym = lookup_minimal_symbol_by_pc (func_addr);
+
+      throw_error (NOT_FOUND_ERROR, _("DW_OP_GNU_entry_value resolving "
+				      "requires caller of %s (%s)"),
+		   paddress (get_frame_arch (frame), func_addr),
+		   msym == NULL ? "???" : SYMBOL_PRINT_NAME (msym));
+    }
+  caller_pc = get_frame_pc (caller_frame);
+
+  call_site = call_site_for_pc (caller_pc);
+  if (call_site == NULL)
+    {
+      struct minimal_symbol *msym = lookup_minimal_symbol_by_pc (caller_pc);
+
+      /* DW_TAG_gnu_call_site will be missing just if GCC could not determine
+	 the call target.  So do not complain more than NOT_FOUND_ERROR.  */
+      throw_error (NOT_FOUND_ERROR,
+		   _("DW_OP_GNU_entry_value resolving cannot find "
+		     "DW_TAG_GNU_call_site %s in %s"),
+		   paddress (caller_gdbarch, caller_pc),
+		   msym == NULL ? "???" : SYMBOL_PRINT_NAME (msym));
+    }
+
+  target_addr = call_site_to_target_addr (call_site, caller_frame);
+  if (target_addr != func_addr)
+    {
+      struct minimal_symbol *target_msym, *func_msym;
+
+      target_msym = lookup_minimal_symbol_by_pc (target_addr);
+      func_msym = lookup_minimal_symbol_by_pc (func_addr);
+      throw_error (NOT_FOUND_ERROR,
+		   _("DW_OP_GNU_entry_value resolving expects callee %s at %s "
+		     "but the called frame is for %s at %s"),
+		   (target_msym == NULL ? "???"
+					: SYMBOL_PRINT_NAME (target_msym)),
+		   paddress (caller_gdbarch, target_addr),
+		   func_msym == NULL ? "???" : SYMBOL_PRINT_NAME (func_msym),
+		   paddress (caller_gdbarch, func_addr));
+    }
+
+  for (iparams = 0; iparams < call_site->parameter_count; iparams++)
+    {
+      parameter = &call_site->parameter[iparams];
+      if (parameter->dwarf_reg == -1 && dwarf_reg == -1)
+	{
+	  if (parameter->fb_offset == fb_offset)
+	    break;
+	}
+      else if (parameter->dwarf_reg == dwarf_reg)
+	break;
+    }
+  if (iparams == call_site->parameter_count)
+    {
+      struct minimal_symbol *msym = lookup_minimal_symbol_by_pc (caller_pc);
+
+      /* DW_TAG_GNU_call_site_parameter will be missing just if GCC could not
+	 determine its value.  So do not complain more than NOT_FOUND_ERROR.  */
+      throw_error (NOT_FOUND_ERROR, _("Cannot find DWARF reg%d/fbreg(%s) at "
+				      "DW_TAG_GNU_call_site %s at %s"),
+		   dwarf_reg, paddress (caller_gdbarch, fb_offset),
+		   paddress (caller_gdbarch, caller_pc),
+		   msym == NULL ? "???" : SYMBOL_PRINT_NAME (msym)); 
+    }
+
+  return parameter;
+}
+
+/* Return dwarf2_locexpr_baton for PARAMETER matching DEREF_SIZE.  If
+   DEREF_SIZE is -1, return the normal DW_AT_GNU_call_site_value block.
+   Otherwise return the DW_AT_GNU_call_site_data_value (dereferenced) block.
+
+   Function always returns non-NULL, it throws NOT_FOUND_ERROR if DEREF_SIZE
+   was not -1 and the DW_AT_GNU_call_site_data_value block is not defined by
+   PARAMETER.  */
+
+static struct dwarf2_locexpr_baton *
+dwarf_entry_parameter_to_block (struct call_site_parameter *parameter,
+				CORE_ADDR deref_size)
+{
+
+  if (deref_size == -1)
+    {
+      gdb_assert (parameter->call_site_value != NULL);
+      return parameter->call_site_value;
+    }
+
+  /* DEREF_SIZE size is not verified here.  */
+
+  if (parameter->call_site_data_value == NULL)
+    throw_error (NOT_FOUND_ERROR,
+		 _("Cannot resolve DW_AT_GNU_call_site_data_value"));
+
+  return parameter->call_site_data_value;
+}
+
+/* Execute DWARF_BLOCK for caller of the CTX's frame.  CTX must be of
+   dwarf_expr_ctx_funcs kind.  See DWARF_REG, FB_OFFSET and DEREF_SIZE
+   description at struct dwarf_expr_context_funcs->push_dwarf_reg_entry_value.
+
+   The CTX caller can be from a different CU - per_cu_dwarf_call is simpler as
+   it does not support cross-CU DWARF executions.  */
+
+static void
+dwarf_expr_push_dwarf_reg_entry_value (struct dwarf_expr_context *ctx,
+				       int dwarf_reg, CORE_ADDR fb_offset,
+				       CORE_ADDR deref_size)
+{
+  struct dwarf_expr_baton *debaton;
+  struct frame_info *frame, *caller_frame;
+  struct dwarf2_locexpr_baton *dwarf_block;
+  struct dwarf_expr_baton baton_local;
+  struct dwarf_expr_context saved_ctx;
+  struct call_site_parameter *parameter;
+
+  gdb_assert (ctx->funcs == &dwarf_expr_ctx_funcs);
+  debaton = ctx->baton;
+  frame = debaton->frame;
+  caller_frame = get_prev_frame (frame);
+
+  parameter = dwarf_expr_dwarf_reg_entry_value (frame, dwarf_reg, fb_offset);
+  dwarf_block = dwarf_entry_parameter_to_block (parameter, deref_size);
+
+  baton_local.frame = caller_frame;
+  baton_local.per_cu = dwarf_block->per_cu;
+
+  saved_ctx.gdbarch = ctx->gdbarch;
+  saved_ctx.addr_size = ctx->addr_size;
+  saved_ctx.offset = ctx->offset;
+  saved_ctx.baton = ctx->baton;
+  ctx->gdbarch = get_objfile_arch (dwarf2_per_cu_objfile (baton_local.per_cu));
+  ctx->addr_size = dwarf2_per_cu_addr_size (baton_local.per_cu);
+  ctx->offset = dwarf2_per_cu_text_offset (baton_local.per_cu);
+  ctx->baton = &baton_local;
+
+  dwarf_expr_eval (ctx, dwarf_block->data, dwarf_block->size);
+
+  ctx->gdbarch = saved_ctx.gdbarch;
+  ctx->addr_size = saved_ctx.addr_size;
+  ctx->offset = saved_ctx.offset;
+  ctx->baton = saved_ctx.baton;
+}
+
 struct piece_closure
 {
   /* Reference count.  */
@@ -1083,7 +1311,8 @@ static const struct dwarf_expr_context_funcs dwarf_expr_ctx_funcs =
   dwarf_expr_frame_pc,
   dwarf_expr_tls_address,
   dwarf_expr_dwarf_call,
-  dwarf_expr_get_base_type
+  dwarf_expr_get_base_type,
+  dwarf_expr_push_dwarf_reg_entry_value
 };
 
 /* Evaluate a location description, starting at DATA and with length
@@ -1129,13 +1358,21 @@ dwarf2_evaluate_loc_desc_full (struct type *type, struct frame_info *frame,
     }
   if (ex.reason < 0)
     {
+      do_cleanups (old_chain);
+
       if (ex.error == NOT_AVAILABLE_ERROR)
 	{
-	  do_cleanups (old_chain);
 	  retval = allocate_value (type);
 	  mark_value_bytes_unavailable (retval, 0, TYPE_LENGTH (type));
 	  return retval;
 	}
+      else if (ex.error == NOT_FOUND_ERROR)
+	{
+	  if (info_verbose)
+	    exception_print (gdb_stdout, ex);
+
+	  return allocate_optimized_out_value (type);
+	}
       else
 	throw_exception (ex);
     }
@@ -1351,6 +1588,17 @@ needs_frame_dwarf_call (struct dwarf_expr_context *ctx, size_t die_offset)
 		     ctx->funcs->get_frame_pc, ctx->baton);
 }
 
+/* DW_OP_GNU_entry_value accesses require a caller, therefore a frame.  */
+
+static void
+needs_dwarf_reg_entry_value (struct dwarf_expr_context *ctx, int dwarf_reg,
+			     CORE_ADDR fb_offset, CORE_ADDR deref_size)
+{
+  struct needs_frame_baton *nf_baton = ctx->baton;
+
+  nf_baton->needs_frame = 1;
+}
+
 /* Virtual method table for dwarf2_loc_desc_needs_frame below.  */
 
 static const struct dwarf_expr_context_funcs needs_frame_ctx_funcs =
@@ -1362,7 +1610,8 @@ static const struct dwarf_expr_context_funcs needs_frame_ctx_funcs =
   needs_frame_frame_cfa,	/* get_frame_pc */
   needs_frame_tls_address,
   needs_frame_dwarf_call,
-  NULL				/* get_base_type */
+  NULL,				/* get_base_type */
+  needs_dwarf_reg_entry_value
 };
 
 /* Return non-zero iff the location expression at DATA (length SIZE)
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -400,6 +400,9 @@ struct dwarf2_cu
      after all type information has been read.  */
   VEC (delayed_method_info) *method_list;
 
+  /* To be copied to symtab->call_site_htab.  */
+  htab_t call_site_htab;
+
   /* Mark used when releasing cached dies.  */
   unsigned int mark : 1;
 
@@ -1068,6 +1071,8 @@ static void read_func_scope (struct die_info *, struct dwarf2_cu *);
 
 static void read_lexical_block_scope (struct die_info *, struct dwarf2_cu *);
 
+static void read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu);
+
 static int dwarf2_ranges_read (unsigned, CORE_ADDR *, CORE_ADDR *,
 			       struct dwarf2_cu *, struct partial_symtab *);
 
@@ -4757,6 +4762,8 @@ process_full_comp_unit (struct dwarf2_per_cu_data *per_cu)
 
       if (gcc_4_minor >= 5)
 	symtab->epilogue_unwind_valid = 1;
+
+      symtab->call_site_htab = cu->call_site_htab;
     }
 
   if (dwarf2_per_objfile->using_index)
@@ -4795,6 +4802,9 @@ process_die (struct die_info *die, struct dwarf2_cu *cu)
     case DW_TAG_catch_block:
       read_lexical_block_scope (die, cu);
       break;
+    case DW_TAG_GNU_call_site:
+      read_call_site_scope (die, cu);
+      break;
     case DW_TAG_class_type:
     case DW_TAG_interface_type:
     case DW_TAG_structure_type:
@@ -6074,6 +6084,215 @@ read_lexical_block_scope (struct die_info *die, struct dwarf2_cu *cu)
   using_directives = new->using_directives;
 }
 
+/* Allocate a copy of BLK on CU's objfile_obstack (not comp_unit_obstack),
+   including a copy of the BLK DWARF code.  */
+
+static struct dwarf2_locexpr_baton *
+dlbaton_obstack_copy (const struct dwarf_block *blk, struct dwarf2_cu *cu)
+{
+  struct objfile *objfile = cu->objfile;
+  struct dwarf2_locexpr_baton *dlbaton;
+
+  dlbaton = obstack_alloc (&objfile->objfile_obstack, sizeof (*dlbaton));
+  dlbaton->data = obstack_copy (&objfile->objfile_obstack, blk->data,
+				blk->size);
+  dlbaton->size = blk->size;
+  dlbaton->per_cu = cu->per_cu;
+
+  return dlbaton;
+}
+
+/* Read in DW_TAG_GNU_call_site and insert it to CU->call_site_htab.  */
+
+static void
+read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu)
+{
+  struct objfile *objfile = cu->objfile;
+  struct gdbarch *gdbarch = get_objfile_arch (objfile);
+  CORE_ADDR pc, baseaddr;
+  struct attribute *attr;
+  struct call_site *call_site, call_site_local;
+  void **slot;
+  int nparams;
+  struct die_info *child_die;
+
+  baseaddr = ANOFFSET (objfile->section_offsets, SECT_OFF_TEXT (objfile));
+
+  attr = dwarf2_attr (die, DW_AT_low_pc, cu);
+  if (!attr)
+    {
+      complaint (&symfile_complaints,
+		 _("missing DW_AT_low_pc for DW_TAG_GNU_call_site "
+		   "DIE 0x%x [in module %s]"),
+		 die->offset, cu->objfile->name);
+      return;
+    }
+  pc = DW_ADDR (attr) + baseaddr;
+
+  if (cu->call_site_htab == NULL)
+    cu->call_site_htab = htab_create_alloc_ex (16, core_addr_hash, core_addr_eq,
+					       NULL, &objfile->objfile_obstack,
+					       hashtab_obstack_allocate, NULL);
+  call_site_local.pc = pc;
+  slot = htab_find_slot (cu->call_site_htab, &call_site_local, INSERT);
+  if (*slot != NULL)
+    {
+      complaint (&symfile_complaints,
+		 _("Duplicate PC %s for DW_TAG_GNU_call_site "
+		   "DIE 0x%x [in module %s]"),
+		 paddress (gdbarch, pc), die->offset, cu->objfile->name);
+      return;
+    }
+
+  /* Count parameters at the caller.  */
+
+  nparams = 0;
+  for (child_die = die->child; child_die && child_die->tag;
+       child_die = sibling_die (child_die))
+    {
+      if (child_die->tag != DW_TAG_GNU_call_site_parameter)
+	{
+	  complaint (&symfile_complaints,
+		     _("Tag %d is not DW_TAG_GNU_call_site_parameter in "
+		       "DW_TAG_GNU_call_site child DIE 0x%x [in module %s]"),
+		     child_die->tag, child_die->offset, cu->objfile->name);
+	  continue;
+	}
+
+      nparams++;
+    }
+
+  call_site = obstack_alloc (&objfile->objfile_obstack,
+			     (sizeof (*call_site)
+			      + (sizeof (*call_site->parameter)
+				 * (nparams - 1))));
+  *slot = call_site;
+  memset (call_site, 0, sizeof (*call_site) - sizeof (*call_site->parameter));
+  call_site->pc = pc;
+
+  attr = dwarf2_attr (die, DW_AT_GNU_call_site_target, cu);
+  if (attr == NULL)
+    attr = dwarf2_attr (die, DW_AT_abstract_origin, cu);
+  SET_FIELD_DWARF_BLOCK (call_site->target, NULL);
+  if (!attr || (attr_form_is_block (attr) && DW_BLOCK (attr)->size == 0))
+    /* Keep NULL DWARF_BLOCK.  */;
+  else if (attr_form_is_block (attr))
+    SET_FIELD_DWARF_BLOCK (call_site->target,
+			   dlbaton_obstack_copy (DW_BLOCK (attr), cu));
+  else if (is_ref_attr (attr))
+    {
+      struct objfile *objfile = cu->objfile;
+      struct dwarf2_cu *target_cu = cu;
+      struct die_info *target_die;
+
+      target_die = follow_die_ref_or_sig (die, attr, &target_cu);
+      gdb_assert (target_cu->objfile == objfile);
+      if (die_is_declaration (target_die, target_cu))
+	{
+	  const char *target_physname;
+
+	  target_physname = dwarf2_physname (NULL, target_die, target_cu);
+	  if (target_physname == NULL)
+	    complaint (&symfile_complaints,
+		       _("DW_AT_GNU_call_site_target target DIE has invalid "
+		         "physname, for referencing DIE 0x%x [in module %s]"),
+		       die->offset, cu->objfile->name);
+	  else
+	    SET_FIELD_PHYSNAME (call_site->target, (char *) target_physname);
+	}
+      else
+	{
+	  CORE_ADDR lowpc;
+
+	  /* DW_AT_entry_pc should be preferred.  */
+	  if (!dwarf2_get_pc_bounds (target_die, &lowpc, NULL, target_cu, NULL))
+	    complaint (&symfile_complaints,
+		       _("DW_AT_GNU_call_site_target target DIE has invalid "
+		         "low pc, for referencing DIE 0x%x [in module %s]"),
+		       die->offset, cu->objfile->name);
+	  else
+	    SET_FIELD_PHYSADDR (call_site->target, lowpc + baseaddr);
+	}
+    }
+  else
+    complaint (&symfile_complaints,
+	       _("DW_TAG_GNU_call_site DW_AT_GNU_call_site_target is neither "
+		 "block nor reference, for DIE 0x%x [in module %s]"),
+	       die->offset, cu->objfile->name);
+
+  for (child_die = die->child;
+       child_die && child_die->tag;
+       child_die = sibling_die (child_die))
+    {
+      struct dwarf2_locexpr_baton *dlbaton;
+      struct call_site_parameter *parameter;
+
+      if (child_die->tag != DW_TAG_GNU_call_site_parameter)
+	{
+	  /* Already printed the complaint above.  */
+	  continue;
+	}
+
+      gdb_assert (call_site->parameter_count < nparams);
+      parameter = &call_site->parameter[call_site->parameter_count];
+
+      /* DW_AT_location specifies the register number.  Value of the data
+	 assumed for the register is contained in DW_AT_GNU_call_site_value.  */
+
+      attr = dwarf2_attr (child_die, DW_AT_location, cu);
+      if (!attr || !attr_form_is_block (attr))
+	{
+	  complaint (&symfile_complaints,
+		     _("No DW_FORM_block* DW_AT_location for "
+		       "DW_TAG_GNU_call_site child DIE 0x%x [in module %s]"),
+		     child_die->offset, cu->objfile->name);
+	  continue;
+	}
+      parameter->dwarf_reg = dwarf_block_to_dwarf_reg (DW_BLOCK (attr)->data,
+				 &DW_BLOCK (attr)->data[DW_BLOCK (attr)->size]);
+      if (parameter->dwarf_reg == -1
+	  && !dwarf_block_to_sp_offset (gdbarch, DW_BLOCK (attr)->data,
+				  &DW_BLOCK (attr)->data[DW_BLOCK (attr)->size],
+					&parameter->fb_offset))
+	{
+	  complaint (&symfile_complaints,
+		     _("Only single DW_OP_reg is supported for DW_FORM_block* "
+		       "DW_AT_location for DW_TAG_GNU_call_site "
+		       "child DIE 0x%x [in module %s]"),
+		     child_die->offset, cu->objfile->name);
+	  continue;
+	}
+
+      attr = dwarf2_attr (child_die, DW_AT_GNU_call_site_value, cu);
+      if (!attr_form_is_block (attr))
+	{
+	  complaint (&symfile_complaints,
+		     _("No DW_FORM_block* DW_AT_GNU_call_site_value for "
+		       "DW_TAG_GNU_call_site child DIE 0x%x [in module %s]"),
+		     child_die->offset, cu->objfile->name);
+	  continue;
+	}
+      parameter->call_site_value = dlbaton_obstack_copy (DW_BLOCK (attr), cu);
+
+      /* Parameters are not pre-cleared by memset above.  */
+      parameter->call_site_data_value = NULL;
+      call_site->parameter_count++;
+
+      attr = dwarf2_attr (child_die, DW_AT_GNU_call_site_data_value, cu);
+      if (attr)
+	{
+	  if (!attr_form_is_block (attr))
+	    complaint (&symfile_complaints,
+		       _("No DW_FORM_block* DW_AT_GNU_call_site_data_value for "
+			 "DW_TAG_GNU_call_site child DIE 0x%x [in module %s]"),
+		       child_die->offset, cu->objfile->name);
+	  else
+	    parameter->call_site_data_value
+	      = dlbaton_obstack_copy (DW_BLOCK (attr), cu);
+	}
+    }
+}
+
 /* Get low and high pc attributes from DW_AT_ranges attribute value OFFSET.
    Return 1 if the attributes are present and valid, otherwise, return 0.
    If RANGES_PST is not NULL we should setup `objfile->psymtabs_addrmap'.  */
@@ -6273,7 +6492,8 @@ dwarf2_get_pc_bounds (struct die_info *die, CORE_ADDR *lowpc,
     return 0;
 
   *lowpc = low;
-  *highpc = high;
+  if (highpc)
+    *highpc = high;
   return ret;
 }
 
@@ -12624,6 +12844,8 @@ dwarf_tag_name (unsigned tag)
       return "DW_TAG_PGI_kanji_type";
     case DW_TAG_PGI_interface_block:
       return "DW_TAG_PGI_interface_block";
+    case DW_TAG_GNU_call_site:
+      return "DW_TAG_GNU_call_site";
     default:
       return "DW_TAG_<unknown>";
     }
--- a/gdb/gdb-gdb.py
+++ b/gdb/gdb-gdb.py
@@ -158,6 +158,8 @@ class StructMainTypePrettyPrinter:
             return 'physaddr = 0x%x' % loc_val['physaddr']
         elif loc_kind == "FIELD_LOC_KIND_PHYSNAME":
             return 'physname = %s' % loc_val['physname']
+        elif loc_kind == "FIELD_LOC_KIND_DWARF_BLOCK":
+            return 'dwarf_block = %s' % loc_val['dwarf_block']
         else:
             return 'loc = ??? (unsupported loc_kind value)'
     def struct_field_img(self, fieldno):
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -348,7 +348,8 @@ enum field_loc_kind
   {
     FIELD_LOC_KIND_BITPOS,	/* bitpos */
     FIELD_LOC_KIND_PHYSADDR,	/* physaddr */
-    FIELD_LOC_KIND_PHYSNAME	/* physname */
+    FIELD_LOC_KIND_PHYSNAME,	/* physname */
+    FIELD_LOC_KIND_DWARF_BLOCK	/* dwarf_block */
   };
 
 /* A discriminant to determine which field in the main_type.type_specific
@@ -510,6 +511,12 @@ struct main_type
 
 	CORE_ADDR physaddr;
 	const char *physname;
+
+	/* The field location can be computed by evaluating the following DWARF
+	   block.  Its DATA is allocated on objfile_obstack - no CU load is
+	   needed to access it.  */
+
+	struct dwarf2_locexpr_baton *dwarf_block;
       }
       loc;
 
@@ -897,6 +904,53 @@ struct func_type
     unsigned calling_convention;
   };
 
+/* A place where some function gets called from, represented by
+   DW_TAG_GNU_call_site.  It can be looked up from symtab->call_site_htab.  */
+
+struct call_site
+  {
+    /* Address of the first instruction after this call.  It must be the first
+       field as we overload core_addr_hash and core_addr_eq for it.  */
+    CORE_ADDR pc;
+
+    /* Describe DW_AT_GNU_call_site_target.  Missing attribute uses
+       FIELD_LOC_KIND_DWARF_BLOCK with FIELD_DWARF_BLOCK == NULL.  */
+    struct
+      {
+	union field_location loc;
+
+	/* Discriminant for union field_location.  */
+	ENUM_BITFIELD(field_loc_kind) loc_kind : 2;
+      }
+    target;
+
+    /* Size of the PARAMETER array.  */
+    unsigned parameter_count;
+
+    /* Describe DW_TAG_GNU_call_site's DW_TAG_formal_parameter.  */
+    struct call_site_parameter
+      {
+	/* DW_TAG_formal_parameter's DW_AT_location's DW_OP_regX as DWARF
+	   register number, for register passed parameters.  If -1 then use
+	   fb_offset.  */
+	int dwarf_reg;
+
+	/* Offset from the callee's frame base, for stack passed parameters.
+	   This equals offset from the caller's stack pointer.  Valid only if
+	   DWARF_REGNUM is -1.  */
+	CORE_ADDR fb_offset;
+
+	/* DW_TAG_formal_parameter's DW_AT_GNU_call_site_value.  It is never
+	   NULL.  */
+	struct dwarf2_locexpr_baton *call_site_value;
+
+	/* DW_TAG_formal_parameter's DW_AT_GNU_call_site_data_value.  It may be
+	   NULL if not provided by DWARF.  */
+	struct dwarf2_locexpr_baton *call_site_data_value;
+      }
+    parameter[1];
+  };
+
 /* The default value of TYPE_CPLUS_SPECIFIC(T) points to the
    this shared static structure.  */
 
@@ -1019,6 +1073,7 @@ extern void allocate_gnat_aux_type (struct type *);
 #define FIELD_BITPOS(thisfld) ((thisfld).loc.bitpos)
 #define FIELD_STATIC_PHYSNAME(thisfld) ((thisfld).loc.physname)
 #define FIELD_STATIC_PHYSADDR(thisfld) ((thisfld).loc.physaddr)
+#define FIELD_DWARF_BLOCK(thisfld) ((thisfld).loc.dwarf_block)
 #define SET_FIELD_BITPOS(thisfld, bitpos)			\
   (FIELD_LOC_KIND (thisfld) = FIELD_LOC_KIND_BITPOS,		\
    FIELD_BITPOS (thisfld) = (bitpos))
@@ -1028,6 +1083,9 @@ extern void allocate_gnat_aux_type (struct type *);
 #define SET_FIELD_PHYSADDR(thisfld, addr)			\
   (FIELD_LOC_KIND (thisfld) = FIELD_LOC_KIND_PHYSADDR,		\
    FIELD_STATIC_PHYSADDR (thisfld) = (addr))
+#define SET_FIELD_DWARF_BLOCK(thisfld, addr)			\
+  (FIELD_LOC_KIND (thisfld) = FIELD_LOC_KIND_DWARF_BLOCK,	\
+   FIELD_DWARF_BLOCK (thisfld) = (addr))
 #define FIELD_ARTIFICIAL(thisfld) ((thisfld).artificial)
 #define FIELD_BITSIZE(thisfld) ((thisfld).bitsize)
 
@@ -1038,6 +1096,7 @@ extern void allocate_gnat_aux_type (struct type *);
 #define TYPE_FIELD_BITPOS(thistype, n) FIELD_BITPOS (TYPE_FIELD (thistype, n))
 #define TYPE_FIELD_STATIC_PHYSNAME(thistype, n) FIELD_STATIC_PHYSNAME (TYPE_FIELD (thistype, n))
 #define TYPE_FIELD_STATIC_PHYSADDR(thistype, n) FIELD_STATIC_PHYSADDR (TYPE_FIELD (thistype, n))
+#define TYPE_FIELD_DWARF_BLOCK(thistype, n) FIELD_DWARF_BLOCK (TYPE_FIELD (thistype, n))
 #define TYPE_FIELD_ARTIFICIAL(thistype, n) FIELD_ARTIFICIAL(TYPE_FIELD(thistype,n))
 #define TYPE_FIELD_BITSIZE(thistype, n) FIELD_BITSIZE(TYPE_FIELD(thistype,n))
 #define TYPE_FIELD_PACKED(thistype, n) (FIELD_BITSIZE(TYPE_FIELD(thistype,n))!=0)
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -831,6 +831,9 @@ struct symtab
 
   struct objfile *objfile;
 
+  /* struct call_site entries for this compilation unit or NULL.  */
+
+  htab_t call_site_htab;
 };
 
 #define BLOCKVECTOR(symtab)	(symtab)->blockvector
--- a/gdb/testsuite/gdb.arch/Makefile.in
+++ b/gdb/testsuite/gdb.arch/Makefile.in
@@ -1,8 +1,8 @@
 VPATH = @srcdir@
 srcdir = @srcdir@
 
-EXECUTABLES = altivec-abi altivec-regs amd64-byte amd64-disp-step \
-	amd64-dword amd64-i386-address amd64-word i386-bp_permanent \
+EXECUTABLES = altivec-abi altivec-regs amd64-byte amd64-disp-step amd64-dword \
+	amd64-entry-value amd64-i386-address amd64-word i386-bp_permanent \
 	i386-permbkpt i386-avx i386-signal i386-sse
 
 all info install-info dvi install uninstall installcheck check:
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-entry-value.cc
@@ -0,0 +1,41 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2011 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 volatile int v;
+
+static void __attribute__((noinline, noclone))
+e (int i)
+{
+  v = 0;
+}
+
+static void __attribute__((noinline, noclone))
+d (int i)
+{
+  i++;
+  e (i);
+  e (v);
+asm ("breakhere:");
+  e (v);
+}
+
+int
+main ()
+{
+  d (30);
+  return 0;
+}
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-entry-value.exp
@@ -0,0 +1,49 @@
+# Copyright (C) 2011 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/>.
+
+set testfile amd64-entry-value
+set srcfile ${testfile}.s
+set opts {}
+
+if [info exists COMPILE] {
+    # make check RUNTESTFLAGS="gdb.arch/amd64-entry-value.exp COMPILE=1"
+    set srcfile ${testfile}.cc
+    lappend opts debug optimize=-O2
+} elseif { ![istarget x86_64-*-* ] || ![is_lp64_target] } {
+    verbose "Skipping amd64-entry-value."
+    return
+}
+
+if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile} $opts] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+gdb_breakpoint "breakhere"
+
+
+# Test @entry values for register passed parameters.
+
+gdb_continue_to_breakpoint "entry: breakhere"
+
+# (gdb) bt full
+# #0  d (i=31) at gdb.arch/amd64-entry-value.cc:33
+# #1  0x00000000004003da in main () at gdb.arch/amd64-entry-value.cc:56
+gdb_test "bt full" "^bt full\r\n#0 +d *\\(i=31\\) \[^\r\n\]*\r\nNo locals\\.\r\n#1 +0x\[0-9a-f\]+ in main .*" \
+	 "entry: bt full"
+gdb_test "p i" " = 31" "entry: p i"
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -403,6 +403,24 @@ make_cleanup_unpush_target (struct target_ops *ops)
   return make_my_cleanup (&cleanup_chain, do_unpush_target, ops);
 }
 
+/* Helper for make_cleanup_htab_delete compile time checking the types.  */
+
+static void
+do_htab_delete_cleanup (void *htab_voidp)
+{
+  htab_t htab = htab_voidp;
+
+  htab_delete (htab);
+}
+
+/* Return a new cleanup that deletes HTAB.  */
+
+struct cleanup *
+make_cleanup_htab_delete (htab_t htab)
+{
+  return make_cleanup (do_htab_delete_cleanup, htab);
+}
+
 struct restore_ui_file_closure
 {
   struct ui_file **variable;
@@ -3051,6 +3069,27 @@ print_core_address (struct gdbarch *gdbarch, CORE_ADDR address)
     return hex_string_custom (address, 16);
 }
 
+/* Callback hash_f for htab_create_alloc or htab_create_alloc_ex.  */
+
+hashval_t
+core_addr_hash (const void *ap)
+{
+  const CORE_ADDR *addrp = ap;
+
+  return *addrp;
+}
+
+/* Callback eq_f for htab_create_alloc or htab_create_alloc_ex.  */
+
+int
+core_addr_eq (const void *ap, const void *bp)
+{
+  const CORE_ADDR *addr_ap = ap;
+  const CORE_ADDR *addr_bp = bp;
+
+  return *addr_ap == *addr_bp;
+}
+
 static char *
 decimal2str (char *sign, ULONGEST addr, int width)
 {

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [patch 02/12] entryval: Basic parameter values recovery
  2011-07-18 20:17 [patch 02/12] entryval: Basic parameter values recovery Jan Kratochvil
@ 2011-07-19 14:43 ` Tom Tromey
  2011-07-24 18:10   ` [patch] Remove excessive DWARF block xmemdup by me [Re: [patch 02/12] entryval: Basic parameter values recovery] Jan Kratochvil
  2011-07-29 15:35   ` [patch 02/12] entryval: Basic parameter values recovery Jan Kratochvil
  0 siblings, 2 replies; 6+ messages in thread
From: Tom Tromey @ 2011-07-19 14:43 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches

>>>>> "Jan" == Jan Kratochvil <jan.kratochvil@redhat.com> writes:

Jan> +extern struct cleanup *make_cleanup_htab_delete (htab_t htab);

Just a minor additional cleanup -- dwarf2read.c:cleanup_htab can now be
removed.  I can do this after the patch goes in if you like.

Jan> +	    error (_("DWARF-2 expression error: DW_OP_GNU_entry_value is "
Jan> +		     "supported only for single DW_OP_reg* "
Jan> +		     "or for DW_OP_breg*(0)+DW_OP_deref*"));

I'm a little surprised that DW_OP_GNU_regval_type isn't included here;
but I suppose that if Jakub adds it to the spec and to GCC, then we can
easily update.

Jan> +/* Allocate a copy of BLK on CU's objfile_obstack (not comp_unit_obstack),
Jan> +   including a copy of the BLK DWARF code.  */
Jan> +
Jan> +static struct dwarf2_locexpr_baton *
Jan> +dlbaton_obstack_copy (const struct dwarf_block *blk, struct dwarf2_cu *cu)

I don't understand the need for this.

Once we have mapped in a DWARF section, we do not unmap it until the
objfile is destroyed or reloaded.  At that point, the types are all
destroyed as well.  So, the lifetimes are already in sync, and you can
just store a pointer directly to the DWARF data.  We already rely on
this in many cases.

Jan> @@ -12624,6 +12844,8 @@ dwarf_tag_name (unsigned tag)
Jan>        return "DW_TAG_PGI_kanji_type";
Jan>      case DW_TAG_PGI_interface_block:
Jan>        return "DW_TAG_PGI_interface_block";
Jan> +    case DW_TAG_GNU_call_site:
Jan> +      return "DW_TAG_GNU_call_site";

Thanks.

I wish this were more automatic; and done in one spot so we don't
constantly have to update both binutils and gdb for this kind of change.

Jan> +    FIELD_LOC_KIND_DWARF_BLOCK	/* dwarf_block */

Since the new data is stored as a field, it will change the Python API.
I think there are two options:

1. Document what the fields of a function mean.
2. Disallow fetching these fields in typy_fields.

I tend to prefer #2, but I can see arguments either way.

Tom

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [patch] Remove excessive DWARF block xmemdup by me  [Re: [patch 02/12] entryval: Basic parameter values recovery]
  2011-07-19 14:43 ` Tom Tromey
@ 2011-07-24 18:10   ` Jan Kratochvil
  2011-09-13 21:53     ` [patch] Remove excessive DWARF block xmemdup by me Jan Kratochvil
  2011-07-29 15:35   ` [patch 02/12] entryval: Basic parameter values recovery Jan Kratochvil
  1 sibling, 1 reply; 6+ messages in thread
From: Jan Kratochvil @ 2011-07-24 18:10 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On Tue, 19 Jul 2011 16:19:00 +0200, Tom Tromey wrote:
> >>>>> "Jan" == Jan Kratochvil <jan.kratochvil@redhat.com> writes:
> Jan> +/* Allocate a copy of BLK on CU's objfile_obstack (not comp_unit_obstack),
> Jan> +   including a copy of the BLK DWARF code.  */
> Jan> +
> Jan> +static struct dwarf2_locexpr_baton *
> Jan> +dlbaton_obstack_copy (const struct dwarf_block *blk, struct dwarf2_cu *cu)
> 
> I don't understand the need for this.
> 
> Once we have mapped in a DWARF section, we do not unmap it until the
> objfile is destroyed or reloaded.  At that point, the types are all
> destroyed as well.  So, the lifetimes are already in sync, and you can
> just store a pointer directly to the DWARF data.  We already rely on
> this in many cases.

I agree.  In such case my recently checked-in patch is also excessively memory
copying this way.
	[patch] Fix DW_OP_call2 and DW_OP_call4 for max-cache-age 0 #2
	http://sourceware.org/ml/gdb-patches/2011-07/msg00343.html
	f35c0b67e636d7ccddfee8ace6b462a73394b482

I will check it in.  No regressions on
{x86_64,x86_64-m32,i686}-fedora16pre-linux-gnu.


Thanks,
Jan


gdb/
2011-07-24  Jan Kratochvil  <jan.kratochvil@redhat.com>

	Remove excessive DWARF expressions memory duplication.
	* dwarf2loc.c (per_cu_dwarf_call): Remove variable back_to and its use
	for block.data.
	(indirect_pieced_value): Remove variable result.  Remove variable
	back_to and its use for baton.data.
	(dwarf2_compile_expr_to_ax): Remove variable back_to and its use for
	block.data.
	* dwarf2read.c (dwarf2_fetch_die_location_block): Remove xmemdup.
	Update the function comment.

--- a/gdb/dwarf2loc.c
+++ b/gdb/dwarf2loc.c
@@ -265,19 +265,14 @@ per_cu_dwarf_call (struct dwarf_expr_context *ctx, size_t die_offset,
 		   void *baton)
 {
   struct dwarf2_locexpr_baton block;
-  struct cleanup *back_to;
 
   block = dwarf2_fetch_die_location_block (die_offset, per_cu,
 					   get_frame_pc, baton);
 
-  back_to = make_cleanup (xfree, (void *) block.data);
-
   /* DW_OP_call_ref is currently not supported.  */
   gdb_assert (block.per_cu == per_cu);
 
   dwarf_expr_eval (ctx, block.data, block.size);
-
-  do_cleanups (back_to);
 }
 
 /* Helper interface of per_cu_dwarf_call for dwarf2_evaluate_loc_desc.  */
@@ -969,9 +964,7 @@ indirect_pieced_value (struct value *value)
   struct dwarf2_locexpr_baton baton;
   int i, bit_offset, bit_length;
   struct dwarf_expr_piece *piece = NULL;
-  struct value *result;
   LONGEST byte_offset;
-  struct cleanup *back_to;
 
   type = check_typedef (value_type (value));
   if (TYPE_CODE (type) != TYPE_CODE_PTR)
@@ -1019,15 +1012,9 @@ indirect_pieced_value (struct value *value)
 					   get_frame_address_in_block_wrapper,
 					   frame);
 
-  back_to = make_cleanup (xfree, (void *) baton.data);
-
-  result = dwarf2_evaluate_loc_desc_full (TYPE_TARGET_TYPE (type), frame,
-					  baton.data, baton.size, baton.per_cu,
-					  byte_offset);
-
-  do_cleanups (back_to);
-
-  return result;
+  return dwarf2_evaluate_loc_desc_full (TYPE_TARGET_TYPE (type), frame,
+					baton.data, baton.size, baton.per_cu,
+					byte_offset);
 }
 
 static void *
@@ -2133,14 +2120,12 @@ dwarf2_compile_expr_to_ax (struct agent_expr *expr, struct axs_value *loc,
 	  {
 	    struct dwarf2_locexpr_baton block;
 	    int size = (op == DW_OP_call2 ? 2 : 4);
-	    struct cleanup *back_to;
 
 	    uoffset = extract_unsigned_integer (op_ptr, size, byte_order);
 	    op_ptr += size;
 
 	    block = dwarf2_fetch_die_location_block (uoffset, per_cu,
 						     get_ax_pc, expr);
-	    back_to = make_cleanup (xfree, (void *) block.data);
 
 	    /* DW_OP_call_ref is currently not supported.  */
 	    gdb_assert (block.per_cu == per_cu);
@@ -2148,8 +2133,6 @@ dwarf2_compile_expr_to_ax (struct agent_expr *expr, struct axs_value *loc,
 	    dwarf2_compile_expr_to_ax (expr, loc, arch, addr_size,
 				       block.data, block.data + block.size,
 				       per_cu);
-
-	    do_cleanups (back_to);
 	  }
 	  break;
 
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -13892,9 +13892,9 @@ follow_die_ref (struct die_info *src_die, struct attribute *attr,
   return die;
 }
 
-/* Return DWARF block and its CU referenced by OFFSET at PER_CU.  Returned
-   value is intended for DW_OP_call*.  You must call xfree on returned
-   dwarf2_locexpr_baton->data.  */
+/* Return DWARF block referenced by DW_AT_location of DIE at OFFSET at PER_CU.
+   Returned value is intended for DW_OP_call*.  Returned
+   dwarf2_locexpr_baton->data has lifetime of PER_CU->OBJFILE.  */
 
 struct dwarf2_locexpr_baton
 dwarf2_fetch_die_location_block (unsigned int offset,
@@ -13950,9 +13950,6 @@ dwarf2_fetch_die_location_block (unsigned int offset,
     }
   retval.per_cu = cu->per_cu;
 
-  if (retval.data)
-    retval.data = xmemdup (retval.data, retval.size, retval.size);
-
   age_cached_comp_units ();
 
   return retval;

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [patch 02/12] entryval: Basic parameter values recovery
  2011-07-19 14:43 ` Tom Tromey
  2011-07-24 18:10   ` [patch] Remove excessive DWARF block xmemdup by me [Re: [patch 02/12] entryval: Basic parameter values recovery] Jan Kratochvil
@ 2011-07-29 15:35   ` Jan Kratochvil
  2011-07-29 16:08     ` Tom Tromey
  1 sibling, 1 reply; 6+ messages in thread
From: Jan Kratochvil @ 2011-07-29 15:35 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On Tue, 19 Jul 2011 16:19:00 +0200, Tom Tromey wrote:
> >>>>> "Jan" == Jan Kratochvil <jan.kratochvil@redhat.com> writes:
> 
> Jan> +extern struct cleanup *make_cleanup_htab_delete (htab_t htab);
> 
> Just a minor additional cleanup -- dwarf2read.c:cleanup_htab can now be
> removed.  I can do this after the patch goes in if you like.

done.


> Jan> +	    error (_("DWARF-2 expression error: DW_OP_GNU_entry_value is "
> Jan> +		     "supported only for single DW_OP_reg* "
> Jan> +		     "or for DW_OP_breg*(0)+DW_OP_deref*"));
> 
> I'm a little surprised that DW_OP_GNU_regval_type isn't included here;
> but I suppose that if Jakub adds it to the spec and to GCC, then we can
> easily update.

done.  There are now some tests for `double' recomputations, after Jakub's fix
of GCC PR debug/49846.


> Jan> +/* Allocate a copy of BLK on CU's objfile_obstack (not comp_unit_obstack),
> Jan> +   including a copy of the BLK DWARF code.  */
> Jan> +
> Jan> +static struct dwarf2_locexpr_baton *
> Jan> +dlbaton_obstack_copy (const struct dwarf_block *blk, struct dwarf2_cu *cu)
> 
> I don't understand the need for this.
> 
> Once we have mapped in a DWARF section, we do not unmap it until the
> objfile is destroyed or reloaded.  At that point, the types are all
> destroyed as well.  So, the lifetimes are already in sync, and you can
> just store a pointer directly to the DWARF data.  We already rely on
> this in many cases.

I agree, the DWARF data are no longer copied.


> Jan> +    FIELD_LOC_KIND_DWARF_BLOCK	/* dwarf_block */
> 
> Since the new data is stored as a field, it will change the Python API.
> I think there are two options:
> 
> 1. Document what the fields of a function mean.
> 2. Disallow fetching these fields in typy_fields.
> 
> I tend to prefer #2, but I can see arguments either way.

enum field_loc_kind is used only for types comparisons in gdb/python/ .
Implemented there FIELD_LOC_KIND_DWARF_BLOCK.

TYPE_CALLING_CONVENTION is not used at all in gdb/python/ so I haven't tried
to implement in gdb/python/ also TYPE_TAIL_CALL_LIST where both belong to
type_specific.func_stuff now.

I do not see how user can "fetch" the type kind in gdb/python/ , convert_field
does not access FIELD_LOC_KIND nor TYPE_FIELD_LOC_KIND in any way.


It will be in a new patchset resubmit.


Thanks,
Jan

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [patch 02/12] entryval: Basic parameter values recovery
  2011-07-29 15:35   ` [patch 02/12] entryval: Basic parameter values recovery Jan Kratochvil
@ 2011-07-29 16:08     ` Tom Tromey
  0 siblings, 0 replies; 6+ messages in thread
From: Tom Tromey @ 2011-07-29 16:08 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches

>>>>> "Jan" == Jan Kratochvil <jan.kratochvil@redhat.com> writes:

Jan> I do not see how user can "fetch" the type kind in gdb/python/ ,
Jan> convert_field does not access FIELD_LOC_KIND nor
Jan> TYPE_FIELD_LOC_KIND in any way.

I think I misunderstood the patch.

Tom

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [patch] Remove excessive DWARF block xmemdup by me
  2011-07-24 18:10   ` [patch] Remove excessive DWARF block xmemdup by me [Re: [patch 02/12] entryval: Basic parameter values recovery] Jan Kratochvil
@ 2011-09-13 21:53     ` Jan Kratochvil
  0 siblings, 0 replies; 6+ messages in thread
From: Jan Kratochvil @ 2011-09-13 21:53 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On Sun, 24 Jul 2011 19:51:23 +0200, Jan Kratochvil wrote:
> gdb/
> 2011-07-24  Jan Kratochvil  <jan.kratochvil@redhat.com>
> 
> 	Remove excessive DWARF expressions memory duplication.
> 	* dwarf2loc.c (per_cu_dwarf_call): Remove variable back_to and its use
> 	for block.data.
> 	(indirect_pieced_value): Remove variable result.  Remove variable
> 	back_to and its use for baton.data.
> 	(dwarf2_compile_expr_to_ax): Remove variable back_to and its use for
> 	block.data.
> 	* dwarf2read.c (dwarf2_fetch_die_location_block): Remove xmemdup.
> 	Update the function comment.

Checked in, with a small technical re-diff.
(This is just a fix-up of a fault of mine I checked in before.)


Sorry,
Jan


http://sourceware.org/ml/gdb-cvs/2011-09/msg00077.html

--- src/gdb/ChangeLog	2011/09/13 19:27:00	1.13327
+++ src/gdb/ChangeLog	2011/09/13 21:44:24	1.13328
@@ -1,3 +1,15 @@
+2011-09-13  Jan Kratochvil  <jan.kratochvil@redhat.com>
+
+	Remove excessive DWARF expressions memory duplication.
+	* dwarf2loc.c (per_cu_dwarf_call): Remove variable back_to and its use
+	for block.data.
+	(indirect_pieced_value): Remove variable result.  Remove variable
+	back_to and its use for baton.data.
+	(dwarf2_compile_expr_to_ax): Remove variable back_to and its use for
+	block.data.
+	* dwarf2read.c (dwarf2_fetch_die_location_block): Remove xmemdup.
+	Update the function comment.
+
 2011-09-13  Pedro Alves  <pedro@codesourcery.com>
 
 	* inferior.h (ALL_INFERIORS): New.
--- src/gdb/dwarf2loc.c	2011/08/08 21:41:13	1.127
+++ src/gdb/dwarf2loc.c	2011/09/13 21:44:27	1.128
@@ -265,19 +265,14 @@
 		   void *baton)
 {
   struct dwarf2_locexpr_baton block;
-  struct cleanup *back_to;
 
   block = dwarf2_fetch_die_location_block (die_offset, per_cu,
 					   get_frame_pc, baton);
 
-  back_to = make_cleanup (xfree, (void *) block.data);
-
   /* DW_OP_call_ref is currently not supported.  */
   gdb_assert (block.per_cu == per_cu);
 
   dwarf_expr_eval (ctx, block.data, block.size);
-
-  do_cleanups (back_to);
 }
 
 /* Helper interface of per_cu_dwarf_call for dwarf2_evaluate_loc_desc.  */
@@ -969,9 +964,7 @@
   struct dwarf2_locexpr_baton baton;
   int i, bit_offset, bit_length;
   struct dwarf_expr_piece *piece = NULL;
-  struct value *result;
   LONGEST byte_offset;
-  struct cleanup *back_to;
 
   type = check_typedef (value_type (value));
   if (TYPE_CODE (type) != TYPE_CODE_PTR)
@@ -1021,15 +1014,9 @@
 					   get_frame_address_in_block_wrapper,
 					   frame);
 
-  back_to = make_cleanup (xfree, (void *) baton.data);
-
-  result = dwarf2_evaluate_loc_desc_full (TYPE_TARGET_TYPE (type), frame,
-					  baton.data, baton.size, baton.per_cu,
-					  piece->v.ptr.offset + byte_offset);
-
-  do_cleanups (back_to);
-
-  return result;
+  return dwarf2_evaluate_loc_desc_full (TYPE_TARGET_TYPE (type), frame,
+					baton.data, baton.size, baton.per_cu,
+					piece->v.ptr.offset + byte_offset);
 }
 
 static void *
@@ -2147,14 +2134,12 @@
 	  {
 	    struct dwarf2_locexpr_baton block;
 	    int size = (op == DW_OP_call2 ? 2 : 4);
-	    struct cleanup *back_to;
 
 	    uoffset = extract_unsigned_integer (op_ptr, size, byte_order);
 	    op_ptr += size;
 
 	    block = dwarf2_fetch_die_location_block (uoffset, per_cu,
 						     get_ax_pc, expr);
-	    back_to = make_cleanup (xfree, (void *) block.data);
 
 	    /* DW_OP_call_ref is currently not supported.  */
 	    gdb_assert (block.per_cu == per_cu);
@@ -2162,8 +2147,6 @@
 	    dwarf2_compile_expr_to_ax (expr, loc, arch, addr_size,
 				       block.data, block.data + block.size,
 				       per_cu);
-
-	    do_cleanups (back_to);
 	  }
 	  break;
 
--- src/gdb/dwarf2read.c	2011/09/09 19:41:13	1.560
+++ src/gdb/dwarf2read.c	2011/09/13 21:44:27	1.561
@@ -13854,9 +13854,9 @@
   return die;
 }
 
-/* Return DWARF block and its CU referenced by OFFSET at PER_CU.  Returned
-   value is intended for DW_OP_call*.  You must call xfree on returned
-   dwarf2_locexpr_baton->data.  */
+/* Return DWARF block referenced by DW_AT_location of DIE at OFFSET at PER_CU.
+   Returned value is intended for DW_OP_call*.  Returned
+   dwarf2_locexpr_baton->data has lifetime of PER_CU->OBJFILE.  */
 
 struct dwarf2_locexpr_baton
 dwarf2_fetch_die_location_block (unsigned int offset,
@@ -13912,9 +13912,6 @@
     }
   retval.per_cu = cu->per_cu;
 
-  if (retval.data)
-    retval.data = xmemdup (retval.data, retval.size, retval.size);
-
   age_cached_comp_units ();
 
   return retval;

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2011-09-13 21:45 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-07-18 20:17 [patch 02/12] entryval: Basic parameter values recovery Jan Kratochvil
2011-07-19 14:43 ` Tom Tromey
2011-07-24 18:10   ` [patch] Remove excessive DWARF block xmemdup by me [Re: [patch 02/12] entryval: Basic parameter values recovery] Jan Kratochvil
2011-09-13 21:53     ` [patch] Remove excessive DWARF block xmemdup by me Jan Kratochvil
2011-07-29 15:35   ` [patch 02/12] entryval: Basic parameter values recovery Jan Kratochvil
2011-07-29 16:08     ` Tom Tromey

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).