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;
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;
{
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. */
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
bool
aarch64_target::supports_qxfer_capability ()
{
- unsigned long hwcap2 = linux_get_hwcap2 (8);
+ unsigned long hwcap2 = linux_get_hwcap2 ();
return (hwcap2 & HWCAP2_MORELLO) != 0;
}
{
struct lwp_info *lwp = get_thread_lwp (current_thread);
+ /* Fetch the auxv. */
+ get_auxv ();
+
low_arch_setup ();
if (lwp->must_set_ptrace_flags)
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
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;
}
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. */
}
#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". */
/* 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;
}
#include "tracepoint.h"
#include <list>
+#include "gdbsupport/byte-vector.h"
#define PTRACE_XFER_TYPE long
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);
/* 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;
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 */
/* 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);
/* 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