Index: gdb/ppc-linux-tdep.c |
diff --git a/gdb/ppc-linux-tdep.c b/gdb/ppc-linux-tdep.c |
index b6470fec8dd6cf439a4d41aad6730792a0886371..ccded83f11ae4a8e5827fd334ed4f51a5caece7b 100644 |
--- a/gdb/ppc-linux-tdep.c |
+++ b/gdb/ppc-linux-tdep.c |
@@ -37,6 +37,7 @@ |
#include "solist.h" |
#include "ppc-tdep.h" |
#include "ppc-linux-tdep.h" |
+#include "glibc-tdep.h" |
#include "trad-frame.h" |
#include "frame-unwind.h" |
#include "tramp-frame.h" |
@@ -49,6 +50,14 @@ |
#include "xml-syscall.h" |
#include "linux-tdep.h" |
+#include "stap-probe.h" |
+#include "ax.h" |
+#include "ax-gdb.h" |
+#include "cli/cli-utils.h" |
+#include "parser-defs.h" |
+#include "user-regs.h" |
+#include <ctype.h> |
+ |
#include "features/rs6000/powerpc-32l.c" |
#include "features/rs6000/powerpc-altivec32l.c" |
#include "features/rs6000/powerpc-cell32l.c" |
@@ -65,6 +74,9 @@ |
#include "features/rs6000/powerpc-isa205-vsx64l.c" |
#include "features/rs6000/powerpc-e500l.c" |
+/* Shared library operations for PowerPC-Linux. */ |
+static struct target_so_ops powerpc_so_ops; |
+ |
/* The syscall's XML filename for PPC and PPC64. */ |
#define XML_SYSCALL_FILENAME_PPC "syscalls/ppc-linux.xml" |
#define XML_SYSCALL_FILENAME_PPC64 "syscalls/ppc64-linux.xml" |
@@ -229,7 +241,7 @@ ppc_linux_memory_remove_breakpoint (struct gdbarch *gdbarch, |
which were added later, do get returned in a register though. */ |
static enum return_value_convention |
-ppc_linux_return_value (struct gdbarch *gdbarch, struct type *func_type, |
+ppc_linux_return_value (struct gdbarch *gdbarch, struct value *function, |
struct type *valtype, struct regcache *regcache, |
gdb_byte *readbuf, const gdb_byte *writebuf) |
{ |
@@ -239,7 +251,7 @@ ppc_linux_return_value (struct gdbarch *gdbarch, struct type *func_type, |
&& TYPE_VECTOR (valtype))) |
return RETURN_VALUE_STRUCT_CONVENTION; |
else |
- return ppc_sysv_abi_return_value (gdbarch, func_type, valtype, regcache, |
+ return ppc_sysv_abi_return_value (gdbarch, function, valtype, regcache, |
readbuf, writebuf); |
} |
@@ -599,6 +611,86 @@ ppc64_standard_linkage3_target (struct frame_info *frame, |
return ppc64_desc_entry_point (gdbarch, desc); |
} |
+/* PLT stub in executable. */ |
+static struct insn_pattern powerpc32_plt_stub[] = |
+ { |
+ { 0xffff0000, 0x3d600000, 0 }, /* lis r11, xxxx */ |
+ { 0xffff0000, 0x816b0000, 0 }, /* lwz r11, xxxx(r11) */ |
+ { 0xffffffff, 0x7d6903a6, 0 }, /* mtctr r11 */ |
+ { 0xffffffff, 0x4e800420, 0 }, /* bctr */ |
+ { 0, 0, 0 } |
+ }; |
+ |
+/* PLT stub in shared library. */ |
+static struct insn_pattern powerpc32_plt_stub_so[] = |
+ { |
+ { 0xffff0000, 0x817e0000, 0 }, /* lwz r11, xxxx(r30) */ |
+ { 0xffffffff, 0x7d6903a6, 0 }, /* mtctr r11 */ |
+ { 0xffffffff, 0x4e800420, 0 }, /* bctr */ |
+ { 0xffffffff, 0x60000000, 0 }, /* nop */ |
+ { 0, 0, 0 } |
+ }; |
+#define POWERPC32_PLT_STUB_LEN ARRAY_SIZE (powerpc32_plt_stub) |
+ |
+/* 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. */ |
+ |
+static int |
+powerpc_linux_in_dynsym_resolve_code (CORE_ADDR pc) |
+{ |
+ struct minimal_symbol *sym; |
+ |
+ /* 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; |
+ |
+ /* Check if we are in the resolver. */ |
+ sym = lookup_minimal_symbol_by_pc (pc); |
+ if (sym != NULL |
+ && (strcmp (SYMBOL_LINKAGE_NAME (sym), "__glink") == 0 |
+ || strcmp (SYMBOL_LINKAGE_NAME (sym), "__glink_PLTresolve") == 0)) |
+ return 1; |
+ |
+ return 0; |
+} |
+ |
+/* Follow PLT stub to actual routine. */ |
+ |
+static CORE_ADDR |
+ppc_skip_trampoline_code (struct frame_info *frame, CORE_ADDR pc) |
+{ |
+ int insnbuf[POWERPC32_PLT_STUB_LEN]; |
+ struct gdbarch *gdbarch = get_frame_arch (frame); |
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); |
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); |
+ CORE_ADDR target = 0; |
+ |
+ if (insns_match_pattern (pc, powerpc32_plt_stub, insnbuf)) |
+ { |
+ /* Insn pattern is |
+ lis r11, xxxx |
+ lwz r11, xxxx(r11) |
+ Branch target is in r11. */ |
+ |
+ target = (insn_d_field (insnbuf[0]) << 16) | insn_d_field (insnbuf[1]); |
+ target = read_memory_unsigned_integer (target, 4, byte_order); |
+ } |
+ |
+ if (insns_match_pattern (pc, powerpc32_plt_stub_so, insnbuf)) |
+ { |
+ /* Insn pattern is |
+ lwz r11, xxxx(r30) |
+ Branch target is in r11. */ |
+ |
+ target = get_frame_register_unsigned (frame, tdep->ppc_gp0_regnum + 30) |
+ + insn_d_field (insnbuf[0]); |
+ target = read_memory_unsigned_integer (target, 4, byte_order); |
+ } |
+ |
+ return target; |
+} |
/* Given that we've begun executing a call trampoline at PC, return |
the entry point of the function the trampoline will go to. */ |
@@ -1192,6 +1284,75 @@ ppc_linux_core_read_description (struct gdbarch *gdbarch, |
} |
} |
+/* Implementation of `gdbarch_stap_is_single_operand', as defined in |
+ gdbarch.h. */ |
+ |
+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. */ |
+} |
+ |
+/* Implementation of `gdbarch_stap_parse_special_token', as defined in |
+ gdbarch.h. */ |
+ |
+static int |
+ppc_stap_parse_special_token (struct gdbarch *gdbarch, |
+ struct stap_parse_info *p) |
+{ |
+ if (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 |
+ we would not need to do anything. */ |
+ const char *s = p->arg; |
+ char *regname; |
+ int len; |
+ struct stoken str; |
+ |
+ while (isdigit (*s)) |
+ ++s; |
+ |
+ if (*s == '(') |
+ { |
+ /* It is a register displacement indeed. Returning 0 means we are |
+ deferring the treatment of this case to the generic parser. */ |
+ return 0; |
+ } |
+ |
+ len = s - p->arg; |
+ regname = alloca (len + 2); |
+ regname[0] = 'r'; |
+ |
+ strncpy (regname + 1, p->arg, len); |
+ ++len; |
+ regname[len] = '\0'; |
+ |
+ if (user_reg_map_name_to_regnum (gdbarch, regname, len) == -1) |
+ error (_("Invalid register name `%s' on expression `%s'."), |
+ regname, p->saved_arg); |
+ |
+ write_exp_elt_opcode (OP_REGISTER); |
+ str.ptr = regname; |
+ str.length = len; |
+ write_exp_string (str); |
+ write_exp_elt_opcode (OP_REGISTER); |
+ |
+ p->arg = s; |
+ } |
+ else |
+ { |
+ /* All the other tokens should be handled correctly by the generic |
+ parser. */ |
+ return 0; |
+ } |
+ |
+ return 1; |
+} |
/* Cell/B.E. active SPE context tracking support. */ |
@@ -1509,6 +1670,15 @@ ppc_linux_init_abi (struct gdbarch_info info, |
/* Get the syscall number from the arch's register. */ |
set_gdbarch_get_syscall_number (gdbarch, ppc_linux_get_syscall_number); |
+ /* SystemTap functions. */ |
+ set_gdbarch_stap_integer_prefix (gdbarch, "i"); |
+ set_gdbarch_stap_register_indirection_prefix (gdbarch, "("); |
+ set_gdbarch_stap_register_indirection_suffix (gdbarch, ")"); |
+ set_gdbarch_stap_gdb_register_prefix (gdbarch, "r"); |
+ set_gdbarch_stap_is_single_operand (gdbarch, ppc_stap_is_single_operand); |
+ set_gdbarch_stap_parse_special_token (gdbarch, |
+ ppc_stap_parse_special_token); |
+ |
if (tdep->wordsize == 4) |
{ |
/* Until November 2001, gcc did not comply with the 32 bit SysV |
@@ -1524,7 +1694,7 @@ ppc_linux_init_abi (struct gdbarch_info info, |
ppc_linux_memory_remove_breakpoint); |
/* Shared library handling. */ |
- set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target); |
+ set_gdbarch_skip_trampoline_code (gdbarch, ppc_skip_trampoline_code); |
set_solib_svr4_fetch_link_map_offsets |
(gdbarch, svr4_ilp32_fetch_link_map_offsets); |
@@ -1555,6 +1725,17 @@ ppc_linux_init_abi (struct gdbarch_info info, |
else |
set_gdbarch_core_regset_sections (gdbarch, |
ppc_linux_fp_regset_sections); |
+ |
+ 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_solib_ops (gdbarch, &powerpc_so_ops); |
+ |
+ set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver); |
} |
if (tdep->wordsize == 8) |
@@ -1642,6 +1823,8 @@ ppc_linux_init_abi (struct gdbarch_info info, |
set_gdbarch_displaced_step_location (gdbarch, |
ppc_linux_displaced_step_location); |
} |
+ |
+ set_gdbarch_get_siginfo_type (gdbarch, linux_get_siginfo_type); |
} |
/* Provide a prototype to silence -Wmissing-prototypes. */ |