/* Target-dependent code for GDB, the GNU debugger.
- Copyright 1986, 1987, 1989, 1991, 1992, 1993, 1994, 1995, 1996,
- 1997, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+ Copyright (C) 1986, 1987, 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
+ 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+ Free Software Foundation, Inc.
This file is part of GDB.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA. */
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
#include "defs.h"
#include "frame.h"
#include "ppc-tdep.h"
#include "trad-frame.h"
#include "frame-unwind.h"
+#include "tramp-frame.h"
/* The following instructions are used in the signal trampoline code
on GNU/Linux PPC. The kernel used to use magic syscalls 0x6666 and
/* Determine if pc is in a signal trampoline...
Ha! That's not what this does at all. wait_for_inferior in
- infrun.c calls DEPRECATED_PC_IN_SIGTRAMP in order to detect entry
- into a signal trampoline just after delivery of a signal. But on
+ infrun.c calls get_frame_type() in order to detect entry into a
+ signal trampoline just after delivery of a signal. But on
GNU/Linux, signal trampolines are used for the return path only.
The kernel sets things up so that the signal handler is called
directly.
signal is delivered while stepping, the next instruction that
would've been stepped over isn't, instead a signal is delivered and
the first instruction of the handler is stepped over instead. That
- puts us on the second instruction. (I added the test for the
- first instruction long after the fact, just in case the observed
- behavior is ever fixed.)
-
- DEPRECATED_PC_IN_SIGTRAMP is called from blockframe.c as well in
- order to set the frame's type (if a SIGTRAMP_FRAME). Because of
- our strange definition of in_sigtramp below, we can't rely on the
- frame's type getting set correctly from within blockframe.c. This
- is why we take pains to set it in init_extra_frame_info().
-
- NOTE: cagney/2002-11-10: I suspect the real problem here is that
- the get_prev_frame() only initializes the frame's type after the
- call to INIT_FRAME_INFO. get_prev_frame() should be fixed, this
- code shouldn't be working its way around a bug :-(. */
+ puts us on the second instruction. (I added the test for the first
+ instruction long after the fact, just in case the observed behavior
+ is ever fixed.) */
int
ppc_linux_in_sigtramp (CORE_ADDR pc, char *func_name)
CORE_ADDR lr;
CORE_ADDR sp;
CORE_ADDR tramp_sp;
- char buf[4];
+ gdb_byte buf[4];
CORE_ADDR handler;
lr = read_register (gdbarch_tdep (current_gdbarch)->ppc_lr_regnum);
static int
ppc_linux_at_sigtramp_return_path (CORE_ADDR pc)
{
- char buf[12];
+ gdb_byte buf[12];
unsigned long pcinsn;
if (target_read_memory (pc - 4, buf, sizeof (buf)) != 0)
return 0;
static CORE_ADDR
ppc_linux_skip_trampoline_code (CORE_ADDR pc)
{
- char buf[4];
+ gdb_byte buf[4];
struct obj_section *sect;
struct objfile *objfile;
unsigned long insn;
/* Fetch the string; we don't know how long it is. Is it possible
that the following will fail because we're trying to fetch too
much? */
- if (target_read_memory (strtab + symidx, symname, sizeof (symname)) != 0)
+ if (target_read_memory (strtab + symidx, (gdb_byte *) symname,
+ sizeof (symname)) != 0)
return 0;
/* This might not work right if we have multiple symbols with the
regard to removing breakpoints in some potentially self modifying
code. */
int
-ppc_linux_memory_remove_breakpoint (CORE_ADDR addr, char *contents_cache)
+ppc_linux_memory_remove_breakpoint (struct bp_target_info *bp_tgt)
{
+ CORE_ADDR addr = bp_tgt->placed_address;
const unsigned char *bp;
int val;
int bplen;
- char old_contents[BREAKPOINT_MAX];
+ gdb_byte old_contents[BREAKPOINT_MAX];
/* Determine appropriate breakpoint contents and size for this address. */
bp = BREAKPOINT_FROM_PC (&addr, &bplen);
if (bp == NULL)
- error ("Software breakpoints not implemented for this target.");
+ error (_("Software breakpoints not implemented for this target."));
val = target_read_memory (addr, old_contents, bplen);
program modified the code on us, so it is wrong to put back the
old value */
if (val == 0 && memcmp (bp, old_contents, bplen) == 0)
- val = target_write_memory (addr, contents_cache, bplen);
+ val = target_write_memory (addr, bp_tgt->shadow_contents, bplen);
return val;
}
static enum return_value_convention
ppc_linux_return_value (struct gdbarch *gdbarch, struct type *valtype,
- struct regcache *regcache, void *readbuf,
- const void *writebuf)
+ struct regcache *regcache, gdb_byte *readbuf,
+ const gdb_byte *writebuf)
{
if ((TYPE_CODE (valtype) == TYPE_CODE_STRUCT
|| TYPE_CODE (valtype) == TYPE_CODE_UNION)
writebuf);
}
-/* Fetch (and possibly build) an appropriate link_map_offsets
- structure for GNU/Linux PPC targets using the struct offsets
- defined in link.h (but without actual reference to that file).
-
- This makes it possible to access GNU/Linux PPC shared libraries
- from a GDB that was not built on an GNU/Linux PPC host (for cross
- debugging). */
-
-struct link_map_offsets *
-ppc_linux_svr4_fetch_link_map_offsets (void)
-{
- static struct link_map_offsets lmo;
- static struct link_map_offsets *lmp = NULL;
-
- if (lmp == NULL)
- {
- lmp = &lmo;
-
- lmo.r_debug_size = 8; /* The actual size is 20 bytes, but
- this is all we need. */
- lmo.r_map_offset = 4;
- lmo.r_map_size = 4;
-
- lmo.link_map_size = 20; /* The actual size is 560 bytes, but
- this is all we need. */
- lmo.l_addr_offset = 0;
- lmo.l_addr_size = 4;
-
- lmo.l_name_offset = 4;
- lmo.l_name_size = 4;
-
- lmo.l_next_offset = 12;
- lmo.l_next_size = 4;
-
- lmo.l_prev_offset = 16;
- lmo.l_prev_size = 4;
- }
-
- return lmp;
-}
-
-
/* Macros for matching instructions. Note that, since all the
operands are masked off before they're or-ed into the instruction,
you can use -1 to make masks. */
#define PPC64_STANDARD_LINKAGE_LEN \
(sizeof (ppc64_standard_linkage) / sizeof (ppc64_standard_linkage[0]))
-
-/* Recognize a 64-bit PowerPC GNU/Linux linkage function --- what GDB
- calls a "solib trampoline". */
-static int
-ppc64_in_solib_call_trampoline (CORE_ADDR pc, char *name)
-{
- /* Detecting solib call trampolines on PPC64 GNU/Linux is a pain.
-
- It's not specifically solib call trampolines that are the issue.
- Any call from one function to another function that uses a
- different TOC requires a trampoline, to save the caller's TOC
- pointer and then load the callee's TOC. An executable or shared
- library may have more than one TOC, so even intra-object calls
- may require a trampoline. Since executable and shared libraries
- will all have their own distinct TOCs, every inter-object call is
- also an inter-TOC call, and requires a trampoline --- so "solib
- call trampolines" are just a special case.
-
- The 64-bit PowerPC GNU/Linux ABI calls these call trampolines
- "linkage functions". Since they need to be near the functions
- that call them, they all appear in .text, not in any special
- section. The .plt section just contains an array of function
- descriptors, from which the linkage functions load the callee's
- entry point, TOC value, and environment pointer. So
- in_plt_section is useless. The linkage functions don't have any
- special linker symbols to name them, either.
-
- The only way I can see to recognize them is to actually look at
- their code. They're generated by ppc_build_one_stub and some
- other functions in bfd/elf64-ppc.c, so that should show us all
- the instruction sequences we need to recognize. */
- unsigned int insn[PPC64_STANDARD_LINKAGE_LEN];
-
- return insns_match_pattern (pc, ppc64_standard_linkage, insn);
-}
-
-
/* When the dynamic linker is doing lazy symbol resolution, the first
call to a function in another object will go like this:
const bfd_byte *buf)
{
regcache_raw_supply (regcache, regnum,
- (buf + wordsize
- - register_size (current_gdbarch, regnum)));
+ (buf + wordsize - register_size (current_gdbarch, regnum)));
}
/* Extract the register values found in the WORDSIZED ABI GREGSET,
struct gdbarch_tdep *regcache_tdep = gdbarch_tdep (regcache_arch);
const bfd_byte *buf = gregs;
- for (regi = 0; regi < 32; regi++)
- right_supply_register (regcache, wordsize, regi, buf + wordsize * regi);
+ for (regi = 0; regi < ppc_num_gprs; regi++)
+ right_supply_register (regcache, wordsize,
+ regcache_tdep->ppc_gp0_regnum + regi,
+ buf + wordsize * regi);
right_supply_register (regcache, wordsize, gdbarch_pc_regnum (regcache_arch),
buf + wordsize * PPC_LINUX_PT_NIP);
NULL, ppc32_linux_supply_gregset
};
-struct ppc_linux_sigtramp_cache
-{
- CORE_ADDR base;
- struct trad_frame_saved_reg *saved_regs;
-};
-
-static struct ppc_linux_sigtramp_cache *
-ppc_linux_sigtramp_cache (struct frame_info *next_frame, void **this_cache)
-{
- CORE_ADDR regs;
- CORE_ADDR gpregs;
- CORE_ADDR fpregs;
- int i;
- struct ppc_linux_sigtramp_cache *cache;
- struct gdbarch *gdbarch = get_frame_arch (next_frame);
- struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
-
- if ((*this_cache) != NULL)
- return (*this_cache);
- cache = FRAME_OBSTACK_ZALLOC (struct ppc_linux_sigtramp_cache);
- (*this_cache) = cache;
- cache->saved_regs = trad_frame_alloc_saved_regs (next_frame);
-
- cache->base = frame_unwind_register_unsigned (next_frame, SP_REGNUM);
-
- /* Find the register pointer, which gives the address of the
- register buffers. */
- if (tdep->wordsize == 4)
- regs = (cache->base
- + 0xd0 /* Offset to ucontext_t. */
- + 0x30 /* Offset to .reg. */);
- else
- regs = (cache->base
- + 0x80 /* Offset to ucontext_t. */
- + 0xe0 /* Offset to .reg. */);
- /* And the corresponding register buffers. */
- gpregs = read_memory_unsigned_integer (regs, tdep->wordsize);
- fpregs = gpregs + 48 * tdep->wordsize;
-
- /* General purpose. */
- for (i = 0; i < 32; i++)
- {
- int regnum = i + tdep->ppc_gp0_regnum;
- cache->saved_regs[regnum].addr = gpregs + i * tdep->wordsize;
- }
- cache->saved_regs[PC_REGNUM].addr = gpregs + 32 * tdep->wordsize;
- cache->saved_regs[tdep->ppc_ctr_regnum].addr = gpregs + 35 * tdep->wordsize;
- cache->saved_regs[tdep->ppc_lr_regnum].addr = gpregs + 36 * tdep->wordsize;
- cache->saved_regs[tdep->ppc_xer_regnum].addr = gpregs + 37 * tdep->wordsize;
- cache->saved_regs[tdep->ppc_cr_regnum].addr = gpregs + 38 * tdep->wordsize;
-
- /* Floating point registers. */
- for (i = 0; i < 32; i++)
- {
- int regnum = i + FP0_REGNUM;
- cache->saved_regs[regnum].addr = fpregs + i * tdep->wordsize;
- }
- cache->saved_regs[tdep->ppc_fpscr_regnum].addr = fpregs + 32 * tdep->wordsize;
-
- return cache;
-}
-
-static void
-ppc_linux_sigtramp_this_id (struct frame_info *next_frame, void **this_cache,
- struct frame_id *this_id)
-{
- struct ppc_linux_sigtramp_cache *info
- = ppc_linux_sigtramp_cache (next_frame, this_cache);
- (*this_id) = frame_id_build (info->base, frame_pc_unwind (next_frame));
-}
-
-static void
-ppc_linux_sigtramp_prev_register (struct frame_info *next_frame,
- void **this_cache,
- int regnum, int *optimizedp,
- enum lval_type *lvalp, CORE_ADDR *addrp,
- int *realnump, void *valuep)
-{
- struct ppc_linux_sigtramp_cache *info
- = ppc_linux_sigtramp_cache (next_frame, this_cache);
- trad_frame_prev_register (next_frame, info->saved_regs, regnum,
- optimizedp, lvalp, addrp, realnump, valuep);
-}
-
-static const struct frame_unwind ppc_linux_sigtramp_unwind =
-{
- SIGTRAMP_FRAME,
- ppc_linux_sigtramp_this_id,
- ppc_linux_sigtramp_prev_register
-};
-
-static const struct frame_unwind *
-ppc_linux_sigtramp_sniffer (struct frame_info *next_frame)
-{
- struct gdbarch_tdep *tdep = gdbarch_tdep (get_frame_arch (next_frame));
- if (frame_pc_unwind (next_frame)
- > frame_unwind_register_unsigned (next_frame, SP_REGNUM))
- /* Assume anything that is vaguely on the stack is a signal
- trampoline. */
- return &ppc_linux_sigtramp_unwind;
- else
- return NULL;
-}
-
static void
ppc64_linux_supply_gregset (const struct regset *regset,
struct regcache * regcache,
struct gdbarch_tdep *regcache_tdep = gdbarch_tdep (regcache_arch);
const bfd_byte *buf = fpset;
- for (regi = 0; regi < 32; regi++)
- regcache_raw_supply (regcache, FP0_REGNUM + regi, buf + 8 * regi);
+ if (! ppc_floating_point_unit_p (regcache_arch))
+ return;
- /* The FPSCR is stored in the low order word of the last doubleword in the
- fpregset. */
+ for (regi = 0; regi < ppc_num_fprs; regi++)
+ regcache_raw_supply (regcache,
+ regcache_tdep->ppc_fp0_regnum + regi,
+ buf + 8 * regi);
+
+ /* The FPSCR is stored in the low order word of the last
+ doubleword in the fpregset. */
regcache_raw_supply (regcache, regcache_tdep->ppc_fpscr_regnum,
- buf + 8 * 32 + 4);
+ buf + 8 * 32 + 4);
}
static struct regset ppc_linux_fpregset = { NULL, ppc_linux_supply_fpregset };
return NULL;
}
+static void
+ppc_linux_sigtramp_cache (struct frame_info *next_frame,
+ struct trad_frame_cache *this_cache,
+ CORE_ADDR func, LONGEST offset,
+ int bias)
+{
+ CORE_ADDR base;
+ CORE_ADDR regs;
+ CORE_ADDR gpregs;
+ CORE_ADDR fpregs;
+ int i;
+ struct gdbarch *gdbarch = get_frame_arch (next_frame);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ base = frame_unwind_register_unsigned (next_frame, SP_REGNUM);
+ if (bias > 0 && frame_pc_unwind (next_frame) != func)
+ /* See below, some signal trampolines increment the stack as their
+ first instruction, need to compensate for that. */
+ base -= bias;
+
+ /* Find the address of the register buffer pointer. */
+ regs = base + offset;
+ /* Use that to find the address of the corresponding register
+ buffers. */
+ gpregs = read_memory_unsigned_integer (regs, tdep->wordsize);
+ fpregs = gpregs + 48 * tdep->wordsize;
+
+ /* General purpose. */
+ for (i = 0; i < 32; i++)
+ {
+ int regnum = i + tdep->ppc_gp0_regnum;
+ trad_frame_set_reg_addr (this_cache, regnum, gpregs + i * tdep->wordsize);
+ }
+ trad_frame_set_reg_addr (this_cache, PC_REGNUM, gpregs + 32 * tdep->wordsize);
+ trad_frame_set_reg_addr (this_cache, tdep->ppc_ctr_regnum,
+ gpregs + 35 * tdep->wordsize);
+ trad_frame_set_reg_addr (this_cache, tdep->ppc_lr_regnum,
+ gpregs + 36 * tdep->wordsize);
+ trad_frame_set_reg_addr (this_cache, tdep->ppc_xer_regnum,
+ gpregs + 37 * tdep->wordsize);
+ trad_frame_set_reg_addr (this_cache, tdep->ppc_cr_regnum,
+ gpregs + 38 * tdep->wordsize);
+
+ if (ppc_floating_point_unit_p (gdbarch))
+ {
+ /* Floating point registers. */
+ for (i = 0; i < 32; i++)
+ {
+ int regnum = i + FP0_REGNUM;
+ trad_frame_set_reg_addr (this_cache, regnum,
+ fpregs + i * tdep->wordsize);
+ }
+ trad_frame_set_reg_addr (this_cache, tdep->ppc_fpscr_regnum,
+ fpregs + 32 * tdep->wordsize);
+ }
+ trad_frame_set_id (this_cache, frame_id_build (base, func));
+}
+
+static void
+ppc32_linux_sigaction_cache_init (const struct tramp_frame *self,
+ struct frame_info *next_frame,
+ struct trad_frame_cache *this_cache,
+ CORE_ADDR func)
+{
+ ppc_linux_sigtramp_cache (next_frame, this_cache, func,
+ 0xd0 /* Offset to ucontext_t. */
+ + 0x30 /* Offset to .reg. */,
+ 0);
+}
+
+static void
+ppc64_linux_sigaction_cache_init (const struct tramp_frame *self,
+ struct frame_info *next_frame,
+ struct trad_frame_cache *this_cache,
+ CORE_ADDR func)
+{
+ ppc_linux_sigtramp_cache (next_frame, this_cache, func,
+ 0x80 /* Offset to ucontext_t. */
+ + 0xe0 /* Offset to .reg. */,
+ 128);
+}
+
+static void
+ppc32_linux_sighandler_cache_init (const struct tramp_frame *self,
+ struct frame_info *next_frame,
+ struct trad_frame_cache *this_cache,
+ CORE_ADDR func)
+{
+ ppc_linux_sigtramp_cache (next_frame, this_cache, func,
+ 0x40 /* Offset to ucontext_t. */
+ + 0x1c /* Offset to .reg. */,
+ 0);
+}
+
+static void
+ppc64_linux_sighandler_cache_init (const struct tramp_frame *self,
+ struct frame_info *next_frame,
+ struct trad_frame_cache *this_cache,
+ CORE_ADDR func)
+{
+ ppc_linux_sigtramp_cache (next_frame, this_cache, func,
+ 0x80 /* Offset to struct sigcontext. */
+ + 0x38 /* Offset to .reg. */,
+ 128);
+}
+
+static struct tramp_frame ppc32_linux_sigaction_tramp_frame = {
+ SIGTRAMP_FRAME,
+ 4,
+ {
+ { 0x380000ac, -1 }, /* li r0, 172 */
+ { 0x44000002, -1 }, /* sc */
+ { TRAMP_SENTINEL_INSN },
+ },
+ ppc32_linux_sigaction_cache_init
+};
+static struct tramp_frame ppc64_linux_sigaction_tramp_frame = {
+ SIGTRAMP_FRAME,
+ 4,
+ {
+ { 0x38210080, -1 }, /* addi r1,r1,128 */
+ { 0x380000ac, -1 }, /* li r0, 172 */
+ { 0x44000002, -1 }, /* sc */
+ { TRAMP_SENTINEL_INSN },
+ },
+ ppc64_linux_sigaction_cache_init
+};
+static struct tramp_frame ppc32_linux_sighandler_tramp_frame = {
+ SIGTRAMP_FRAME,
+ 4,
+ {
+ { 0x38000077, -1 }, /* li r0,119 */
+ { 0x44000002, -1 }, /* sc */
+ { TRAMP_SENTINEL_INSN },
+ },
+ ppc32_linux_sighandler_cache_init
+};
+static struct tramp_frame ppc64_linux_sighandler_tramp_frame = {
+ SIGTRAMP_FRAME,
+ 4,
+ {
+ { 0x38210080, -1 }, /* addi r1,r1,128 */
+ { 0x38000077, -1 }, /* li r0,119 */
+ { 0x44000002, -1 }, /* sc */
+ { TRAMP_SENTINEL_INSN },
+ },
+ ppc64_linux_sighandler_cache_init
+};
+
static void
ppc_linux_init_abi (struct gdbarch_info info,
struct gdbarch *gdbarch)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ /* NOTE: jimb/2004-03-26: The System V ABI PowerPC Processor
+ Supplement says that long doubles are sixteen bytes long.
+ However, as one of the known warts of its ABI, PPC GNU/Linux uses
+ eight-byte long doubles. GCC only recently got 128-bit long
+ double support on PPC, so it may be changing soon. The
+ Linux[sic] Standards Base says that programs that use 'long
+ double' on PPC GNU/Linux are non-conformant. */
+ /* NOTE: cagney/2005-01-25: True for both 32- and 64-bit. */
+ set_gdbarch_long_double_bit (gdbarch, 8 * TARGET_CHAR_BIT);
+
if (tdep->wordsize == 4)
{
- /* NOTE: jimb/2004-03-26: The System V ABI PowerPC Processor
- Supplement says that long doubles are sixteen bytes long.
- However, as one of the known warts of its ABI, PPC GNU/Linux
- uses eight-byte long doubles. GCC only recently got 128-bit
- long double support on PPC, so it may be changing soon. The
- Linux Standards Base says that programs that use 'long
- double' on PPC GNU/Linux are non-conformant. */
- set_gdbarch_long_double_bit (gdbarch, 8 * TARGET_CHAR_BIT);
-
/* Until November 2001, gcc did not comply with the 32 bit SysV
R4 ABI requirement that structures less than or equal to 8
bytes should be returned in registers. Instead GCC was using
ppc_linux_memory_remove_breakpoint);
/* Shared library handling. */
- set_gdbarch_in_solib_call_trampoline (gdbarch, in_plt_section);
set_gdbarch_skip_trampoline_code (gdbarch,
ppc_linux_skip_trampoline_code);
set_solib_svr4_fetch_link_map_offsets
- (gdbarch, ppc_linux_svr4_fetch_link_map_offsets);
+ (gdbarch, svr4_ilp32_fetch_link_map_offsets);
+
+ /* Trampolines. */
+ tramp_frame_prepend_unwinder (gdbarch, &ppc32_linux_sigaction_tramp_frame);
+ tramp_frame_prepend_unwinder (gdbarch, &ppc32_linux_sighandler_tramp_frame);
}
if (tdep->wordsize == 8)
function descriptors). */
set_gdbarch_convert_from_func_ptr_addr
(gdbarch, ppc64_linux_convert_from_func_ptr_addr);
-
- set_gdbarch_in_solib_call_trampoline
- (gdbarch, ppc64_in_solib_call_trampoline);
set_gdbarch_skip_trampoline_code (gdbarch, ppc64_skip_trampoline_code);
- /* PPC64 malloc's entry-point is called ".malloc". */
- set_gdbarch_name_of_malloc (gdbarch, ".malloc");
+ /* Shared library handling. */
+ set_solib_svr4_fetch_link_map_offsets
+ (gdbarch, svr4_lp64_fetch_link_map_offsets);
+
+ /* Trampolines. */
+ tramp_frame_prepend_unwinder (gdbarch, &ppc64_linux_sigaction_tramp_frame);
+ tramp_frame_prepend_unwinder (gdbarch, &ppc64_linux_sighandler_tramp_frame);
}
set_gdbarch_regset_from_core_section (gdbarch, ppc_linux_regset_from_core_section);
- frame_unwind_append_sniffer (gdbarch, ppc_linux_sigtramp_sniffer);
+
+ /* Enable TLS support. */
+ set_gdbarch_fetch_tls_load_module_address (gdbarch,
+ svr4_fetch_objfile_link_map);
}
void