]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Teach gdbserver about 32-byte auxv entries for the PCuABI
authorLuis Machado <luis.machado@arm.com>
Tue, 17 May 2022 08:55:39 +0000 (09:55 +0100)
committerJohn Baldwin <jhb@FreeBSD.org>
Thu, 1 Sep 2022 22:59:25 +0000 (15:59 -0700)
In order for GDBServer to work correctly with the PCuABI, it needs to
understand that there are auxv entries of 16 bytes and 32 bytes.

gdbserver/linux-aarch64-low.cc
gdbserver/linux-low.cc
gdbserver/linux-low.h
gdbserver/linux-ppc-low.cc
gdbserver/linux-s390-low.cc

index f1708d0c06fe07b9ed57c659011bfc24c329139c..cf36cbe71abb2fcfc0d11332a3cd1e7c03e11c66 100644 (file)
@@ -103,6 +103,10 @@ public:
                        unsigned const char *writebuf,
                        CORE_ADDR offset, int len) override;
 
+  /* AArch64 (Morello) implementation of auxv_search.  We need this
+     override to handle Morello 16-byte AUXV entries in the PCuABI.  */
+  bool auxv_search (CORE_ADDR type, CORE_ADDR &value) override;
+
 protected:
 
   void low_arch_setup () override;
@@ -937,12 +941,12 @@ aarch64_target::low_arch_setup ()
 
       features.vq = aarch64_sve_get_vq (tid);
       /* A-profile PAC is 64-bit only.  */
-      features.pauth = linux_get_hwcap (8) & AARCH64_HWCAP_PACA;
+      features.pauth = linux_get_hwcap () & AARCH64_HWCAP_PACA;
       /* A-profile MTE is 64-bit only.  */
-      features.mte = linux_get_hwcap2 (8) & HWCAP2_MTE;
+      features.mte = linux_get_hwcap2 () & HWCAP2_MTE;
       features.tls = true;
       /* Morello is 64-bit only.  */
-      features.capability = linux_get_hwcap2 (8) & HWCAP2_MORELLO;
+      features.capability = linux_get_hwcap2 () & HWCAP2_MORELLO;
 
       current_process ()->tdesc = aarch64_linux_read_description (features);
 
@@ -1041,7 +1045,7 @@ aarch64_target::low_fetch_linkmap_offsets (int is_elf64)
 {
   if (is_elf64)
     {
-      CORE_ADDR entry_addr = linux_get_at_entry (8);
+      CORE_ADDR entry_addr = linux_get_at_entry ();
 
       /* If the LSB of AT_ENTRY is 1, then we have a pure capability Morello
         ELF.  */
@@ -1052,6 +1056,73 @@ aarch64_target::low_fetch_linkmap_offsets (int is_elf64)
   return linux_process_target::low_fetch_linkmap_offsets (is_elf64);
 }
 
+bool
+aarch64_target::auxv_search (CORE_ADDR type, CORE_ADDR &value)
+{
+  const gdb::byte_vector auxv = linux_process_target::get_auxv ();
+  const gdb_byte *ptr = auxv.data ();
+  const gdb_byte *end_ptr = auxv.data () + auxv.size ();
+
+  if (ptr == end_ptr)
+    return false;
+
+  /* There needs to be at least one auxv entry.  */
+  gdb_assert (end_ptr - ptr >= 16);
+
+  /* We're dealing with three different AUXV layouts:
+
+     A - The regular AArch64 format: Each type entry is 64-bit and each value
+        is 64-bit.  This is also the case for Morello Hybrid binaries.
+     B - The Morello pure capability format with libshim: This is a compability
+        layout and it keeps the 64-bit types and 64-bit values.
+     C - The Morello pure capability format without libshim: This layout has
+        64-bit types followed by 64-bit padding.  The value is 128-bit.
+
+     We need to determine what layout we have, so we can read the data
+     correctly.
+
+     The easiest way to tell the difference is to assume 8-byte entries and
+     look for any types outside the range [AT_NULL, AT_MINSIGSTKSZ].  If we
+     find one such type, assume that we have layout C.  Otherwise we have
+     layouts A or B.  */
+
+  bool layout_c = false;
+  const gdb_byte *p = ptr;
+  while (p < end_ptr)
+    {
+      CORE_ADDR *entry_type = (CORE_ADDR *) p;
+
+      if (*entry_type > AT_MINSIGSTKSZ)
+       {
+         layout_c = true;
+         break;
+       }
+      p += 16;
+    }
+
+  /* Do the actual search now that we know the auxv format.  */
+  if (layout_c)
+    {
+      p = ptr;
+      while (p < end_ptr)
+       {
+         CORE_ADDR *entry_type = (CORE_ADDR *) p;
+
+         if (type == *entry_type)
+           {
+             const gdb_byte *value_ptr = p + 16;
+             value = *((CORE_ADDR *) value_ptr);
+             return true;
+           }
+         p += 32;
+       }
+      return false;
+    }
+
+  /* We have the regular layout.  Let generic code handle it.  */
+  return linux_process_target::auxv_search (type, value);
+}
+
 /* List of condition codes that we need.  */
 
 enum aarch64_condition_codes
@@ -3475,7 +3546,7 @@ aarch64_target::store_memtags (CORE_ADDR address, size_t len,
 bool
 aarch64_target::supports_qxfer_capability ()
 {
-  unsigned long hwcap2 = linux_get_hwcap2 (8);
+  unsigned long hwcap2 = linux_get_hwcap2 ();
 
   return (hwcap2 & HWCAP2_MORELLO) != 0;
 }
index 58a2a12b3327eebb533a8b057cc90bc865d22863..f8b5809743d816227ec1864bac40330b213954a1 100644 (file)
@@ -950,6 +950,9 @@ linux_process_target::post_create_inferior ()
 {
   struct lwp_info *lwp = get_thread_lwp (current_thread);
 
+  /* Fetch the auxv.  */
+  get_auxv ();
+
   low_arch_setup ();
 
   if (lwp->must_set_ptrace_flags)
@@ -5536,32 +5539,72 @@ linux_process_target::supports_read_auxv ()
   return true;
 }
 
-/* Copy LEN bytes from inferior's auxiliary vector starting at OFFSET
-   to debugger memory starting at MYADDR.  */
-
-int
-linux_process_target::read_auxv (CORE_ADDR offset, unsigned char *myaddr,
-                                unsigned int len)
+const gdb::byte_vector
+linux_process_target::get_auxv ()
 {
+  /* Always update the auxv.  */
+  auxv.clear ();
+
   char filename[PATH_MAX];
-  int fd, n;
+  int fd;
   int pid = lwpid_of (current_thread);
 
   xsnprintf (filename, sizeof filename, "/proc/%d/auxv", pid);
 
   fd = open (filename, O_RDONLY);
   if (fd < 0)
-    return -1;
+    return auxv;
 
-  if (offset != (CORE_ADDR) 0
-      && lseek (fd, (off_t) offset, SEEK_SET) != (off_t) offset)
-    n = -1;
-  else
-    n = read (fd, myaddr, len);
+  size_t block_size = 1024;
+  auxv.resize (block_size);
+  gdb_byte *ptr = auxv.data ();
+  bool done = false;
+
+  while (!done)
+    {
+      int n = read (fd, ptr, block_size);
+
+      if (n < 0)
+       {
+         auxv.clear ();
+         done = true;
+       }
+      else if (n < block_size)
+       {
+         /* We're done reading data.  */
+         auxv.resize (auxv.size () - (block_size - n));
+         done = true;
+       }
+      else
+       {
+         auxv.resize (auxv.size () + block_size);
+         ptr = auxv.data () + auxv.size ();
+       }
+    }
 
   close (fd);
+  return auxv;
+}
+
+/* Copy LEN bytes from inferior's auxiliary vector starting at OFFSET
+   to debugger memory starting at MYADDR.  */
 
-  return n;
+int
+linux_process_target::read_auxv (CORE_ADDR offset, unsigned char *myaddr,
+                                unsigned int len)
+{
+  if (!auxv.empty ())
+    {
+      /* Don't attempt to read past the end of the auxv.  */
+      if (offset + len > auxv.size ())
+       len = auxv.size () - offset;
+      /* Copy the requested contents.  */
+      memcpy (myaddr, auxv.data () + offset, len);
+    }
+  else
+    return -1;
+
+  return len;
 }
 
 int
@@ -6233,62 +6276,18 @@ linux_process_target::done_accessing_memory ()
 
 static int
 get_phdr_phnum_from_proc_auxv (const int pid, const int is_elf64,
-                              CORE_ADDR *phdr_memaddr, int *num_phdr)
+                              CORE_ADDR &phdr_memaddr, int &num_phdr)
 {
-  char filename[PATH_MAX];
-  int fd;
-  const int auxv_size = is_elf64
-    ? sizeof (Elf64_auxv_t) : sizeof (Elf32_auxv_t);
-  char buf[sizeof (Elf64_auxv_t)];  /* The larger of the two.  */
-
-  xsnprintf (filename, sizeof filename, "/proc/%d/auxv", pid);
-
-  fd = open (filename, O_RDONLY);
-  if (fd < 0)
-    return 1;
+  linux_get_auxv (AT_PHDR, phdr_memaddr);
+  CORE_ADDR value = 0;
+  linux_get_auxv (AT_PHNUM, value);
+  num_phdr = (int) value;
 
-  *phdr_memaddr = 0;
-  *num_phdr = 0;
-  while (read (fd, buf, auxv_size) == auxv_size
-        && (*phdr_memaddr == 0 || *num_phdr == 0))
-    {
-      if (is_elf64)
-       {
-         Elf64_auxv_t *const aux = (Elf64_auxv_t *) buf;
-
-         switch (aux->a_type)
-           {
-           case AT_PHDR:
-             *phdr_memaddr = aux->a_un.a_val;
-             break;
-           case AT_PHNUM:
-             *num_phdr = aux->a_un.a_val;
-             break;
-           }
-       }
-      else
-       {
-         Elf32_auxv_t *const aux = (Elf32_auxv_t *) buf;
-
-         switch (aux->a_type)
-           {
-           case AT_PHDR:
-             *phdr_memaddr = aux->a_un.a_val;
-             break;
-           case AT_PHNUM:
-             *num_phdr = aux->a_un.a_val;
-             break;
-           }
-       }
-    }
-
-  close (fd);
-
-  if (*phdr_memaddr == 0 || *num_phdr == 0)
+  if (phdr_memaddr == 0 || num_phdr == 0)
     {
       warning ("Unexpected missing AT_PHDR and/or AT_PHNUM: "
               "phdr_memaddr = %ld, phdr_num = %d",
-              (long) *phdr_memaddr, *num_phdr);
+              (long) phdr_memaddr, num_phdr);
       return 2;
     }
 
@@ -6305,7 +6304,7 @@ get_dynamic (const int pid, const int is_elf64)
   unsigned char *phdr_buf;
   const int phdr_size = is_elf64 ? sizeof (Elf64_Phdr) : sizeof (Elf32_Phdr);
 
-  if (get_phdr_phnum_from_proc_auxv (pid, is_elf64, &phdr_memaddr, &num_phdr))
+  if (get_phdr_phnum_from_proc_auxv (pid, is_elf64, phdr_memaddr, num_phdr))
     return 0;
 
   gdb_assert (num_phdr < 100);  /* Basic sanity check.  */
@@ -6919,6 +6918,57 @@ linux_process_target::thread_pending_child (thread_info *thread)
   return get_lwp_thread (child);
 }
 
+/* See linux-low.h.  */
+
+bool
+linux_process_target::auxv_search (CORE_ADDR type, CORE_ADDR &value)
+{
+  get_auxv ();
+
+  gdb_byte *ptr = auxv.data ();
+  gdb_byte *end_ptr = auxv.data () + auxv.size ();
+
+  if (ptr == end_ptr)
+    return false;
+
+  int pid = lwpid_of (current_thread);
+  unsigned int machine;
+  int elf64 = elf_64_file_p (the_target->pid_to_exec_file (pid),
+                            &machine);
+  int wordsize = elf64? 8 : 4;
+
+  gdb_assert (end_ptr - ptr >= 2 * wordsize);
+
+  gdb_byte *p = ptr;
+
+  while (p < end_ptr)
+    {
+      if (wordsize == 4)
+       {
+         Elf32_auxv_t *auxv_entry = (Elf32_auxv_t *) p;
+
+         if (type == auxv_entry->a_type)
+           {
+             value = auxv_entry->a_un.a_val;
+             return true;
+           }
+       }
+      else
+       {
+         Elf64_auxv_t *auxv_entry = (Elf64_auxv_t *) p;
+
+         if (type == auxv_entry->a_type)
+           {
+             value = auxv_entry->a_un.a_val;
+             return true;
+           }
+       }
+
+      p += 2 * wordsize;
+    }
+  return false;
+}
+
 /* Default implementation of linux_target_ops method "set_pc" for
    32-bit pc register which is literally named "pc".  */
 
@@ -6969,68 +7019,39 @@ linux_get_pc_64bit (struct regcache *regcache)
 
 /* See linux-low.h.  */
 
-int
-linux_get_auxv (int wordsize, CORE_ADDR match, CORE_ADDR *valp)
+bool
+linux_get_auxv (CORE_ADDR match, CORE_ADDR &valp)
 {
-  gdb_byte *data = (gdb_byte *) alloca (2 * wordsize);
-  int offset = 0;
-
-  gdb_assert (wordsize == 4 || wordsize == 8);
-
-  while (the_target->read_auxv (offset, data, 2 * wordsize) == 2 * wordsize)
-    {
-      if (wordsize == 4)
-       {
-         uint32_t *data_p = (uint32_t *) data;
-         if (data_p[0] == match)
-           {
-             *valp = data_p[1];
-             return 1;
-           }
-       }
-      else
-       {
-         uint64_t *data_p = (uint64_t *) data;
-         if (data_p[0] == match)
-           {
-             *valp = data_p[1];
-             return 1;
-           }
-       }
-
-      offset += 2 * wordsize;
-    }
-
-  return 0;
+  return the_linux_target->auxv_search (match, valp);
 }
 
 /* See linux-low.h.  */
 
 CORE_ADDR
-linux_get_at_entry (int wordsize)
+linux_get_at_entry ()
 {
   CORE_ADDR entry = 0;
-  linux_get_auxv (wordsize, AT_ENTRY, &entry);
+  linux_get_auxv (AT_ENTRY, entry);
   return entry;
 }
 
 /* See linux-low.h.  */
 
 CORE_ADDR
-linux_get_hwcap (int wordsize)
+linux_get_hwcap ()
 {
   CORE_ADDR hwcap = 0;
-  linux_get_auxv (wordsize, AT_HWCAP, &hwcap);
+  linux_get_auxv (AT_HWCAP, hwcap);
   return hwcap;
 }
 
 /* See linux-low.h.  */
 
 CORE_ADDR
-linux_get_hwcap2 (int wordsize)
+linux_get_hwcap2 ()
 {
   CORE_ADDR hwcap2 = 0;
-  linux_get_auxv (wordsize, AT_HWCAP2, &hwcap2);
+  linux_get_auxv (AT_HWCAP2, hwcap2);
   return hwcap2;
 }
 
index e2259ae96c71c74277bdbd900426c19620683fe7..fbfa37f2f17ba773f6cfa45c185c237ad0b93272 100644 (file)
@@ -32,6 +32,7 @@
 #include "tracepoint.h"
 
 #include <list>
+#include "gdbsupport/byte-vector.h"
 
 #define PTRACE_XFER_TYPE long
 
@@ -588,6 +589,14 @@ public: /* Make this public because it's used from outside.  */
      error.  */
   int attach_lwp (ptid_t ptid);
 
+  /* Given PTR, a pointer to the beginning of auxv data, and PTR_END the
+     pointer to the end of the auxv data, search for the entry with type
+     TYPE and store its value in VALUE.
+
+     Return TRUE if an entry was found.  Return FALSE in case of error or if
+     no entries have been found.  */
+  virtual bool auxv_search (CORE_ADDR type, CORE_ADDR &value);
+
 private: /* Back to private.  */
   /* Detach from LWP.  */
   void detach_one_lwp (lwp_info *lwp);
@@ -730,6 +739,11 @@ protected:
   /* Return the linkmap offsets based on IS_ELF64.  */
   virtual const struct link_map_offsets *low_fetch_linkmap_offsets (int is_elf64);
 
+  /* Fetch the entire contents of the auxv.  */
+  const gdb::byte_vector get_auxv ();
+
+  /* The auxv data.  */
+  gdb::byte_vector auxv;
 };
 
 extern linux_process_target *the_linux_target;
@@ -970,27 +984,26 @@ bool thread_db_thread_handle (ptid_t ptid, gdb_byte **handle, int *handle_len);
 
 extern int have_ptrace_getregset;
 
-/* Search for the value with type MATCH in the auxv vector with
-   entries of length WORDSIZE bytes.  If found, store the value in
-   *VALP and return 1.  If not found or if there is an error, return
-   0.  */
+/* Search for the value with type MATCH in the auxv vector.
+
+   If found, store the value in *VALP and return TRUE.  If not found or if there
+   is an error, return FALSE.  */
 
-int linux_get_auxv (int wordsize, CORE_ADDR match,
-                   CORE_ADDR *valp);
+bool linux_get_auxv (CORE_ADDR match, CORE_ADDR &valp);
 
-/* Fetch the AT_ENTRY entry from the auxv vector, where entries are length
-   WORDSIZE.  If no entry was found, return zero.  */
+/* Fetch the AT_ENTRY entry from the auxv vector.
+   If no entry was found, return zero.  */
 
-CORE_ADDR linux_get_at_entry (int wordsize);
+CORE_ADDR linux_get_at_entry ();
 
-/* Fetch the AT_HWCAP entry from the auxv vector, where entries are length
-   WORDSIZE.  If no entry was found, return zero.  */
+/* Fetch the AT_HWCAP entry from the auxv vector.
+   If no entry was found, return zero.  */
 
-CORE_ADDR linux_get_hwcap (int wordsize);
+CORE_ADDR linux_get_hwcap ();
 
-/* Fetch the AT_HWCAP2 entry from the auxv vector, where entries are length
-   WORDSIZE.  If no entry was found, return zero.  */
+/* Fetch the AT_HWCAP2 entry from the auxv vector.
+   If no entry was found, return zero.  */
 
-CORE_ADDR linux_get_hwcap2 (int wordsize);
+CORE_ADDR linux_get_hwcap2 ();
 
 #endif /* GDBSERVER_LINUX_LOW_H */
index 08824887003ba090f1ec1a644c1ba9cf9afc44f0..a9b718a56bc0439067fba49f14d9835893ed4d99 100644 (file)
@@ -894,8 +894,8 @@ ppc_target::low_arch_setup ()
 
   /* The value of current_process ()->tdesc needs to be set for this
      call.  */
-  ppc_hwcap = linux_get_hwcap (features.wordsize);
-  ppc_hwcap2 = linux_get_hwcap2 (features.wordsize);
+  ppc_hwcap = linux_get_hwcap ();
+  ppc_hwcap2 = linux_get_hwcap2 ();
 
   features.isa205 = ppc_linux_has_isa205 (ppc_hwcap);
 
index 5adc28070574734bf3972be9b93b522ab3192523..3f691c881c1b707e7a50c8d4c2718b9d22f7c206 100644 (file)
@@ -592,7 +592,7 @@ s390_target::low_arch_setup ()
   /* Determine word size and HWCAP.  */
   int pid = pid_of (current_thread);
   int wordsize = s390_get_wordsize (pid);
-  unsigned long hwcap = linux_get_hwcap (wordsize);
+  unsigned long hwcap = linux_get_hwcap ();
 
   /* Check whether the kernel supports extra register sets.  */
   int have_regset_last_break