From: Luis Machado Date: Tue, 17 May 2022 08:55:39 +0000 (+0100) Subject: Teach gdbserver about 32-byte auxv entries for the PCuABI X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5e61a5c9c457b42400e4d300235aa3a1cfbca5f6;p=thirdparty%2Fbinutils-gdb.git Teach gdbserver about 32-byte auxv entries for the PCuABI 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. --- diff --git a/gdbserver/linux-aarch64-low.cc b/gdbserver/linux-aarch64-low.cc index f1708d0c06f..cf36cbe71ab 100644 --- a/gdbserver/linux-aarch64-low.cc +++ b/gdbserver/linux-aarch64-low.cc @@ -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; } diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc index 58a2a12b332..f8b5809743d 100644 --- a/gdbserver/linux-low.cc +++ b/gdbserver/linux-low.cc @@ -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; } diff --git a/gdbserver/linux-low.h b/gdbserver/linux-low.h index e2259ae96c7..fbfa37f2f17 100644 --- a/gdbserver/linux-low.h +++ b/gdbserver/linux-low.h @@ -32,6 +32,7 @@ #include "tracepoint.h" #include +#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 */ diff --git a/gdbserver/linux-ppc-low.cc b/gdbserver/linux-ppc-low.cc index 08824887003..a9b718a56bc 100644 --- a/gdbserver/linux-ppc-low.cc +++ b/gdbserver/linux-ppc-low.cc @@ -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); diff --git a/gdbserver/linux-s390-low.cc b/gdbserver/linux-s390-low.cc index 5adc2807057..3f691c881c1 100644 --- a/gdbserver/linux-s390-low.cc +++ b/gdbserver/linux-s390-low.cc @@ -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