aboutsummaryrefslogtreecommitdiff
path: root/gdb/ppc-linux-tdep.c
diff options
context:
space:
mode:
authorMichael Eager <eager@eagercon.com>2012-01-30 17:09:40 +0000
committerMichael Eager <eager@eagercon.com>2012-01-30 17:09:40 +0000
commit5d853008e8e605f547c0eada69359d483099e73e (patch)
tree475c408f7ac371ba4a53aff19db93ec5c56d765b /gdb/ppc-linux-tdep.c
parentf0d1a53ee3440a219c4d8bc003bbb81c8ec5e3d2 (diff)
downloadgdb-5d853008e8e605f547c0eada69359d483099e73e.zip
gdb-5d853008e8e605f547c0eada69359d483099e73e.tar.gz
gdb-5d853008e8e605f547c0eada69359d483099e73e.tar.bz2
Support stepping through PPC PLT with securePLT.
* configure.tgt (powerpc-*-linux*): Add glibc-tdep.o. * ppc-linux-tdep.c: Include glibc-tdep.h. (powerpc32_plt_stub, powerpc32_plt_stub_so): Add PLT stub templates. (powerpc_linux_in_plt_stub): New function. (powerpc_linux_in_dynsym_resolve_code): New function. (ppc_skip_trampoline_code): New function. (ppc_linux_init_abi): Use PPC specific functions rather than generic. Use glibc_skip_solib_resolver.
Diffstat (limited to 'gdb/ppc-linux-tdep.c')
-rw-r--r--gdb/ppc-linux-tdep.c97
1 files changed, 96 insertions, 1 deletions
diff --git a/gdb/ppc-linux-tdep.c b/gdb/ppc-linux-tdep.c
index b6470fe..b94dea2 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"
@@ -65,6 +66,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"
@@ -599,6 +603,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 objfile *objfile;
+ 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 ((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. */
@@ -1524,7 +1608,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 +1639,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)