diff options
Diffstat (limited to 'gdb/ppc-linux-tdep.c')
-rw-r--r-- | gdb/ppc-linux-tdep.c | 141 |
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. */ |