[5/6] Add a new gdbarch method to print a single AUXV entry.

Message ID 20160616060202.63470-6-jhb@FreeBSD.org
State New, archived
Headers

Commit Message

John Baldwin June 16, 2016, 6:02 a.m. UTC
  Different platforms have different meanings for auxiliary vector
entries.  The 'print_auxv' gdbarch method allows an architecture
to output a suitable description for platform-specific entries.

A fprint_single_auxv function is split out of fprint_target_auxv.
This function outputs the description of a single auxiliary vector
entry to the specified file using caller-supplied formatting and
strings to describe the vector type.

The existing switch on auxiliary vector types is moved out of
fprint_target_auxv into a new default_print_auxv function.
default_print_auxv chooses an appropriate format and description
and calls fprint_single_auxv to describe a single vector entry.

fprint_target_auxv now invokes the gdbarch 'print_auxv' function
on each vector entry.  If the function is not present or returns
zero, default_printf_auxv is called to output a description for
the vector.

gdb/ChangeLog:

	* auxv.c (fprint_single_auxv): New function.
	(default_print_auxv): New function.
	(fprint_target_auxv): Use gdbarch_print_auxv and default_print_auxv.
	* auxv.h (enum auxv_format): New enum.
	(fprint_single_auxv): Declare.
	* gdbarch.sh (print_auxv): New.
	* gdbarch.c, gdbarch.h: Re-generated.
---
 gdb/ChangeLog  |  10 +++
 gdb/auxv.c     | 191 +++++++++++++++++++++++++++++++--------------------------
 gdb/auxv.h     |   8 +++
 gdb/gdbarch.c  |  32 ++++++++++
 gdb/gdbarch.h  |  11 ++++
 gdb/gdbarch.sh |   6 ++
 6 files changed, 172 insertions(+), 86 deletions(-)
  

Comments

Pedro Alves June 20, 2016, 11:40 p.m. UTC | #1
On 06/16/2016 07:02 AM, John Baldwin wrote:
> Different platforms have different meanings for auxiliary vector
> entries.  The 'print_auxv' gdbarch method allows an architecture
> to output a suitable description for platform-specific entries.
> 
> A fprint_single_auxv function is split out of fprint_target_auxv.
> This function outputs the description of a single auxiliary vector
> entry to the specified file using caller-supplied formatting and
> strings to describe the vector type.
> 
> The existing switch on auxiliary vector types is moved out of
> fprint_target_auxv into a new default_print_auxv function.
> default_print_auxv chooses an appropriate format and description
> and calls fprint_single_auxv to describe a single vector entry.
> 
> fprint_target_auxv now invokes the gdbarch 'print_auxv' function
> on each vector entry.  If the function is not present or returns
> zero, default_printf_auxv is called to output a description for
> the vector.

I like the idea.  Though, I think we can simplify this.  How about:

- make default_print_auxv be the default gdbarch_print_auxv
  implementation, in gdbarch.sh.   

- make fprint_target_auxv calls gdbarch_print_auxv unconditionally.

- remove the support for gdbarch_print_auxv returning 0.  Instead,
  implementations that want to defer to default_print_auxv simply
  call it directly.


Also, I think it'd be a bit less confusing to rename things like this:

  gdbarch_print_auxv -> gdbarch_print_auxv_entry
  default_print_auxv -> default_print_auxv_entry
  fprint_single_auxv -> fprint_auxv_entry

This way methods that print a single entry are consistently named,
and not so easily confused with methods that print the whole table,
like fprint_target_auxv.

>  
>  
> +/* Print a description of a single AUXV entry on the specified file.  */
> +void

Empty line between comment and function.

> +fprint_single_auxv (struct ui_file *file, const char *name,
> +		    const char *description, enum auxv_format flavor,
> +		    CORE_ADDR type, CORE_ADDR val)
> +{
> +  fprintf_filtered (file, "%-4s %-20s %-30s ",
> +		    plongest (type), name, description);

If not translatable, wrap string in () [not _()], in order to
avoid ARI complaints.

> +  switch (flavor)
> +    {
> +    case dec:
> +      fprintf_filtered (file, "%s\n", plongest (val));
> +      break;
> +    case hex:
> +      fprintf_filtered (file, "%s\n", paddress (target_gdbarch (), val));
> +      break;
> +    case str:
> +      {
> +	struct value_print_options opts;
> +
> +	get_user_print_options (&opts);
> +	if (opts.addressprint)
> +	  fprintf_filtered (file, "%s ", paddress (target_gdbarch (), val));
> +	val_print_string (builtin_type (target_gdbarch ())->builtin_char,
> +			  NULL, val, -1, file, &opts);
> +	fprintf_filtered (file, "\n");
> +      }

Likewise the other format strings.


> +
> +static void
> +default_print_auxv (struct ui_file *file, CORE_ADDR type, CORE_ADDR val)
> +{

Intro comment.   Something like "default implementation of ...".


> +		    
>  /* Print the contents of the target's AUXV on the specified file.  */
>  int
>  fprint_target_auxv (struct ui_file *file, struct target_ops *ops)
>  {
> +  struct gdbarch *gdbarch = target_gdbarch();

Space before open parenthesis.

> diff --git a/gdb/auxv.h b/gdb/auxv.h
> index 9efe604..91d94f9 100644
> --- a/gdb/auxv.h
> +++ b/gdb/auxv.h
> @@ -46,6 +46,14 @@ extern int target_auxv_parse (struct target_ops *ops,
>  extern int target_auxv_search (struct target_ops *ops,
>  			       CORE_ADDR match, CORE_ADDR *valp);
>  
> +/* Print a description of a single AUXV entry on the specified file.  */
> +enum auxv_format { dec, hex, str };

These very generic names used to be OK, because they were
defined inside the function.  But now that this is in a header,
the potential for conflict is much higher.  hex could conflict
with std::hex, for instance.  Please rename the values to
for example: AUXV_FORMAT_DEC / AUXV_FORMAT_HEX / AUXV_FORMAT_STR.
We should add a comment for what each value means, too, since
we lost the pass of obvious-because-of-locality.

> +
> +extern void fprint_single_auxv (struct ui_file *file, const char *name,
> +				const char *description,
> +				enum auxv_format flavor, CORE_ADDR type,

I'd find it more consistent to call parameter "format" instead of "flavor",
which then avoids people looking for enum auxv_flavor.

> +# Print the description of a single auxv entry described by TYPE and VAL
> +# to FILE.  Return 0 and output no description if the TYPE is unknown.
> +# Return 0 if the TYPE of the auxv entry is unknown.
> +# Return 1 if a description was output.
> +M:int:print_auxv:struct ui_file *file, CORE_ADDR type, CORE_ADDR val:file, type, val

Thanks,
Pedro Alves
  
John Baldwin June 23, 2016, 8:19 p.m. UTC | #2
On Tuesday, June 21, 2016 12:40:16 AM Pedro Alves wrote:
> On 06/16/2016 07:02 AM, John Baldwin wrote:
> > Different platforms have different meanings for auxiliary vector
> > entries.  The 'print_auxv' gdbarch method allows an architecture
> > to output a suitable description for platform-specific entries.
> > 
> > A fprint_single_auxv function is split out of fprint_target_auxv.
> > This function outputs the description of a single auxiliary vector
> > entry to the specified file using caller-supplied formatting and
> > strings to describe the vector type.
> > 
> > The existing switch on auxiliary vector types is moved out of
> > fprint_target_auxv into a new default_print_auxv function.
> > default_print_auxv chooses an appropriate format and description
> > and calls fprint_single_auxv to describe a single vector entry.
> > 
> > fprint_target_auxv now invokes the gdbarch 'print_auxv' function
> > on each vector entry.  If the function is not present or returns
> > zero, default_printf_auxv is called to output a description for
> > the vector.
> 
> I like the idea.  Though, I think we can simplify this.  How about:
> 
> - make default_print_auxv be the default gdbarch_print_auxv
>   implementation, in gdbarch.sh.   
> 
> - make fprint_target_auxv calls gdbarch_print_auxv unconditionally.
> 
> - remove the support for gdbarch_print_auxv returning 0.  Instead,
>   implementations that want to defer to default_print_auxv simply
>   call it directly.
> 
> 
> Also, I think it'd be a bit less confusing to rename things like this:
> 
>   gdbarch_print_auxv -> gdbarch_print_auxv_entry
>   default_print_auxv -> default_print_auxv_entry
>   fprint_single_auxv -> fprint_auxv_entry
> 
> This way methods that print a single entry are consistently named,
> and not so easily confused with methods that print the whole table,
> like fprint_target_auxv.

Agreed, thanks.  I've made these changes along with the auxv_format
enum rename and the other issues you've pointed out.  Will be posting
a v2 series with those fixes in a bit.
  

Patch

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index b11174c..2426a2f 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,15 @@ 
 2016-06-15  John Baldwin  <jhb@FreeBSD.org>
 
+	* auxv.c (fprint_single_auxv): New function.
+	(default_print_auxv): New function.
+	(fprint_target_auxv): Use gdbarch_print_auxv and default_print_auxv.
+	* auxv.h (enum auxv_format): New enum.
+	(fprint_single_auxv): Declare.
+	* gdbarch.sh (print_auxv): New.
+	* gdbarch.c, gdbarch.h: Re-generated.
+
+2016-06-15  John Baldwin  <jhb@FreeBSD.org>
+
 	* fbsd-nat.c [KERN_PROC_AUXV] New variable super_xfer_partial.
 	(fbsd_xfer_partial): New function.
 	(fbsd_nat_add_target) [KERN_PROC_AUXV] Set "to_xfer_partial" to
diff --git a/gdb/auxv.c b/gdb/auxv.c
index 396862e..275c23e 100644
--- a/gdb/auxv.c
+++ b/gdb/auxv.c
@@ -407,10 +407,112 @@  target_auxv_search (struct target_ops *ops, CORE_ADDR match, CORE_ADDR *valp)
 }
 
 
+/* Print a description of a single AUXV entry on the specified file.  */
+void
+fprint_single_auxv (struct ui_file *file, const char *name,
+		    const char *description, enum auxv_format flavor,
+		    CORE_ADDR type, CORE_ADDR val)
+{
+  fprintf_filtered (file, "%-4s %-20s %-30s ",
+		    plongest (type), name, description);
+  switch (flavor)
+    {
+    case dec:
+      fprintf_filtered (file, "%s\n", plongest (val));
+      break;
+    case hex:
+      fprintf_filtered (file, "%s\n", paddress (target_gdbarch (), val));
+      break;
+    case str:
+      {
+	struct value_print_options opts;
+
+	get_user_print_options (&opts);
+	if (opts.addressprint)
+	  fprintf_filtered (file, "%s ", paddress (target_gdbarch (), val));
+	val_print_string (builtin_type (target_gdbarch ())->builtin_char,
+			  NULL, val, -1, file, &opts);
+	fprintf_filtered (file, "\n");
+      }
+      break;
+    }
+}
+
+static void
+default_print_auxv (struct ui_file *file, CORE_ADDR type, CORE_ADDR val)
+{
+  const char *name = "???";
+  const char *description = "";
+  enum auxv_format flavor = hex;
+
+  switch (type)
+    {
+#define TAG(tag, text, kind) \
+      case tag: name = #tag; description = text; flavor = kind; break
+      TAG (AT_NULL, _("End of vector"), hex);
+      TAG (AT_IGNORE, _("Entry should be ignored"), hex);
+      TAG (AT_EXECFD, _("File descriptor of program"), dec);
+      TAG (AT_PHDR, _("Program headers for program"), hex);
+      TAG (AT_PHENT, _("Size of program header entry"), dec);
+      TAG (AT_PHNUM, _("Number of program headers"), dec);
+      TAG (AT_PAGESZ, _("System page size"), dec);
+      TAG (AT_BASE, _("Base address of interpreter"), hex);
+      TAG (AT_FLAGS, _("Flags"), hex);
+      TAG (AT_ENTRY, _("Entry point of program"), hex);
+      TAG (AT_NOTELF, _("Program is not ELF"), dec);
+      TAG (AT_UID, _("Real user ID"), dec);
+      TAG (AT_EUID, _("Effective user ID"), dec);
+      TAG (AT_GID, _("Real group ID"), dec);
+      TAG (AT_EGID, _("Effective group ID"), dec);
+      TAG (AT_CLKTCK, _("Frequency of times()"), dec);
+      TAG (AT_PLATFORM, _("String identifying platform"), str);
+      TAG (AT_HWCAP, _("Machine-dependent CPU capability hints"), hex);
+      TAG (AT_FPUCW, _("Used FPU control word"), dec);
+      TAG (AT_DCACHEBSIZE, _("Data cache block size"), dec);
+      TAG (AT_ICACHEBSIZE, _("Instruction cache block size"), dec);
+      TAG (AT_UCACHEBSIZE, _("Unified cache block size"), dec);
+      TAG (AT_IGNOREPPC, _("Entry should be ignored"), dec);
+      TAG (AT_BASE_PLATFORM, _("String identifying base platform"), str);
+      TAG (AT_RANDOM, _("Address of 16 random bytes"), hex);
+      TAG (AT_HWCAP2, _("Extension of AT_HWCAP"), hex);
+      TAG (AT_EXECFN, _("File name of executable"), str);
+      TAG (AT_SECURE, _("Boolean, was exec setuid-like?"), dec);
+      TAG (AT_SYSINFO, _("Special system info/entry points"), hex);
+      TAG (AT_SYSINFO_EHDR, _("System-supplied DSO's ELF header"), hex);
+      TAG (AT_L1I_CACHESHAPE, _("L1 Instruction cache information"), hex);
+      TAG (AT_L1D_CACHESHAPE, _("L1 Data cache information"), hex);
+      TAG (AT_L2_CACHESHAPE, _("L2 cache information"), hex);
+      TAG (AT_L3_CACHESHAPE, _("L3 cache information"), hex);
+      TAG (AT_SUN_UID, _("Effective user ID"), dec);
+      TAG (AT_SUN_RUID, _("Real user ID"), dec);
+      TAG (AT_SUN_GID, _("Effective group ID"), dec);
+      TAG (AT_SUN_RGID, _("Real group ID"), dec);
+      TAG (AT_SUN_LDELF, _("Dynamic linker's ELF header"), hex);
+      TAG (AT_SUN_LDSHDR, _("Dynamic linker's section headers"), hex);
+      TAG (AT_SUN_LDNAME, _("String giving name of dynamic linker"), str);
+      TAG (AT_SUN_LPAGESZ, _("Large pagesize"), dec);
+      TAG (AT_SUN_PLATFORM, _("Platform name string"), str);
+      TAG (AT_SUN_HWCAP, _("Machine-dependent CPU capability hints"), hex);
+      TAG (AT_SUN_IFLUSH, _("Should flush icache?"), dec);
+      TAG (AT_SUN_CPU, _("CPU name string"), str);
+      TAG (AT_SUN_EMUL_ENTRY, _("COFF entry point address"), hex);
+      TAG (AT_SUN_EMUL_EXECFD, _("COFF executable file descriptor"), dec);
+      TAG (AT_SUN_EXECNAME,
+	   _("Canonicalized file name given to execve"), str);
+      TAG (AT_SUN_MMU, _("String for name of MMU module"), str);
+      TAG (AT_SUN_LDDATA, _("Dynamic linker's data segment address"), hex);
+      TAG (AT_SUN_AUXFLAGS,
+	   _("AF_SUN_ flags passed from the kernel"), hex);
+    }
+
+  fprint_single_auxv (file, name, description, flavor, type, val);
+}
+		    
 /* Print the contents of the target's AUXV on the specified file.  */
 int
 fprint_target_auxv (struct ui_file *file, struct target_ops *ops)
 {
+  struct gdbarch *gdbarch = target_gdbarch();
   CORE_ADDR type, val;
   gdb_byte *data;
   gdb_byte *ptr;
@@ -426,93 +528,10 @@  fprint_target_auxv (struct ui_file *file, struct target_ops *ops)
 
   while (target_auxv_parse (ops, &ptr, data + info->length, &type, &val) > 0)
     {
-      const char *name = "???";
-      const char *description = "";
-      enum { dec, hex, str } flavor = hex;
-
-      switch (type)
-	{
-#define TAG(tag, text, kind) \
-	case tag: name = #tag; description = text; flavor = kind; break
-	  TAG (AT_NULL, _("End of vector"), hex);
-	  TAG (AT_IGNORE, _("Entry should be ignored"), hex);
-	  TAG (AT_EXECFD, _("File descriptor of program"), dec);
-	  TAG (AT_PHDR, _("Program headers for program"), hex);
-	  TAG (AT_PHENT, _("Size of program header entry"), dec);
-	  TAG (AT_PHNUM, _("Number of program headers"), dec);
-	  TAG (AT_PAGESZ, _("System page size"), dec);
-	  TAG (AT_BASE, _("Base address of interpreter"), hex);
-	  TAG (AT_FLAGS, _("Flags"), hex);
-	  TAG (AT_ENTRY, _("Entry point of program"), hex);
-	  TAG (AT_NOTELF, _("Program is not ELF"), dec);
-	  TAG (AT_UID, _("Real user ID"), dec);
-	  TAG (AT_EUID, _("Effective user ID"), dec);
-	  TAG (AT_GID, _("Real group ID"), dec);
-	  TAG (AT_EGID, _("Effective group ID"), dec);
-	  TAG (AT_CLKTCK, _("Frequency of times()"), dec);
-	  TAG (AT_PLATFORM, _("String identifying platform"), str);
-	  TAG (AT_HWCAP, _("Machine-dependent CPU capability hints"), hex);
-	  TAG (AT_FPUCW, _("Used FPU control word"), dec);
-	  TAG (AT_DCACHEBSIZE, _("Data cache block size"), dec);
-	  TAG (AT_ICACHEBSIZE, _("Instruction cache block size"), dec);
-	  TAG (AT_UCACHEBSIZE, _("Unified cache block size"), dec);
-	  TAG (AT_IGNOREPPC, _("Entry should be ignored"), dec);
-	  TAG (AT_BASE_PLATFORM, _("String identifying base platform"), str);
-	  TAG (AT_RANDOM, _("Address of 16 random bytes"), hex);
-	  TAG (AT_HWCAP2, _("Extension of AT_HWCAP"), hex);
-	  TAG (AT_EXECFN, _("File name of executable"), str);
-	  TAG (AT_SECURE, _("Boolean, was exec setuid-like?"), dec);
-	  TAG (AT_SYSINFO, _("Special system info/entry points"), hex);
-	  TAG (AT_SYSINFO_EHDR, _("System-supplied DSO's ELF header"), hex);
-	  TAG (AT_L1I_CACHESHAPE, _("L1 Instruction cache information"), hex);
-	  TAG (AT_L1D_CACHESHAPE, _("L1 Data cache information"), hex);
-	  TAG (AT_L2_CACHESHAPE, _("L2 cache information"), hex);
-	  TAG (AT_L3_CACHESHAPE, _("L3 cache information"), hex);
-	  TAG (AT_SUN_UID, _("Effective user ID"), dec);
-	  TAG (AT_SUN_RUID, _("Real user ID"), dec);
-	  TAG (AT_SUN_GID, _("Effective group ID"), dec);
-	  TAG (AT_SUN_RGID, _("Real group ID"), dec);
-	  TAG (AT_SUN_LDELF, _("Dynamic linker's ELF header"), hex);
-	  TAG (AT_SUN_LDSHDR, _("Dynamic linker's section headers"), hex);
-	  TAG (AT_SUN_LDNAME, _("String giving name of dynamic linker"), str);
-	  TAG (AT_SUN_LPAGESZ, _("Large pagesize"), dec);
-	  TAG (AT_SUN_PLATFORM, _("Platform name string"), str);
-	  TAG (AT_SUN_HWCAP, _("Machine-dependent CPU capability hints"), hex);
-	  TAG (AT_SUN_IFLUSH, _("Should flush icache?"), dec);
-	  TAG (AT_SUN_CPU, _("CPU name string"), str);
-	  TAG (AT_SUN_EMUL_ENTRY, _("COFF entry point address"), hex);
-	  TAG (AT_SUN_EMUL_EXECFD, _("COFF executable file descriptor"), dec);
-	  TAG (AT_SUN_EXECNAME,
-	       _("Canonicalized file name given to execve"), str);
-	  TAG (AT_SUN_MMU, _("String for name of MMU module"), str);
-	  TAG (AT_SUN_LDDATA, _("Dynamic linker's data segment address"), hex);
-	  TAG (AT_SUN_AUXFLAGS,
-	       _("AF_SUN_ flags passed from the kernel"), hex);
-	}
+      if (!gdbarch_print_auxv_p (gdbarch)
+	  || gdbarch_print_auxv (gdbarch, file, type, val) == 0)
+	default_print_auxv (file, type, val);
 
-      fprintf_filtered (file, "%-4s %-20s %-30s ",
-			plongest (type), name, description);
-      switch (flavor)
-	{
-	case dec:
-	  fprintf_filtered (file, "%s\n", plongest (val));
-	  break;
-	case hex:
-	  fprintf_filtered (file, "%s\n", paddress (target_gdbarch (), val));
-	  break;
-	case str:
-	  {
-	    struct value_print_options opts;
-
-	    get_user_print_options (&opts);
-	    if (opts.addressprint)
-	      fprintf_filtered (file, "%s ", paddress (target_gdbarch (), val));
-	    val_print_string (builtin_type (target_gdbarch ())->builtin_char,
-			      NULL, val, -1, file, &opts);
-	    fprintf_filtered (file, "\n");
-	  }
-	  break;
-	}
       ++ents;
       if (type == AT_NULL)
 	break;
diff --git a/gdb/auxv.h b/gdb/auxv.h
index 9efe604..91d94f9 100644
--- a/gdb/auxv.h
+++ b/gdb/auxv.h
@@ -46,6 +46,14 @@  extern int target_auxv_parse (struct target_ops *ops,
 extern int target_auxv_search (struct target_ops *ops,
 			       CORE_ADDR match, CORE_ADDR *valp);
 
+/* Print a description of a single AUXV entry on the specified file.  */
+enum auxv_format { dec, hex, str };
+
+extern void fprint_single_auxv (struct ui_file *file, const char *name,
+				const char *description,
+				enum auxv_format flavor, CORE_ADDR type,
+				CORE_ADDR val);
+
 /* Print the contents of the target's AUXV on the specified file.  */
 extern int fprint_target_auxv (struct ui_file *file, struct target_ops *ops);
 
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index 313502b..15a4a98 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -328,6 +328,7 @@  struct gdbarch
   gdbarch_insn_is_ret_ftype *insn_is_ret;
   gdbarch_insn_is_jump_ftype *insn_is_jump;
   gdbarch_auxv_parse_ftype *auxv_parse;
+  gdbarch_print_auxv_ftype *print_auxv;
   gdbarch_vsyscall_range_ftype *vsyscall_range;
   gdbarch_infcall_mmap_ftype *infcall_mmap;
   gdbarch_infcall_munmap_ftype *infcall_munmap;
@@ -678,6 +679,7 @@  verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of insn_is_ret, invalid_p == 0 */
   /* Skip verify of insn_is_jump, invalid_p == 0 */
   /* Skip verify of auxv_parse, has predicate.  */
+  /* Skip verify of print_auxv, has predicate.  */
   /* Skip verify of vsyscall_range, invalid_p == 0 */
   /* Skip verify of infcall_mmap, invalid_p == 0 */
   /* Skip verify of infcall_munmap, invalid_p == 0 */
@@ -1167,6 +1169,12 @@  gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
                       "gdbarch_dump: pointer_to_address = <%s>\n",
                       host_address_to_string (gdbarch->pointer_to_address));
   fprintf_unfiltered (file,
+                      "gdbarch_dump: gdbarch_print_auxv_p() = %d\n",
+                      gdbarch_print_auxv_p (gdbarch));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: print_auxv = <%s>\n",
+                      host_address_to_string (gdbarch->print_auxv));
+  fprintf_unfiltered (file,
                       "gdbarch_dump: print_float_info = <%s>\n",
                       host_address_to_string (gdbarch->print_float_info));
   fprintf_unfiltered (file,
@@ -4770,6 +4778,30 @@  set_gdbarch_auxv_parse (struct gdbarch *gdbarch,
 }
 
 int
+gdbarch_print_auxv_p (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  return gdbarch->print_auxv != NULL;
+}
+
+int
+gdbarch_print_auxv (struct gdbarch *gdbarch, struct ui_file *file, CORE_ADDR type, CORE_ADDR val)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->print_auxv != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_print_auxv called\n");
+  return gdbarch->print_auxv (gdbarch, file, type, val);
+}
+
+void
+set_gdbarch_print_auxv (struct gdbarch *gdbarch,
+                        gdbarch_print_auxv_ftype print_auxv)
+{
+  gdbarch->print_auxv = print_auxv;
+}
+
+int
 gdbarch_vsyscall_range (struct gdbarch *gdbarch, struct mem_range *range)
 {
   gdb_assert (gdbarch != NULL);
diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h
index a6366fc..9cd1175 100644
--- a/gdb/gdbarch.h
+++ b/gdb/gdbarch.h
@@ -1464,6 +1464,17 @@  typedef int (gdbarch_auxv_parse_ftype) (struct gdbarch *gdbarch, gdb_byte **read
 extern int gdbarch_auxv_parse (struct gdbarch *gdbarch, gdb_byte **readptr, gdb_byte *endptr, CORE_ADDR *typep, CORE_ADDR *valp);
 extern void set_gdbarch_auxv_parse (struct gdbarch *gdbarch, gdbarch_auxv_parse_ftype *auxv_parse);
 
+/* Print the description of a single auxv entry described by TYPE and VAL
+   to FILE.  Return 0 and output no description if the TYPE is unknown.
+   Return 0 if the TYPE of the auxv entry is unknown.
+   Return 1 if a description was output. */
+
+extern int gdbarch_print_auxv_p (struct gdbarch *gdbarch);
+
+typedef int (gdbarch_print_auxv_ftype) (struct gdbarch *gdbarch, struct ui_file *file, CORE_ADDR type, CORE_ADDR val);
+extern int gdbarch_print_auxv (struct gdbarch *gdbarch, struct ui_file *file, CORE_ADDR type, CORE_ADDR val);
+extern void set_gdbarch_print_auxv (struct gdbarch *gdbarch, gdbarch_print_auxv_ftype *print_auxv);
+
 /* Find the address range of the current inferior's vsyscall/vDSO, and
    write it to *RANGE.  If the vsyscall's length can't be determined, a
    range with zero length is returned.  Returns true if the vsyscall is
diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh
index f170c10..1b4342f 100755
--- a/gdb/gdbarch.sh
+++ b/gdb/gdbarch.sh
@@ -1110,6 +1110,12 @@  m:int:insn_is_jump:CORE_ADDR addr:addr::default_insn_is_jump::0
 # Return 1 if an entry was read into *TYPEP and *VALP.
 M:int:auxv_parse:gdb_byte **readptr, gdb_byte *endptr, CORE_ADDR *typep, CORE_ADDR *valp:readptr, endptr, typep, valp
 
+# Print the description of a single auxv entry described by TYPE and VAL
+# to FILE.  Return 0 and output no description if the TYPE is unknown.
+# Return 0 if the TYPE of the auxv entry is unknown.
+# Return 1 if a description was output.
+M:int:print_auxv:struct ui_file *file, CORE_ADDR type, CORE_ADDR val:file, type, val
+
 # Find the address range of the current inferior's vsyscall/vDSO, and
 # write it to *RANGE.  If the vsyscall's length can't be determined, a
 # range with zero length is returned.  Returns true if the vsyscall is