/* Target-dependent code for GDB, the GNU debugger.
Copyright (C) 1986, 1987, 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
- 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+ 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
Free Software Foundation, Inc.
This file is part of GDB.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
GNU General Public License for more details.
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., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "defs.h"
#include "frame.h"
#include "frame-unwind.h"
#include "tramp-frame.h"
-/* From <asm/ptrace.h>, values for PT_NIP, PT_R1, and PT_LNK */
-#define PPC_LINUX_PT_R0 0
-#define PPC_LINUX_PT_R1 1
-#define PPC_LINUX_PT_R2 2
-#define PPC_LINUX_PT_R3 3
-#define PPC_LINUX_PT_R4 4
-#define PPC_LINUX_PT_R5 5
-#define PPC_LINUX_PT_R6 6
-#define PPC_LINUX_PT_R7 7
-#define PPC_LINUX_PT_R8 8
-#define PPC_LINUX_PT_R9 9
-#define PPC_LINUX_PT_R10 10
-#define PPC_LINUX_PT_R11 11
-#define PPC_LINUX_PT_R12 12
-#define PPC_LINUX_PT_R13 13
-#define PPC_LINUX_PT_R14 14
-#define PPC_LINUX_PT_R15 15
-#define PPC_LINUX_PT_R16 16
-#define PPC_LINUX_PT_R17 17
-#define PPC_LINUX_PT_R18 18
-#define PPC_LINUX_PT_R19 19
-#define PPC_LINUX_PT_R20 20
-#define PPC_LINUX_PT_R21 21
-#define PPC_LINUX_PT_R22 22
-#define PPC_LINUX_PT_R23 23
-#define PPC_LINUX_PT_R24 24
-#define PPC_LINUX_PT_R25 25
-#define PPC_LINUX_PT_R26 26
-#define PPC_LINUX_PT_R27 27
-#define PPC_LINUX_PT_R28 28
-#define PPC_LINUX_PT_R29 29
-#define PPC_LINUX_PT_R30 30
-#define PPC_LINUX_PT_R31 31
-#define PPC_LINUX_PT_NIP 32
-#define PPC_LINUX_PT_MSR 33
-#define PPC_LINUX_PT_CTR 35
-#define PPC_LINUX_PT_LNK 36
-#define PPC_LINUX_PT_XER 37
-#define PPC_LINUX_PT_CCR 38
-#define PPC_LINUX_PT_MQ 39
-#define PPC_LINUX_PT_FPR0 48 /* each FP reg occupies 2 slots in this space */
-#define PPC_LINUX_PT_FPR31 (PPC_LINUX_PT_FPR0 + 2*31)
-#define PPC_LINUX_PT_FPSCR (PPC_LINUX_PT_FPR0 + 2*32 + 1)
-
-
static CORE_ADDR
-ppc_linux_skip_trampoline_code (CORE_ADDR pc)
+ppc_linux_skip_trampoline_code (struct frame_info *frame, CORE_ADDR pc)
{
gdb_byte buf[4];
struct obj_section *sect;
char symname[1024];
struct minimal_symbol *msymbol;
- /* Find the section pc is in; return if not in .plt */
+ /* Find the section pc is in; if not in .plt, try the default method. */
sect = find_pc_section (pc);
if (!sect || strcmp (sect->the_bfd_section->name, ".plt") != 0)
- return 0;
+ return find_solib_trampoline_target (frame, pc);
objfile = sect->objfile;
standard linkage function will send them. (This doesn't deal with
dynamic linker lazy symbol resolution stubs.) */
static CORE_ADDR
-ppc64_standard_linkage_target (CORE_ADDR pc, unsigned int *insn)
+ppc64_standard_linkage_target (struct frame_info *frame,
+ CORE_ADDR pc, unsigned int *insn)
{
- struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (get_frame_arch (frame));
/* The address of the function descriptor this linkage function
references. */
CORE_ADDR desc
- = ((CORE_ADDR) read_register (tdep->ppc_gp0_regnum + 2)
+ = ((CORE_ADDR) get_frame_register_unsigned (frame,
+ tdep->ppc_gp0_regnum + 2)
+ (insn_d_field (insn[0]) << 16)
+ insn_ds_field (insn[2]));
/* Given that we've begun executing a call trampoline at PC, return
the entry point of the function the trampoline will go to. */
static CORE_ADDR
-ppc64_skip_trampoline_code (CORE_ADDR pc)
+ppc64_skip_trampoline_code (struct frame_info *frame, CORE_ADDR pc)
{
unsigned int ppc64_standard_linkage_insn[PPC64_STANDARD_LINKAGE_LEN];
if (insns_match_pattern (pc, ppc64_standard_linkage,
ppc64_standard_linkage_insn))
- return ppc64_standard_linkage_target (pc, ppc64_standard_linkage_insn);
+ return ppc64_standard_linkage_target (frame, pc,
+ ppc64_standard_linkage_insn);
else
return 0;
}
-/* Support for CONVERT_FROM_FUNC_PTR_ADDR (ARCH, ADDR, TARG) on PPC64
+/* Support for convert_from_func_ptr_addr (ARCH, ADDR, TARG) on PPC
GNU/Linux.
Usually a function pointer's representation is simply the address
- of the function. On GNU/Linux on the 64-bit PowerPC however, a
- function pointer is represented by a pointer to a TOC entry. This
- TOC entry contains three words, the first word is the address of
- the function, the second word is the TOC pointer (r2), and the
- third word is the static chain value. Throughout GDB it is
- currently assumed that a function pointer contains the address of
- the function, which is not easy to fix. In addition, the
+ of the function. On GNU/Linux on the PowerPC however, a function
+ pointer may be a pointer to a function descriptor.
+
+ For PPC64, a function descriptor is a TOC entry, in a data section,
+ which contains three words: the first word is the address of the
+ function, the second word is the TOC pointer (r2), and the third word
+ is the static chain value.
+
+ For PPC32, there are two kinds of function pointers: non-secure and
+ secure. Non-secure function pointers point directly to the
+ function in a code section and thus need no translation. Secure
+ ones (from GCC's -msecure-plt option) are in a data section and
+ contain one word: the address of the function.
+
+ Throughout GDB it is currently assumed that a function pointer contains
+ the address of the function, which is not easy to fix. In addition, the
conversion of a function address to a function pointer would
require allocation of a TOC entry in the inferior's memory space,
with all its drawbacks. To be able to call C++ virtual methods in
the inferior (which are called via function pointers),
find_function_addr uses this function to get the function address
- from a function pointer. */
+ from a function pointer.
-/* If ADDR points at what is clearly a function descriptor, transform
- it into the address of the corresponding function. Be
- conservative, otherwize GDB will do the transformation on any
- random addresses such as occures when there is no symbol table. */
+ If ADDR points at what is clearly a function descriptor, transform
+ it into the address of the corresponding function, if needed. Be
+ conservative, otherwise GDB will do the transformation on any
+ random addresses such as occur when there is no symbol table. */
static CORE_ADDR
-ppc64_linux_convert_from_func_ptr_addr (struct gdbarch *gdbarch,
- CORE_ADDR addr,
- struct target_ops *targ)
+ppc_linux_convert_from_func_ptr_addr (struct gdbarch *gdbarch,
+ CORE_ADDR addr,
+ struct target_ops *targ)
{
+ struct gdbarch_tdep *tdep;
struct section_table *s = target_section_by_addr (targ, addr);
+ char *sect_name = NULL;
+
+ if (!s)
+ return addr;
+
+ tdep = gdbarch_tdep (gdbarch);
+
+ switch (tdep->wordsize)
+ {
+ case 4:
+ sect_name = ".plt";
+ break;
+ case 8:
+ sect_name = ".opd";
+ break;
+ default:
+ internal_error (__FILE__, __LINE__,
+ _("failed internal consistency check"));
+ }
/* Check if ADDR points to a function descriptor. */
- if (s && strcmp (s->the_bfd_section->name, ".opd") == 0)
- return get_target_memory_unsigned (targ, addr, 8);
+
+ /* NOTE: this depends on the coincidence that the address of a functions
+ entry point is contained in the first word of its function descriptor
+ for both PPC-64 and for PPC-32 with secure PLTs. */
+ if ((strcmp (s->the_bfd_section->name, sect_name) == 0)
+ && s->the_bfd_section->flags & SEC_DATA)
+ return get_target_memory_unsigned (targ, addr, tdep->wordsize);
return addr;
}
+/* This wrapper clears areas in the linux gregset not written by
+ ppc_collect_gregset. */
+
static void
-right_supply_register (struct regcache *regcache, int wordsize, int regnum,
- const bfd_byte *buf)
+ppc_linux_collect_gregset (const struct regset *regset,
+ const struct regcache *regcache,
+ int regnum, void *gregs, size_t len)
{
- regcache_raw_supply (regcache, regnum,
- (buf + wordsize - register_size (current_gdbarch, regnum)));
+ if (regnum == -1)
+ memset (gregs, 0, len);
+ ppc_collect_gregset (regset, regcache, regnum, gregs, len);
}
-/* Extract the register values found in the WORDSIZED ABI GREGSET,
- storing their values in REGCACHE. Note that some are left-aligned,
- while others are right aligned. */
+/* Regset descriptions. */
+static const struct ppc_reg_offsets ppc32_linux_reg_offsets =
+ {
+ /* General-purpose registers. */
+ /* .r0_offset = */ 0,
+ /* .gpr_size = */ 4,
+ /* .xr_size = */ 4,
+ /* .pc_offset = */ 128,
+ /* .ps_offset = */ 132,
+ /* .cr_offset = */ 152,
+ /* .lr_offset = */ 144,
+ /* .ctr_offset = */ 140,
+ /* .xer_offset = */ 148,
+ /* .mq_offset = */ 156,
+
+ /* Floating-point registers. */
+ /* .f0_offset = */ 0,
+ /* .fpscr_offset = */ 256,
+ /* .fpscr_size = */ 8,
+
+ /* AltiVec registers. */
+ /* .vr0_offset = */ 0,
+ /* .vscr_offset = */ 512 + 12,
+ /* .vrsave_offset = */ 528
+ };
-void
-ppc_linux_supply_gregset (struct regcache *regcache,
- int regnum, const void *gregs, size_t size,
- int wordsize)
-{
- int regi;
- struct gdbarch *regcache_arch = get_regcache_arch (regcache);
- struct gdbarch_tdep *regcache_tdep = gdbarch_tdep (regcache_arch);
- const bfd_byte *buf = gregs;
-
- 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);
- right_supply_register (regcache, wordsize, regcache_tdep->ppc_lr_regnum,
- buf + wordsize * PPC_LINUX_PT_LNK);
- regcache_raw_supply (regcache, regcache_tdep->ppc_cr_regnum,
- buf + wordsize * PPC_LINUX_PT_CCR);
- regcache_raw_supply (regcache, regcache_tdep->ppc_xer_regnum,
- buf + wordsize * PPC_LINUX_PT_XER);
- regcache_raw_supply (regcache, regcache_tdep->ppc_ctr_regnum,
- buf + wordsize * PPC_LINUX_PT_CTR);
- if (regcache_tdep->ppc_mq_regnum != -1)
- right_supply_register (regcache, wordsize, regcache_tdep->ppc_mq_regnum,
- buf + wordsize * PPC_LINUX_PT_MQ);
- right_supply_register (regcache, wordsize, regcache_tdep->ppc_ps_regnum,
- buf + wordsize * PPC_LINUX_PT_MSR);
-}
+static const struct ppc_reg_offsets ppc64_linux_reg_offsets =
+ {
+ /* General-purpose registers. */
+ /* .r0_offset = */ 0,
+ /* .gpr_size = */ 8,
+ /* .xr_size = */ 8,
+ /* .pc_offset = */ 256,
+ /* .ps_offset = */ 264,
+ /* .cr_offset = */ 304,
+ /* .lr_offset = */ 288,
+ /* .ctr_offset = */ 280,
+ /* .xer_offset = */ 296,
+ /* .mq_offset = */ 312,
+
+ /* Floating-point registers. */
+ /* .f0_offset = */ 0,
+ /* .fpscr_offset = */ 256,
+ /* .fpscr_size = */ 8,
+
+ /* AltiVec registers. */
+ /* .vr0_offset = */ 0,
+ /* .vscr_offset = */ 512 + 12,
+ /* .vrsave_offset = */ 528
+ };
-static void
-ppc32_linux_supply_gregset (const struct regset *regset,
- struct regcache *regcache,
- int regnum, const void *gregs, size_t size)
-{
- ppc_linux_supply_gregset (regcache, regnum, gregs, size, 4);
-}
+static const struct regset ppc32_linux_gregset = {
+ &ppc32_linux_reg_offsets,
+ ppc_supply_gregset,
+ ppc_linux_collect_gregset,
+ NULL
+};
-static struct regset ppc32_linux_gregset = {
- NULL, ppc32_linux_supply_gregset
+static const struct regset ppc64_linux_gregset = {
+ &ppc64_linux_reg_offsets,
+ ppc_supply_gregset,
+ ppc_linux_collect_gregset,
+ NULL
};
-static void
-ppc64_linux_supply_gregset (const struct regset *regset,
- struct regcache * regcache,
- int regnum, const void *gregs, size_t size)
-{
- ppc_linux_supply_gregset (regcache, regnum, gregs, size, 8);
-}
+static const struct regset ppc32_linux_fpregset = {
+ &ppc32_linux_reg_offsets,
+ ppc_supply_fpregset,
+ ppc_collect_fpregset,
+ NULL
+};
-static struct regset ppc64_linux_gregset = {
- NULL, ppc64_linux_supply_gregset
+static const struct regset ppc32_linux_vrregset = {
+ &ppc32_linux_reg_offsets,
+ ppc_supply_vrregset,
+ ppc_collect_vrregset,
+ NULL
};
-void
-ppc_linux_supply_fpregset (const struct regset *regset,
- struct regcache * regcache,
- int regnum, const void *fpset, size_t size)
+const struct regset *
+ppc_linux_gregset (int wordsize)
{
- int regi;
- struct gdbarch *regcache_arch = get_regcache_arch (regcache);
- struct gdbarch_tdep *regcache_tdep = gdbarch_tdep (regcache_arch);
- const bfd_byte *buf = fpset;
-
- if (! ppc_floating_point_unit_p (regcache_arch))
- return;
-
- 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);
+ return wordsize == 8 ? &ppc64_linux_gregset : &ppc32_linux_gregset;
}
-static struct regset ppc_linux_fpregset = { NULL, ppc_linux_supply_fpregset };
+const struct regset *
+ppc_linux_fpregset (void)
+{
+ return &ppc32_linux_fpregset;
+}
static const struct regset *
ppc_linux_regset_from_core_section (struct gdbarch *core_arch,
return &ppc64_linux_gregset;
}
if (strcmp (sect_name, ".reg2") == 0)
- return &ppc_linux_fpregset;
+ return &ppc32_linux_fpregset;
+ if (strcmp (sect_name, ".reg-ppc-vmx") == 0)
+ return &ppc32_linux_vrregset;
return NULL;
}
struct gdbarch *gdbarch = get_frame_arch (next_frame);
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
- base = frame_unwind_register_unsigned (next_frame, SP_REGNUM);
+ base = frame_unwind_register_unsigned (next_frame,
+ gdbarch_sp_regnum (gdbarch));
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. */
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,
+ gdbarch_pc_regnum (gdbarch),
+ 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,
/* Floating point registers. */
for (i = 0; i < 32; i++)
{
- int regnum = i + FP0_REGNUM;
+ int regnum = i + gdbarch_fp0_regnum (gdbarch);
trad_frame_set_reg_addr (this_cache, regnum,
fpregs + i * tdep->wordsize);
}
{
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);
+ /* PPC GNU/Linux uses either 64-bit or 128-bit long doubles; where
+ 128-bit, they are IBM long double, not IEEE quad long double as
+ in the System V ABI PowerPC Processor Supplement. We can safely
+ let them default to 128-bit, since the debug info will give the
+ size of type actually used in each case. */
+ set_gdbarch_long_double_bit (gdbarch, 16 * TARGET_CHAR_BIT);
+ set_gdbarch_long_double_format (gdbarch, floatformats_ibm_long_double);
+
+ /* Handle PPC GNU/Linux 64-bit function pointers (which are really
+ function descriptors) and 32-bit secure PLT entries. */
+ set_gdbarch_convert_from_func_ptr_addr
+ (gdbarch, ppc_linux_convert_from_func_ptr_addr);
if (tdep->wordsize == 4)
{
if (tdep->wordsize == 8)
{
- /* Handle PPC64 GNU/Linux function pointers (which are really
- function descriptors). */
- set_gdbarch_convert_from_func_ptr_addr
- (gdbarch, ppc64_linux_convert_from_func_ptr_addr);
- set_gdbarch_skip_trampoline_code (gdbarch, ppc64_skip_trampoline_code);
-
/* Shared library handling. */
+ set_gdbarch_skip_trampoline_code (gdbarch, ppc64_skip_trampoline_code);
set_solib_svr4_fetch_link_map_offsets
(gdbarch, svr4_lp64_fetch_link_map_offsets);