aboutsummaryrefslogtreecommitdiff
path: root/gdb/ppc-linux-tdep.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/ppc-linux-tdep.c')
-rw-r--r--gdb/ppc-linux-tdep.c141
1 files changed, 104 insertions, 37 deletions
diff --git a/gdb/ppc-linux-tdep.c b/gdb/ppc-linux-tdep.c
index 3050a9e..b5f8bbf 100644
--- a/gdb/ppc-linux-tdep.c
+++ b/gdb/ppc-linux-tdep.c
@@ -32,7 +32,6 @@
#include "regset.h"
#include "solib-svr4.h"
#include "solib.h"
-#include "solist.h"
#include "ppc-tdep.h"
#include "ppc64-tdep.h"
#include "ppc-linux-tdep.h"
@@ -49,6 +48,8 @@
#include "arch-utils.h"
#include "xml-syscall.h"
#include "linux-tdep.h"
+#include "solib-svr4-linux.h"
+#include "svr4-tls-tdep.h"
#include "linux-record.h"
#include "record-full.h"
#include "infrun.h"
@@ -60,7 +61,6 @@
#include "cli/cli-utils.h"
#include "parser-defs.h"
#include "user-regs.h"
-#include <ctype.h>
#include "elf-bfd.h"
#include "producer.h"
#include "target-float.h"
@@ -86,8 +86,6 @@
#include "features/rs6000/powerpc-e500l.c"
#include "dwarf2/frame.h"
-/* Shared library operations for PowerPC-Linux. */
-static solib_ops powerpc_so_ops;
/* The syscall's XML filename for PPC and PPC64. */
#define XML_SYSCALL_FILENAME_PPC "syscalls/ppc-linux.xml"
@@ -306,26 +304,45 @@ static const struct ppc_insn_pattern powerpc32_plt_stub_so_2[] =
/* The max number of insns we check using ppc_insns_match_pattern. */
#define POWERPC32_PLT_CHECK_LEN (ARRAY_SIZE (powerpc32_plt_stub) - 1)
-/* Check if PC is in PLT stub. For non-secure PLT, stub is in .plt
- section. For secure PLT, stub is in .text and we need to check
- instruction patterns. */
+/* solib_ops for ILP32 PowerPC/Linux systems. */
-static int
-powerpc_linux_in_dynsym_resolve_code (CORE_ADDR pc)
+struct ppc_linux_ilp32_svr4_solib_ops : public linux_ilp32_svr4_solib_ops
+{
+ using linux_ilp32_svr4_solib_ops::linux_ilp32_svr4_solib_ops;
+
+ /* Check if PC is in PLT stub. For non-secure PLT, stub is in .plt
+ section. For secure PLT, stub is in .text and we need to check
+ instruction patterns. */
+
+ bool in_dynsym_resolve_code (CORE_ADDR pc) const override;
+};
+
+/* Return a new solib_ops for ILP32 PowerPC/Linux systems. */
+
+static solib_ops_up
+make_ppc_linux_ilp32_svr4_solib_ops (program_space *pspace)
+{
+ return std::make_unique<ppc_linux_ilp32_svr4_solib_ops> (pspace);
+}
+
+/* See ppc_linux_ilp32_svr4_solib_ops. */
+
+bool
+ppc_linux_ilp32_svr4_solib_ops::in_dynsym_resolve_code (CORE_ADDR pc) const
{
/* Check whether PC is in the dynamic linker. This also checks
whether it is in the .plt section, used by non-PIC executables. */
- if (svr4_in_dynsym_resolve_code (pc))
- return 1;
+ if (linux_ilp32_svr4_solib_ops::in_dynsym_resolve_code (pc))
+ return true;
/* Check if we are in the resolver. */
bound_minimal_symbol sym = lookup_minimal_symbol_by_pc (pc);
- if (sym.minsym != NULL
- && (strcmp (sym.minsym->linkage_name (), "__glink") == 0
- || strcmp (sym.minsym->linkage_name (), "__glink_PLTresolve") == 0))
- return 1;
- return 0;
+ if (sym.minsym == nullptr)
+ return false;
+
+ return (strcmp (sym.minsym->linkage_name (), "__glink") == 0
+ || strcmp (sym.minsym->linkage_name (), "__glink_PLTresolve") == 0);
}
/* Follow PLT stub to actual routine.
@@ -1694,10 +1711,10 @@ static int
ppc_stap_is_single_operand (struct gdbarch *gdbarch, const char *s)
{
return (*s == 'i' /* Literal number. */
- || (isdigit (*s) && s[1] == '('
- && isdigit (s[2])) /* Displacement. */
- || (*s == '(' && isdigit (s[1])) /* Register indirection. */
- || isdigit (*s)); /* Register value. */
+ || (c_isdigit (*s) && s[1] == '('
+ && c_isdigit (s[2])) /* Displacement. */
+ || (*s == '(' && c_isdigit (s[1])) /* Register indirection. */
+ || c_isdigit (*s)); /* Register value. */
}
/* Implementation of `gdbarch_stap_parse_special_token', as defined in
@@ -1707,7 +1724,7 @@ static expr::operation_up
ppc_stap_parse_special_token (struct gdbarch *gdbarch,
struct stap_parse_info *p)
{
- if (isdigit (*p->arg))
+ if (c_isdigit (*p->arg))
{
/* This temporary pointer is needed because we have to do a lookahead.
We could be dealing with a register displacement, and in such case
@@ -1716,7 +1733,7 @@ ppc_stap_parse_special_token (struct gdbarch *gdbarch,
char *regname;
int len;
- while (isdigit (*s))
+ while (c_isdigit (*s))
++s;
if (*s == '(')
@@ -2071,6 +2088,63 @@ ppc64_linux_gcc_target_options (struct gdbarch *gdbarch)
return "";
}
+/* Fetch and return the TLS DTV (dynamic thread vector) address for PTID.
+ Throw a suitable TLS error if something goes wrong. */
+
+static CORE_ADDR
+ppc64_linux_get_tls_dtv_addr (struct gdbarch *gdbarch, ptid_t ptid,
+ enum svr4_tls_libc libc)
+{
+ /* On ppc64, the thread pointer is found in r13. Fetch this
+ register. */
+ regcache *regcache
+ = get_thread_arch_regcache (current_inferior (), ptid, gdbarch);
+ int thread_pointer_regnum = PPC_R0_REGNUM + 13;
+ target_fetch_registers (regcache, thread_pointer_regnum);
+ ULONGEST thr_ptr;
+ if (regcache->cooked_read (thread_pointer_regnum, &thr_ptr) != REG_VALID)
+ throw_error (TLS_GENERIC_ERROR, _("Unable to fetch thread pointer"));
+
+ /* The thread pointer (r13) is an address that is 0x7000 ahead of
+ the *end* of the TCB (thread control block). The field
+ holding the DTV address is at the very end of the TCB.
+ Therefore, the DTV pointer address can be found by
+ subtracting (0x7000+8) from the thread pointer. Compute the
+ address of the DTV pointer, fetch it, and convert it to an
+ address. */
+ CORE_ADDR dtv_ptr_addr = thr_ptr - 0x7000 - 8;
+ gdb::byte_vector buf (gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT);
+ if (target_read_memory (dtv_ptr_addr, buf.data (), buf.size ()) != 0)
+ throw_error (TLS_GENERIC_ERROR, _("Unable to fetch DTV address"));
+
+ const struct builtin_type *builtin = builtin_type (gdbarch);
+ CORE_ADDR dtv_addr = gdbarch_pointer_to_address
+ (gdbarch, builtin->builtin_data_ptr, buf.data ());
+ return dtv_addr;
+}
+
+/* For internal TLS lookup, return the DTP offset, which is the offset
+ to subtract from a DTV entry, in order to obtain the address of the
+ TLS block. */
+
+static ULONGEST
+ppc_linux_get_tls_dtp_offset (struct gdbarch *gdbarch, ptid_t ptid,
+ svr4_tls_libc libc)
+{
+ if (libc == svr4_tls_libc_musl)
+ {
+ /* This value is DTP_OFFSET, which represents the value to
+ subtract from the DTV entry. For PPC, it can be found in
+ MUSL's arch/powerpc64/pthread_arch.h and
+ arch/powerpc32/pthread_arch.h. (Both values are the same.)
+ It represents the value to subtract from the DTV entry, once
+ it has been fetched from the DTV array. */
+ return 0x8000;
+ }
+ else
+ return 0;
+}
+
static displaced_step_prepare_status
ppc_linux_displaced_step_prepare (gdbarch *arch, thread_info *thread,
CORE_ADDR &displaced_pc)
@@ -2208,8 +2282,6 @@ ppc_linux_init_abi (struct gdbarch_info info,
/* Shared library handling. */
set_gdbarch_skip_trampoline_code (gdbarch, ppc_skip_trampoline_code);
- set_solib_svr4_fetch_link_map_offsets
- (gdbarch, linux_ilp32_fetch_link_map_offsets);
/* Setting the correct XML syscall filename. */
set_xml_syscall_file_name (gdbarch, XML_SYSCALL_FILENAME_PPC);
@@ -2226,14 +2298,7 @@ ppc_linux_init_abi (struct gdbarch_info info,
else
set_gdbarch_gcore_bfd_target (gdbarch, "elf32-powerpc");
- if (powerpc_so_ops.in_dynsym_resolve_code == NULL)
- {
- powerpc_so_ops = svr4_so_ops;
- /* Override dynamic resolve function. */
- powerpc_so_ops.in_dynsym_resolve_code =
- powerpc_linux_in_dynsym_resolve_code;
- }
- set_gdbarch_so_ops (gdbarch, &powerpc_so_ops);
+ set_solib_svr4_ops (gdbarch, make_ppc_linux_ilp32_svr4_solib_ops);
set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver);
}
@@ -2260,8 +2325,7 @@ ppc_linux_init_abi (struct gdbarch_info info,
/* Shared library handling. */
set_gdbarch_skip_trampoline_code (gdbarch, ppc64_skip_trampoline_code);
- set_solib_svr4_fetch_link_map_offsets
- (gdbarch, linux_lp64_fetch_link_map_offsets);
+ set_solib_svr4_ops (gdbarch, make_linux_lp64_svr4_solib_ops);
/* Setting the correct XML syscall filename. */
set_xml_syscall_file_name (gdbarch, XML_SYSCALL_FILENAME_PPC64);
@@ -2284,6 +2348,11 @@ ppc_linux_init_abi (struct gdbarch_info info,
set_gdbarch_gnu_triplet_regexp (gdbarch, ppc64_gnu_triplet_regexp);
/* Set GCC target options. */
set_gdbarch_gcc_target_options (gdbarch, ppc64_linux_gcc_target_options);
+ /* Internal thread local address support. */
+ set_gdbarch_get_thread_local_address (gdbarch,
+ svr4_tls_get_thread_local_address);
+ svr4_tls_register_tls_methods (info, gdbarch, ppc64_linux_get_tls_dtv_addr,
+ ppc_linux_get_tls_dtp_offset);
}
set_gdbarch_core_read_description (gdbarch, ppc_linux_core_read_description);
@@ -2330,9 +2399,7 @@ ppc_linux_init_abi (struct gdbarch_info info,
}
-void _initialize_ppc_linux_tdep ();
-void
-_initialize_ppc_linux_tdep ()
+INIT_GDB_FILE (ppc_linux_tdep)
{
/* Register for all sub-families of the POWER/PowerPC: 32-bit and
64-bit PowerPC, and the older rs6k. */