]> 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)
committerLuis Machado <luis.machado@arm.com>
Thu, 19 May 2022 08:28:17 +0000 (09:28 +0100)
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 627ddccc5f79c57898e45f44ee73202487634daf..3a6537a9e8ea49354e321e14057cf29177321a07 100644 (file)
@@ -89,6 +89,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;
@@ -824,8 +828,8 @@ aarch64_target::low_arch_setup ()
   if (is_elf64)
     {
       uint64_t vq = aarch64_sve_get_vq (tid);
-      unsigned long hwcap = linux_get_hwcap (8);
-      unsigned long hwcap2 = linux_get_hwcap2 (8);
+      unsigned long hwcap = linux_get_hwcap ();
+      unsigned long hwcap2 = linux_get_hwcap2 ();
       bool pauth_p = hwcap & AARCH64_HWCAP_PACA;
       bool capability_p = hwcap2 & HWCAP2_MORELLO;
 
@@ -931,7 +935,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.  */
@@ -942,6 +946,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
@@ -3318,7 +3389,7 @@ aarch64_target::breakpoint_kind_from_current_state (CORE_ADDR *pcptr)
 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 0ba60881f1119037bb2df317327483939eee8e63..110be634f310d6e5a27e3f83f5fa9bc30bb4de20 100644 (file)
@@ -997,6 +997,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)
@@ -5753,32 +5756,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
@@ -6465,62 +6508,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;
     }
 
@@ -6537,7 +6536,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.  */
@@ -7129,6 +7128,57 @@ linux_process_target::thread_handle (ptid_t ptid, gdb_byte **handle,
 }
 #endif
 
+/* 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".  */
 
@@ -7181,68 +7231,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 b2b3e74612e42d4d3aa0b30c728b87f5a26cf2cc..b7ffbfc64a67c36fe881a00a2abf7378d2fed2ad 100644 (file)
@@ -32,6 +32,7 @@
 #include "tracepoint.h"
 
 #include <list>
+#include "gdbsupport/byte-vector.h"
 
 #define PTRACE_XFER_TYPE long
 
@@ -585,6 +586,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);
@@ -727,6 +736,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;
@@ -913,27 +927,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 337d555aee724279b540c3a5ab85d4945da34176..91e7d466cccfb13af0432175411bebec76fd8b26 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 f095181a23443b46cfa3460d142a3c88efd819d4..2f8f4c70cdf2344751bedea6812858581f8fd1aa 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