aarch64_gdbarch_tdep *tdep = gdbarch_tdep<aarch64_gdbarch_tdep> (gdbarch);
if (tdep->has_tls ())
fetch_regset<uint64_t> (regcache, regnum, NT_ARM_TLS,
- &aarch64_fbsd_tls_regset, tdep->tls_regnum);
+ &aarch64_fbsd_tls_regset, tdep->tls_regnum_base);
}
/* Store register REGNUM back into the inferior. If REGNUM is -1, do
aarch64_gdbarch_tdep *tdep = gdbarch_tdep<aarch64_gdbarch_tdep> (gdbarch);
if (tdep->has_tls ())
store_regset<uint64_t> (regcache, regnum, NT_ARM_TLS,
- &aarch64_fbsd_tls_regset, tdep->tls_regnum);
+ &aarch64_fbsd_tls_regset, tdep->tls_regnum_base);
}
/* Implement the target read_description method. */
aarch64_fbsd_nat_target::read_description ()
{
aarch64_features features;
- features.tls = have_regset (inferior_ptid, NT_ARM_TLS) != 0;
+ features.tls = have_regset (inferior_ptid, NT_ARM_TLS)? 1 : 0;
return aarch64_read_description (features);
}
{ 0 }
};
-/* Register numbers are relative to tdep->tls_regnum. */
+/* Register numbers are relative to tdep->tls_regnum_base. */
static const struct regcache_map_entry aarch64_fbsd_tls_regmap[] =
{
struct gdbarch *gdbarch = regcache->arch ();
aarch64_gdbarch_tdep *tdep = gdbarch_tdep<aarch64_gdbarch_tdep> (gdbarch);
- regcache->supply_regset (regset, tdep->tls_regnum, regnum, buf, size);
+ regcache->supply_regset (regset, tdep->tls_regnum_base, regnum, buf, size);
}
static void
struct gdbarch *gdbarch = regcache->arch ();
aarch64_gdbarch_tdep *tdep = gdbarch_tdep<aarch64_gdbarch_tdep> (gdbarch);
- regcache->collect_regset (regset, tdep->tls_regnum, regnum, buf, size);
+ regcache->collect_regset (regset, tdep->tls_regnum_base, regnum, buf, size);
}
const struct regset aarch64_fbsd_tls_regset =
asection *tls = bfd_get_section_by_name (abfd, ".reg-aarch-tls");
aarch64_features features;
- features.tls = tls != nullptr;
+ features.tls = tls != nullptr? 1 : 0;
return aarch64_read_description (features);
}
regcache = get_thread_arch_regcache (current_inferior ()->process_target (),
ptid, gdbarch);
- target_fetch_registers (regcache, tdep->tls_regnum);
+ target_fetch_registers (regcache, tdep->tls_regnum_base);
ULONGEST tpidr;
- if (regcache->cooked_read (tdep->tls_regnum, &tpidr) != REG_VALID)
+ if (regcache->cooked_read (tdep->tls_regnum_base, &tpidr) != REG_VALID)
error (_("Unable to fetch %%tpidr"));
/* %tpidr points to the TCB whose first member is the dtv
{
aarch64_gdbarch_tdep *tdep
= gdbarch_tdep<aarch64_gdbarch_tdep> (regcache->arch ());
- int regno = tdep->tls_regnum;
+ int regno = tdep->tls_regnum_base;
gdb_assert (regno != -1);
+ gdb_assert (tdep->tls_register_count > 0);
- uint64_t tpidr = 0;
+ uint64_t tpidrs[tdep->tls_register_count] = { 0 };
struct iovec iovec;
-
- iovec.iov_base = &tpidr;
- iovec.iov_len = sizeof (tpidr);
+ iovec.iov_base = tpidrs;
+ iovec.iov_len = sizeof (tpidrs);
int tid = get_ptrace_pid (regcache->ptid ());
if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_TLS, &iovec) != 0)
- perror_with_name (_("unable to fetch TLS register"));
+ perror_with_name (_("unable to fetch TLS registers"));
- regcache->raw_supply (regno, &tpidr);
+ for (int i = 0; i < tdep->tls_register_count; i++)
+ regcache->raw_supply (regno + i, &tpidrs[i]);
}
/* Store to the current thread the valid TLS register set in GDB's
{
aarch64_gdbarch_tdep *tdep
= gdbarch_tdep<aarch64_gdbarch_tdep> (regcache->arch ());
- int regno = tdep->tls_regnum;
+ int regno = tdep->tls_regnum_base;
gdb_assert (regno != -1);
+ gdb_assert (tdep->tls_register_count > 0);
- uint64_t tpidr = 0;
+ uint64_t tpidrs[tdep->tls_register_count] = { 0 };
- if (REG_VALID != regcache->get_register_status (regno))
- return;
+ for (int i = 0; i < tdep->tls_register_count; i++)
+ {
+ if (REG_VALID != regcache->get_register_status (regno + i))
+ continue;
- regcache->raw_collect (regno, (char *) &tpidr);
+ regcache->raw_collect (regno + i, (char *) &tpidrs[i]);
+ }
struct iovec iovec;
-
- iovec.iov_base = &tpidr;
- iovec.iov_len = sizeof (tpidr);
+ iovec.iov_base = &tpidrs;
+ iovec.iov_len = sizeof (tpidrs);
int tid = get_ptrace_pid (regcache->ptid ());
if (ptrace (PTRACE_SETREGSET, tid, NT_ARM_TLS, &iovec) != 0)
&& (regno == tdep->mte_reg_base))
fetch_mteregs_from_thread (regcache);
- if (tdep->has_tls () && regno == tdep->tls_regnum)
+ if (tdep->has_tls ()
+ && regno >= tdep->tls_regnum_base
+ && regno < tdep->tls_regnum_base + tdep->tls_register_count)
fetch_tlsregs_from_thread (regcache);
}
&& (regno == tdep->mte_reg_base))
store_mteregs_to_thread (regcache);
- if (tdep->has_tls () && regno == tdep->tls_regnum)
+ if (tdep->has_tls ()
+ && regno >= tdep->tls_regnum_base
+ && regno < tdep->tls_regnum_base + tdep->tls_register_count)
store_tlsregs_to_thread (regcache);
}
features.vq = aarch64_sve_get_vq (tid);
features.pauth = hwcap & AARCH64_HWCAP_PACA;
features.mte = hwcap2 & HWCAP2_MTE;
- features.tls = true;
+ features.tls = aarch64_tls_register_count (tid);
return aarch64_read_description (features);
}
"MTE registers", cb_data);
}
+ /* Handle the TLS registers. */
if (tdep->has_tls ())
{
+ gdb_assert (tdep->tls_regnum_base != -1);
+ gdb_assert (tdep->tls_register_count > 0);
+
+ int sizeof_tls_regset
+ = AARCH64_TLS_REGISTER_SIZE * tdep->tls_register_count;
+
const struct regcache_map_entry tls_regmap[] =
{
- { 1, tdep->tls_regnum, 8 },
+ { tdep->tls_register_count, tdep->tls_regnum_base,
+ AARCH64_TLS_REGISTER_SIZE },
{ 0 }
};
const struct regset aarch64_linux_tls_regset =
{
- tls_regmap, regcache_supply_regset, regcache_collect_regset
+ tls_regmap, regcache_supply_regset, regcache_collect_regset,
+ REGSET_VARIABLE_SIZE
};
- cb (".reg-aarch-tls", AARCH64_LINUX_SIZEOF_TLSREGSET,
- AARCH64_LINUX_SIZEOF_TLSREGSET, &aarch64_linux_tls_regset,
- "TLS register", cb_data);
+ cb (".reg-aarch-tls", sizeof_tls_regset, sizeof_tls_regset,
+ &aarch64_linux_tls_regset, "TLS register", cb_data);
}
}
aarch64_linux_core_read_description (struct gdbarch *gdbarch,
struct target_ops *target, bfd *abfd)
{
- asection *tls = bfd_get_section_by_name (abfd, ".reg-aarch-tls");
gdb::optional<gdb::byte_vector> auxv = target_read_auxv_raw (target);
CORE_ADDR hwcap = linux_get_hwcap (auxv, target, gdbarch);
CORE_ADDR hwcap2 = linux_get_hwcap2 (auxv, target, gdbarch);
features.vq = aarch64_linux_core_read_vq (gdbarch, abfd);
features.pauth = hwcap & AARCH64_HWCAP_PACA;
features.mte = hwcap2 & HWCAP2_MTE;
- features.tls = tls != nullptr;
+
+ /* Handle the TLS section. */
+ asection *tls = bfd_get_section_by_name (abfd, ".reg-aarch-tls");
+ if (tls != nullptr)
+ {
+ size_t size = bfd_section_size (tls);
+ /* Convert the size to the number of actual registers, by
+ dividing by 8. */
+ features.tls = size / AARCH64_TLS_REGISTER_SIZE;
+ }
return aarch64_read_description (features);
}
/* The MTE regset consists of a 64-bit register. */
#define AARCH64_LINUX_SIZEOF_MTE_REGSET (8)
-/* The TLS regset consists of a single register. */
-#define AARCH64_LINUX_SIZEOF_TLSREGSET (X_REGISTER_SIZE)
-
extern const struct regset aarch64_linux_gregset;
extern const struct regset aarch64_linux_fpregset;
= (tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.pauth") != nullptr);
features.mte
= (tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.mte") != nullptr);
- features.tls
- = (tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.tls") != nullptr);
+
+ const struct tdesc_feature *tls_feature
+ = tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.tls");
+
+ if (tls_feature != nullptr)
+ {
+ /* We have TLS registers. Find out how many. */
+ if (tdesc_unnumbered_register (tls_feature, "tpidr2"))
+ features.tls = 2;
+ else
+ features.tls = 1;
+ }
return features;
}
bool valid_p = true;
int i, num_regs = 0, num_pseudo_regs = 0;
int first_pauth_regnum = -1, ra_sign_state_offset = -1;
- int first_mte_regnum = -1, tls_regnum = -1;
+ int first_mte_regnum = -1, first_tls_regnum = -1;
uint64_t vq = aarch64_get_tdesc_vq (info.target_desc);
if (vq > AARCH64_MAX_SVE_VQ)
}
/* Add the TLS register. */
+ int tls_register_count = 0;
if (feature_tls != nullptr)
{
- tls_regnum = num_regs;
- /* Validate the descriptor provides the mandatory TLS register
- and allocate its number. */
- valid_p = tdesc_numbered_register (feature_tls, tdesc_data.get (),
- tls_regnum, "tpidr");
+ first_tls_regnum = num_regs;
- num_regs++;
+ /* Look for the TLS registers. tpidr is required, but tpidr2 is
+ optional. */
+ valid_p
+ = tdesc_numbered_register (feature_tls, tdesc_data.get (),
+ first_tls_regnum, "tpidr");
+
+ if (valid_p)
+ {
+ tls_register_count++;
+
+ bool has_tpidr2
+ = tdesc_numbered_register (feature_tls, tdesc_data.get (),
+ first_tls_regnum + tls_register_count,
+ "tpidr2");
+
+ /* Figure out how many TLS registers we have. */
+ if (has_tpidr2)
+ tls_register_count++;
+
+ num_regs += tls_register_count;
+ }
+ else
+ {
+ warning (_("Provided TLS register feature doesn't contain "
+ "required tpidr register."));
+ return nullptr;
+ }
}
/* Add the pauth registers. */
tdep->pauth_reg_base = first_pauth_regnum;
tdep->ra_sign_state_regnum = -1;
tdep->mte_reg_base = first_mte_regnum;
- tdep->tls_regnum = tls_regnum;
+ tdep->tls_regnum_base = first_tls_regnum;
+ tdep->tls_register_count = tls_register_count;
set_gdbarch_push_dummy_call (gdbarch, aarch64_push_dummy_call);
set_gdbarch_frame_align (gdbarch, aarch64_frame_align);
return mte_reg_base != -1;
}
- /* TLS register. This is -1 if the TLS register is not available. */
- int tls_regnum = 0;
+ /* TLS registers. This is -1 if the TLS registers are not available. */
+ int tls_regnum_base = 0;
+ int tls_register_count = 0;
bool has_tls() const
{
- return tls_regnum != -1;
+ return tls_regnum_base != -1;
}
/* The W pseudo-registers. */
if (features.mte)
regnum = create_feature_aarch64_mte (tdesc.get (), regnum);
- if (features.tls)
- regnum = create_feature_aarch64_tls (tdesc.get (), regnum);
+ /* TLS registers. */
+ if (features.tls > 0)
+ regnum = create_feature_aarch64_tls (tdesc.get (), regnum, features.tls);
return tdesc.release ();
}
bool pauth = false;
bool mte = false;
- bool tls = false;
+
+ /* A positive TLS value indicates the number of TLS registers available. */
+ uint8_t tls = 0;
};
inline bool operator==(const aarch64_features &lhs, const aarch64_features &rhs)
h = features.vq;
h = h << 1 | features.pauth;
h = h << 1 | features.mte;
- h = h << 1 | features.tls;
+ /* Shift by two bits for now. We may need to increase this in the future
+ if more TLS registers get added. */
+ h = h << 2 | features.tls;
return h;
}
};
AARCH64_LAST_V_ARG_REGNUM = AARCH64_V0_REGNUM + 7
};
-#define V_REGISTER_SIZE 16
+/* Sizes of various AArch64 registers. */
+#define AARCH64_TLS_REGISTER_SIZE 8
+#define V_REGISTER_SIZE 16
/* Pseudo register base numbers. */
#define AARCH64_Q0_REGNUM 0
#define AARCH64_NUM_REGS AARCH64_FPCR_REGNUM + 1
#define AARCH64_SVE_NUM_REGS AARCH64_SVE_VG_REGNUM + 1
-#define AARCH64_TLS_REGS_SIZE (8)
-
/* There are a number of ways of expressing the current SVE vector size:
VL : Vector Length.
aarch64-fpu.xml \
aarch64-pauth.xml \
aarch64-mte.xml \
- aarch64-tls.xml \
arc/v1-core.xml \
arc/v1-aux.xml \
arc/v2-core.xml \
-/* THIS FILE IS GENERATED. -*- buffer-read-only: t -*- vi:set ro:
- Original: aarch64-tls.xml */
+/* Copyright (C) 2022 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "gdbsupport/tdesc.h"
+/* This function is NOT auto generated from xml.
+
+ Create the aarch64 description containing the TLS registers. TPIDR is
+ always available, but TPIDR2 is only available on some systems.
+
+ COUNT is the number of registers in this set. The minimum is 1. */
+
static int
-create_feature_aarch64_tls (struct target_desc *result, long regnum)
+create_feature_aarch64_tls (struct target_desc *result, long regnum, int count)
{
+ /* TPIDR is always present. */
+ gdb_assert (count >= 1);
+
struct tdesc_feature *feature;
feature = tdesc_create_feature (result, "org.gnu.gdb.aarch64.tls");
tdesc_create_reg (feature, "tpidr", regnum++, 1, NULL, 64, "data_ptr");
+
+ /* Add TPIDR2. */
+ if (count > 1)
+ tdesc_create_reg (feature, "tpidr2", regnum++, 1, NULL, 64, "data_ptr");
+
return regnum;
}
+++ /dev/null
-<?xml version="1.0"?>
-<!-- Copyright (C) 2022 Free Software Foundation, Inc.
-
- Copying and distribution of this file, with or without modification,
- are permitted in any medium without royalty provided the copyright
- notice and this notice are preserved. -->
-
-<!DOCTYPE feature SYSTEM "gdb-target.dtd">
-<feature name="org.gnu.gdb.aarch64.tls">
- <reg name="tpidr" bitsize="64" type="data_ptr"/>
-</feature>
return PS_OK;
}
+
+/* See nat/aarch64-linux.h. */
+
+int
+aarch64_tls_register_count (int tid)
+{
+ uint64_t tls_regs[2];
+ struct iovec iovec;
+ iovec.iov_base = tls_regs;
+ iovec.iov_len = sizeof (tls_regs);
+
+ /* Attempt to read both TPIDR and TPIDR2. If the request fails, it means
+ the Linux Kernel does not support TPIDR2.
+
+ Otherwise the Linux Kernel supports both TPIDR and TPIDR2. */
+ if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_TLS, &iovec) != 0)
+ return 1;
+
+ /* TPIDR2 is available as well. */
+ return 2;
+}
lwpid_t lwpid, int idx, void **base,
int is_64bit_p);
+/* Return the number of TLS registers in the NT_ARM_TLS set. This is only
+ used for aarch64 state. */
+int aarch64_tls_register_count (int tid);
+
#endif /* NAT_AARCH64_LINUX_H */
static void
aarch64_fill_tlsregset (struct regcache *regcache, void *buf)
{
+ gdb_byte *tls_buf = (gdb_byte *) buf;
int tls_regnum = find_regno (regcache->tdesc, "tpidr");
- collect_register (regcache, tls_regnum, buf);
+ collect_register (regcache, tls_regnum, tls_buf);
+
+ /* Read TPIDR2, if it exists. */
+ gdb::optional<int> regnum = find_regno_no_throw (regcache->tdesc, "tpidr2");
+
+ if (regnum.has_value ())
+ collect_register (regcache, *regnum, tls_buf + sizeof (uint64_t));
}
/* Store TLS register to regcache. */
static void
aarch64_store_tlsregset (struct regcache *regcache, const void *buf)
{
+ gdb_byte *tls_buf = (gdb_byte *) buf;
int tls_regnum = find_regno (regcache->tdesc, "tpidr");
- supply_register (regcache, tls_regnum, buf);
+ supply_register (regcache, tls_regnum, tls_buf);
+
+ /* Write TPIDR2, if it exists. */
+ gdb::optional<int> regnum = find_regno_no_throw (regcache->tdesc, "tpidr2");
+
+ if (regnum.has_value ())
+ supply_register (regcache, *regnum, tls_buf + sizeof (uint64_t));
}
bool
regset->size = AARCH64_LINUX_SIZEOF_MTE;
break;
case NT_ARM_TLS:
- if (features.tls)
- regset->size = AARCH64_TLS_REGS_SIZE;
+ if (features.tls > 0)
+ regset->size = AARCH64_TLS_REGISTER_SIZE * features.tls;
break;
default:
gdb_assert_not_reached ("Unknown register set found.");
features.pauth = linux_get_hwcap (8) & AARCH64_HWCAP_PACA;
/* A-profile MTE is 64-bit only. */
features.mte = linux_get_hwcap2 (8) & HWCAP2_MTE;
- features.tls = true;
+ features.tls = aarch64_tls_register_count (tid);
current_process ()->tdesc = aarch64_linux_read_description (features);
hex2bin (buf, registers, len / 2);
}
-int
-find_regno (const struct target_desc *tdesc, const char *name)
+/* See regcache.h */
+
+gdb::optional<int>
+find_regno_no_throw (const struct target_desc *tdesc, const char *name)
{
for (int i = 0; i < tdesc->reg_defs.size (); ++i)
{
if (strcmp (name, find_register_by_number (tdesc, i).name) == 0)
return i;
}
- internal_error ("Unknown register %s requested",
- name);
+ return {};
+}
+
+int
+find_regno (const struct target_desc *tdesc, const char *name)
+{
+ gdb::optional<int> regnum = find_regno_no_throw (tdesc, name);
+
+ if (regnum.has_value ())
+ return *regnum;
+
+ internal_error ("Unknown register %s requested", name);
}
static void
int register_size (const struct target_desc *tdesc, int n);
+/* No throw version of find_regno. If NAME is not a known register, return
+ an empty value. */
+gdb::optional<int> find_regno_no_throw (const struct target_desc *tdesc,
+ const char *name);
+
int find_regno (const struct target_desc *tdesc, const char *name);
void supply_register (struct regcache *regcache, int n, const void *buf);