public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
From: Simon Marchi <simon.marchi@polymtl.ca>
To: gdb-patches@sourceware.org
Cc: Simon Marchi <simon.marchi@efficios.com>
Subject: [PATCH v2 05/13] gdb: move call site types to call-site.h
Date: Tue, 13 Dec 2022 22:34:33 -0500	[thread overview]
Message-ID: <20221214033441.499512-6-simon.marchi@polymtl.ca> (raw)
In-Reply-To: <20221214033441.499512-1-simon.marchi@polymtl.ca>

From: Simon Marchi <simon.marchi@efficios.com>

I hesitated between putting  the file in the dwarf2 directory (as
gdb/dwarf2/call-site.h) or in the common directory (as gdb/call-site.h).
The concept of call site is not DWARF-specific, another debug info
reader could provide this information.  But as it is, the implementation
is a bit DWARF-specific, as one form it can take is a DWARF expression
and parameters can be defined using a DWARF register number.  So I ended up
choosing to put it under dwarf2/.  If another debug info reader ever
wants to provide call site information, we can introduce a layer of
abstraction between the "common" call site and the "dwarf2" call site.

The copyright start year comes from the date `struct call_site` was
introduced.

Change-Id: I1cd84aa581fbbf729edc91b20f7d7a6e0377014d
---
 gdb/dwarf2/abbrev-cache.h   |   1 -
 gdb/dwarf2/attribute.h      |   2 +-
 gdb/dwarf2/call-site.h      | 244 ++++++++++++++++++++++++++++++++++++
 gdb/dwarf2/comp-unit-head.h |   5 +-
 gdb/dwarf2/cooked-index.h   |   2 +-
 gdb/dwarf2/expr.h           |   2 +-
 gdb/dwarf2/line-header.h    |   2 -
 gdb/dwarf2/types.h          |   1 +
 gdb/gdbtypes.h              | 211 -------------------------------
 gdb/symtab.c                |   1 +
 10 files changed, 253 insertions(+), 218 deletions(-)
 create mode 100644 gdb/dwarf2/call-site.h

diff --git a/gdb/dwarf2/abbrev-cache.h b/gdb/dwarf2/abbrev-cache.h
index d729eb9dd1bb..c56a7aba217a 100644
--- a/gdb/dwarf2/abbrev-cache.h
+++ b/gdb/dwarf2/abbrev-cache.h
@@ -21,7 +21,6 @@
 #define GDB_DWARF2_ABBREV_CACHE_H
 
 #include "dwarf2/abbrev.h"
-#include "gdbtypes.h"
 
 /* An abbrev cache holds abbrev tables for easier reuse.  */
 class abbrev_cache
diff --git a/gdb/dwarf2/attribute.h b/gdb/dwarf2/attribute.h
index 447d195eb9a4..aa6380cb11fb 100644
--- a/gdb/dwarf2/attribute.h
+++ b/gdb/dwarf2/attribute.h
@@ -28,7 +28,7 @@
 #define GDB_DWARF2_ATTRIBUTE_H
 
 #include "dwarf2.h"
-#include "gdbtypes.h"
+#include "dwarf2/types.h"
 #include "gdbsupport/gdb_optional.h"
 
 /* Blocks are a bunch of untyped bytes.  */
diff --git a/gdb/dwarf2/call-site.h b/gdb/dwarf2/call-site.h
new file mode 100644
index 000000000000..f5bee967594e
--- /dev/null
+++ b/gdb/dwarf2/call-site.h
@@ -0,0 +1,244 @@
+/* Call site information.
+
+   Copyright (C) 2011-2022 Free Software Foundation, Inc.
+
+   Contributed by Cygnus Support, using pieces from other GDB modules.
+
+   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/>.  */
+
+#ifndef CALL_SITE_H
+#define CALL_SITE_H
+
+#include "dwarf2/types.h"
+#include "gdbsupport/function-view.h"
+#include "frame-info.h"
+
+struct dwarf2_locexpr_baton;
+struct dwarf2_per_cu_data;
+struct dwarf2_per_objfile;
+
+/* struct call_site_parameter can be referenced in callees by several ways.  */
+
+enum call_site_parameter_kind
+{
+  /* * Use field call_site_parameter.u.dwarf_reg.  */
+  CALL_SITE_PARAMETER_DWARF_REG,
+
+  /* * Use field call_site_parameter.u.fb_offset.  */
+  CALL_SITE_PARAMETER_FB_OFFSET,
+
+  /* * Use field call_site_parameter.u.param_offset.  */
+  CALL_SITE_PARAMETER_PARAM_OFFSET
+};
+
+struct call_site_target
+{
+  /* The kind of location held by this call site target.  */
+  enum kind
+  {
+    /* An address.  */
+    PHYSADDR,
+    /* A name.  */
+    PHYSNAME,
+    /* A DWARF block.  */
+    DWARF_BLOCK,
+    /* An array of addresses.  */
+    ADDRESSES,
+  };
+
+  void set_loc_physaddr (CORE_ADDR physaddr)
+  {
+    m_loc_kind = PHYSADDR;
+    m_loc.physaddr = physaddr;
+  }
+
+  void set_loc_physname (const char *physname)
+    {
+      m_loc_kind = PHYSNAME;
+      m_loc.physname = physname;
+    }
+
+  void set_loc_dwarf_block (dwarf2_locexpr_baton *dwarf_block)
+    {
+      m_loc_kind = DWARF_BLOCK;
+      m_loc.dwarf_block = dwarf_block;
+    }
+
+  void set_loc_array (unsigned length, const CORE_ADDR *data)
+  {
+    m_loc_kind = ADDRESSES;
+    m_loc.addresses.length = length;
+    m_loc.addresses.values = data;
+  }
+
+  /* Callback type for iterate_over_addresses.  */
+
+  using iterate_ftype = gdb::function_view<void (CORE_ADDR)>;
+
+  /* Call CALLBACK for each DW_TAG_call_site's DW_AT_call_target
+     address.  CALLER_FRAME (for registers) can be NULL if it is not
+     known.  This function always may throw NO_ENTRY_VALUE_ERROR.  */
+
+  void iterate_over_addresses (struct gdbarch *call_site_gdbarch,
+			       const struct call_site *call_site,
+			       frame_info_ptr caller_frame,
+			       iterate_ftype callback) const;
+
+private:
+
+  union
+  {
+    /* Address.  */
+    CORE_ADDR physaddr;
+    /* Mangled name.  */
+    const char *physname;
+    /* DWARF block.  */
+    struct dwarf2_locexpr_baton *dwarf_block;
+    /* Array of addresses.  */
+    struct
+    {
+      unsigned length;
+      const CORE_ADDR *values;
+    } addresses;
+  } m_loc;
+
+  /* * Discriminant for union field_location.  */
+  enum kind m_loc_kind;
+};
+
+union call_site_parameter_u
+{
+  /* * DW_TAG_formal_parameter's DW_AT_location's DW_OP_regX
+     as DWARF register number, for register passed
+     parameters.  */
+
+  int dwarf_reg;
+
+  /* * Offset from the callee's frame base, for stack passed
+     parameters.  This equals offset from the caller's stack
+     pointer.  */
+
+  CORE_ADDR fb_offset;
+
+  /* * Offset relative to the start of this PER_CU to
+     DW_TAG_formal_parameter which is referenced by both
+     caller and the callee.  */
+
+  cu_offset param_cu_off;
+};
+
+struct call_site_parameter
+{
+  ENUM_BITFIELD (call_site_parameter_kind) kind : 2;
+
+  union call_site_parameter_u u;
+
+  /* * DW_TAG_formal_parameter's DW_AT_call_value.  It is never NULL.  */
+
+  const gdb_byte *value;
+  size_t value_size;
+
+  /* * DW_TAG_formal_parameter's DW_AT_call_data_value.
+     It may be NULL if not provided by DWARF.  */
+
+  const gdb_byte *data_value;
+  size_t data_value_size;
+};
+
+/* * A place where a function gets called from, represented by
+   DW_TAG_call_site.  It can be looked up from symtab->call_site_htab.  */
+
+struct call_site
+{
+  call_site (CORE_ADDR pc, dwarf2_per_cu_data *per_cu,
+	     dwarf2_per_objfile *per_objfile)
+    : per_cu (per_cu), per_objfile (per_objfile), m_unrelocated_pc (pc)
+  {}
+
+  static int
+  eq (const call_site *a, const call_site *b)
+  {
+    return a->m_unrelocated_pc == b->m_unrelocated_pc;
+  }
+
+  static hashval_t
+  hash (const call_site *a)
+  {
+    return a->m_unrelocated_pc;
+  }
+
+  static int
+  eq (const void *a, const void *b)
+  {
+    return eq ((const call_site *)a, (const call_site *)b);
+  }
+
+  static hashval_t
+  hash (const void *a)
+  {
+    return hash ((const call_site *)a);
+  }
+
+  /* Return the address of the first instruction after this call.  */
+
+  CORE_ADDR pc () const;
+
+  /* Call CALLBACK for each target address.  CALLER_FRAME (for
+     registers) can be NULL if it is not known.  This function may
+     throw NO_ENTRY_VALUE_ERROR.  */
+
+  void iterate_over_addresses (struct gdbarch *call_site_gdbarch,
+			       frame_info_ptr caller_frame,
+			       call_site_target::iterate_ftype callback)
+    const
+  {
+    return target.iterate_over_addresses (call_site_gdbarch, this,
+					  caller_frame, callback);
+  }
+
+  /* * List successor with head in FUNC_TYPE.TAIL_CALL_LIST.  */
+
+  struct call_site *tail_call_next = nullptr;
+
+  /* * Describe DW_AT_call_target.  Missing attribute uses
+     FIELD_LOC_KIND_DWARF_BLOCK with FIELD_DWARF_BLOCK == NULL.  */
+
+  struct call_site_target target {};
+
+  /* * Size of the PARAMETER array.  */
+
+  unsigned parameter_count = 0;
+
+  /* * CU of the function where the call is located.  It gets used
+     for DWARF blocks execution in the parameter array below.  */
+
+  dwarf2_per_cu_data *const per_cu = nullptr;
+
+  /* objfile of the function where the call is located.  */
+
+  dwarf2_per_objfile *const per_objfile = nullptr;
+
+private:
+  /* Unrelocated address of the first instruction after this call.  */
+  const CORE_ADDR m_unrelocated_pc;
+
+public:
+  /* * Describe DW_TAG_call_site's DW_TAG_formal_parameter.  */
+
+  struct call_site_parameter parameter[];
+};
+
+#endif /* CALL_SITE_H */
diff --git a/gdb/dwarf2/comp-unit-head.h b/gdb/dwarf2/comp-unit-head.h
index a7ee3e64546d..ba63c26d3a76 100644
--- a/gdb/dwarf2/comp-unit-head.h
+++ b/gdb/dwarf2/comp-unit-head.h
@@ -27,8 +27,11 @@
 #ifndef GDB_DWARF2_COMP_UNIT_H
 #define GDB_DWARF2_COMP_UNIT_H
 
+#include "dwarf2.h"
 #include "dwarf2/leb.h"
-#include "gdbtypes.h"
+#include "dwarf2/types.h"
+
+struct dwarf2_per_objfile;
 
 /* The data in a compilation unit header, after target2host
    translation, looks like this.  */
diff --git a/gdb/dwarf2/cooked-index.h b/gdb/dwarf2/cooked-index.h
index 2ea32781be5c..8f9d250f467b 100644
--- a/gdb/dwarf2/cooked-index.h
+++ b/gdb/dwarf2/cooked-index.h
@@ -21,7 +21,7 @@
 #define GDB_DWARF2_COOKED_INDEX_H
 
 #include "dwarf2.h"
-#include "gdbtypes.h"
+#include "dwarf2/types.h"
 #include "symtab.h"
 #include "hashtab.h"
 #include "dwarf2/index-common.h"
diff --git a/gdb/dwarf2/expr.h b/gdb/dwarf2/expr.h
index 6078dce0abc5..aa4b392bc99b 100644
--- a/gdb/dwarf2/expr.h
+++ b/gdb/dwarf2/expr.h
@@ -23,7 +23,7 @@
 #define DWARF2EXPR_H
 
 #include "leb128.h"
-#include "gdbtypes.h"
+#include "dwarf2/call-site.h"
 
 struct dwarf2_per_objfile;
 
diff --git a/gdb/dwarf2/line-header.h b/gdb/dwarf2/line-header.h
index e7a63a37918a..ac73bf2e4ede 100644
--- a/gdb/dwarf2/line-header.h
+++ b/gdb/dwarf2/line-header.h
@@ -20,8 +20,6 @@
 #ifndef DWARF2_LINE_HEADER_H
 #define DWARF2_LINE_HEADER_H
 
-#include "gdbtypes.h"
-
 /* dir_index is 1-based in DWARF 4 and before, and is 0-based in DWARF 5 and
    later.  */
 typedef int dir_index;
diff --git a/gdb/dwarf2/types.h b/gdb/dwarf2/types.h
index 463dc485925f..db026d3b6f3f 100644
--- a/gdb/dwarf2/types.h
+++ b/gdb/dwarf2/types.h
@@ -21,6 +21,7 @@
 #define DWARF2_TYPES_H
 
 #include "gdbsupport/offset-type.h"
+#include "gdbsupport/underlying.h"
 
 /* Offset relative to the start of its containing CU (compilation
    unit).  */
diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h
index fbe46323216f..d5b966dc73f1 100644
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -57,7 +57,6 @@
 #include "gdbsupport/gdb_obstack.h"
 #include "gmp-utils.h"
 #include "frame-info.h"
-#include "dwarf2/types.h"
 
 /* Forward declarations for prototypes.  */
 struct field;
@@ -1735,216 +1734,6 @@ struct func_type
     struct type *self_type;
   };
 
-/* struct call_site_parameter can be referenced in callees by several ways.  */
-
-enum call_site_parameter_kind
-{
-  /* * Use field call_site_parameter.u.dwarf_reg.  */
-  CALL_SITE_PARAMETER_DWARF_REG,
-
-  /* * Use field call_site_parameter.u.fb_offset.  */
-  CALL_SITE_PARAMETER_FB_OFFSET,
-
-  /* * Use field call_site_parameter.u.param_offset.  */
-  CALL_SITE_PARAMETER_PARAM_OFFSET
-};
-
-struct call_site_target
-{
-  /* The kind of location held by this call site target.  */
-  enum kind
-  {
-    /* An address.  */
-    PHYSADDR,
-    /* A name.  */
-    PHYSNAME,
-    /* A DWARF block.  */
-    DWARF_BLOCK,
-    /* An array of addresses.  */
-    ADDRESSES,
-  };
-
-  void set_loc_physaddr (CORE_ADDR physaddr)
-  {
-    m_loc_kind = PHYSADDR;
-    m_loc.physaddr = physaddr;
-  }
-
-  void set_loc_physname (const char *physname)
-    {
-      m_loc_kind = PHYSNAME;
-      m_loc.physname = physname;
-    }
-
-  void set_loc_dwarf_block (dwarf2_locexpr_baton *dwarf_block)
-    {
-      m_loc_kind = DWARF_BLOCK;
-      m_loc.dwarf_block = dwarf_block;
-    }
-
-  void set_loc_array (unsigned length, const CORE_ADDR *data)
-  {
-    m_loc_kind = ADDRESSES;
-    m_loc.addresses.length = length;
-    m_loc.addresses.values = data;
-  }
-
-  /* Callback type for iterate_over_addresses.  */
-
-  using iterate_ftype = gdb::function_view<void (CORE_ADDR)>;
-
-  /* Call CALLBACK for each DW_TAG_call_site's DW_AT_call_target
-     address.  CALLER_FRAME (for registers) can be NULL if it is not
-     known.  This function always may throw NO_ENTRY_VALUE_ERROR.  */
-
-  void iterate_over_addresses (struct gdbarch *call_site_gdbarch,
-			       const struct call_site *call_site,
-			       frame_info_ptr caller_frame,
-			       iterate_ftype callback) const;
-
-private:
-
-  union
-  {
-    /* Address.  */
-    CORE_ADDR physaddr;
-    /* Mangled name.  */
-    const char *physname;
-    /* DWARF block.  */
-    struct dwarf2_locexpr_baton *dwarf_block;
-    /* Array of addresses.  */
-    struct
-    {
-      unsigned length;
-      const CORE_ADDR *values;
-    } addresses;
-  } m_loc;
-
-  /* * Discriminant for union field_location.  */
-  enum kind m_loc_kind;
-};
-
-union call_site_parameter_u
-{
-  /* * DW_TAG_formal_parameter's DW_AT_location's DW_OP_regX
-     as DWARF register number, for register passed
-     parameters.  */
-
-  int dwarf_reg;
-
-  /* * Offset from the callee's frame base, for stack passed
-     parameters.  This equals offset from the caller's stack
-     pointer.  */
-
-  CORE_ADDR fb_offset;
-
-  /* * Offset relative to the start of this PER_CU to
-     DW_TAG_formal_parameter which is referenced by both
-     caller and the callee.  */
-
-  cu_offset param_cu_off;
-};
-
-struct call_site_parameter
-{
-  ENUM_BITFIELD (call_site_parameter_kind) kind : 2;
-
-  union call_site_parameter_u u;
-
-  /* * DW_TAG_formal_parameter's DW_AT_call_value.  It is never NULL.  */
-
-  const gdb_byte *value;
-  size_t value_size;
-
-  /* * DW_TAG_formal_parameter's DW_AT_call_data_value.
-     It may be NULL if not provided by DWARF.  */
-
-  const gdb_byte *data_value;
-  size_t data_value_size;
-};
-
-/* * A place where a function gets called from, represented by
-   DW_TAG_call_site.  It can be looked up from symtab->call_site_htab.  */
-
-struct call_site
-  {
-    call_site (CORE_ADDR pc, dwarf2_per_cu_data *per_cu,
-	       dwarf2_per_objfile *per_objfile)
-      : per_cu (per_cu), per_objfile (per_objfile), m_unrelocated_pc (pc)
-    {}
-
-    static int
-    eq (const call_site *a, const call_site *b)
-    {
-      return a->m_unrelocated_pc == b->m_unrelocated_pc;
-    }
-
-    static hashval_t
-    hash (const call_site *a)
-    {
-      return a->m_unrelocated_pc;
-    }
-
-    static int
-    eq (const void *a, const void *b)
-    {
-      return eq ((const call_site *)a, (const call_site *)b);
-    }
-
-    static hashval_t
-    hash (const void *a)
-    {
-      return hash ((const call_site *)a);
-    }
-
-    /* Return the address of the first instruction after this call.  */
-
-    CORE_ADDR pc () const;
-
-    /* Call CALLBACK for each target address.  CALLER_FRAME (for
-       registers) can be NULL if it is not known.  This function may
-       throw NO_ENTRY_VALUE_ERROR.  */
-
-    void iterate_over_addresses (struct gdbarch *call_site_gdbarch,
-				 frame_info_ptr caller_frame,
-				 call_site_target::iterate_ftype callback)
-      const
-    {
-      return target.iterate_over_addresses (call_site_gdbarch, this,
-					    caller_frame, callback);
-    }
-
-    /* * List successor with head in FUNC_TYPE.TAIL_CALL_LIST.  */
-
-    struct call_site *tail_call_next = nullptr;
-
-    /* * Describe DW_AT_call_target.  Missing attribute uses
-       FIELD_LOC_KIND_DWARF_BLOCK with FIELD_DWARF_BLOCK == NULL.  */
-
-    struct call_site_target target {};
-
-    /* * Size of the PARAMETER array.  */
-
-    unsigned parameter_count = 0;
-
-    /* * CU of the function where the call is located.  It gets used
-       for DWARF blocks execution in the parameter array below.  */
-
-    dwarf2_per_cu_data *const per_cu = nullptr;
-
-    /* objfile of the function where the call is located.  */
-
-    dwarf2_per_objfile *const per_objfile = nullptr;
-
-  private:
-    /* Unrelocated address of the first instruction after this call.  */
-    const CORE_ADDR m_unrelocated_pc;
-
-  public:
-    /* * Describe DW_TAG_call_site's DW_TAG_formal_parameter.  */
-
-    struct call_site_parameter parameter[];
-  };
 
 /* The type-specific info for TYPE_CODE_FIXED_POINT types.  */
 
diff --git a/gdb/symtab.c b/gdb/symtab.c
index 0d342f765f2f..57102e2c085a 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -18,6 +18,7 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "defs.h"
+#include "dwarf2/call-site.h"
 #include "symtab.h"
 #include "gdbtypes.h"
 #include "gdbcore.h"
-- 
2.38.1


  parent reply	other threads:[~2022-12-14  3:41 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-12-14  3:34 [PATCH v2 00/13] Make frame_info_ptr automatic Simon Marchi
2022-12-14  3:34 ` [PATCH v2 01/13] gdb: move type_map_instance to compile/compile.c Simon Marchi
2022-12-14  3:34 ` [PATCH v2 02/13] gdb: move compile_instance to compile/compile.h Simon Marchi
2022-12-14  3:34 ` [PATCH v2 03/13] gdb: remove language.h include from frame.h Simon Marchi
2022-12-14  3:34 ` [PATCH v2 04/13] gdb: move sect_offset and cu_offset to dwarf2/types.h Simon Marchi
2022-12-14  3:34 ` Simon Marchi [this message]
2022-12-14  3:34 ` [PATCH v2 06/13] gdb: move frame_info_ptr to frame.{c,h} Simon Marchi
2022-12-20 17:01   ` Bruno Larsen
2023-01-03 18:59     ` Simon Marchi
2022-12-14  3:34 ` [PATCH v2 07/13] gdb: add frame_id::user_created_p Simon Marchi
2022-12-14  3:34 ` [PATCH v2 08/13] gdb: add user-created frames to stash Simon Marchi
2022-12-14  3:34 ` [PATCH v2 09/13] gdb: add create_new_frame(frame_id) overload Simon Marchi
2022-12-14  3:34 ` [PATCH v2 10/13] gdb: make it possible to restore selected user-created frames Simon Marchi
2022-12-14  3:34 ` [PATCH v2 11/13] gdb: make user-created frames reinflatable Simon Marchi
2023-01-23 12:57   ` Tom de Vries
2023-01-23 14:34     ` Luis Machado
2023-01-24  3:55       ` Simon Marchi
2023-01-24  8:22         ` Luis Machado
2023-01-25  3:45           ` Simon Marchi
2023-01-30  8:49             ` Luis Machado
2023-01-30 16:20               ` Simon Marchi
2022-12-14  3:34 ` [PATCH v2 12/13] gdb: make frame_info_ptr grab frame level and id on construction Simon Marchi
2022-12-14  3:34 ` [PATCH v2 13/13] gdb: make frame_info_ptr auto-reinflatable Simon Marchi
2022-12-20 16:57 ` [PATCH v2 00/13] Make frame_info_ptr automatic Bruno Larsen
2023-01-03 19:00   ` Simon Marchi
2023-01-03 19:09 ` Simon Marchi
2023-01-18 18:10 ` Tom Tromey
2023-01-19  3:40   ` Simon Marchi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20221214033441.499512-6-simon.marchi@polymtl.ca \
    --to=simon.marchi@polymtl.ca \
    --cc=gdb-patches@sourceware.org \
    --cc=simon.marchi@efficios.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).