From 29b21c31ed47a77e4fed4fd225febc5167da3868 Mon Sep 17 00:00:00 2001 From: tausq Date: Thu, 29 Apr 2004 03:36:48 +0000 Subject: [PATCH] 2004-04-24 Randolph Chung * configure.host (hppa*-*-linux*): New target. * configure.tgt (hppa*-*-linux*): Likewise. * hppa-tdep.c (hppa_gdbarch_init): Set cannot_fetch_register, move gdbarch_init_osabi() call earlier so that osabi-specific frame unwinders can be registered first. * config/djgpp/fnchange.lst: Add entries for hppa-linux-tdep.c and hppa-linux-nat.c. * config/pa/tm-hppa.h (ISR_REGNUM, PID0_REGNUM, PID1_REGNUM) (PID2_REGNUM, PID3_REGNUM): Add definitions of some register numbers. * config/pa/linux.mh: New file. * config/pa/linux.mt: New file. * config/pa/nm-linux.h: New file. * config/pa/xm-linux.h: New file. * hppa-linux-nat.c: New file. * hppa-linux-tdep.c: New file. --- gdb/ChangeLog | 18 ++ gdb/config/djgpp/fnchange.lst | 2 + gdb/config/pa/linux.mh | 9 + gdb/config/pa/linux.mt | 3 + gdb/config/pa/nm-linux.h | 29 +++ gdb/config/pa/tm-hppa.h | 5 + gdb/config/pa/xm-linux.h | 31 +++ gdb/configure.host | 1 + gdb/configure.tgt | 1 + gdb/hppa-linux-nat.c | 277 +++++++++++++++++++++++ gdb/hppa-linux-tdep.c | 499 ++++++++++++++++++++++++++++++++++++++++++ gdb/hppa-tdep.c | 7 +- 12 files changed, 879 insertions(+), 3 deletions(-) create mode 100644 gdb/config/pa/linux.mh create mode 100644 gdb/config/pa/linux.mt create mode 100644 gdb/config/pa/nm-linux.h create mode 100644 gdb/config/pa/xm-linux.h create mode 100644 gdb/hppa-linux-nat.c create mode 100644 gdb/hppa-linux-tdep.c diff --git a/gdb/ChangeLog b/gdb/ChangeLog index aa65b7a21b..fcfcbcfae2 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,21 @@ +2004-04-24 Randolph Chung + + * configure.host (hppa*-*-linux*): New target. + * configure.tgt (hppa*-*-linux*): Likewise. + * hppa-tdep.c (hppa_gdbarch_init): Set cannot_fetch_register, move + gdbarch_init_osabi() call earlier so that osabi-specific frame + unwinders can be registered first. + * config/djgpp/fnchange.lst: Add entries for hppa-linux-tdep.c and + hppa-linux-nat.c. + * config/pa/tm-hppa.h (ISR_REGNUM, PID0_REGNUM, PID1_REGNUM) + (PID2_REGNUM, PID3_REGNUM): Add definitions of some register numbers. + * config/pa/linux.mh: New file. + * config/pa/linux.mt: New file. + * config/pa/nm-linux.h: New file. + * config/pa/xm-linux.h: New file. + * hppa-linux-nat.c: New file. + * hppa-linux-tdep.c: New file. + 2004-04-28 Randolph Chung * hppa-tdep.c (hppa32_return_value): Handle both 4- and 8-byte fp diff --git a/gdb/config/djgpp/fnchange.lst b/gdb/config/djgpp/fnchange.lst index 1d9e3936ee..4950a63be9 100644 --- a/gdb/config/djgpp/fnchange.lst +++ b/gdb/config/djgpp/fnchange.lst @@ -232,6 +232,8 @@ @V@/gdb/testsuite/gdb.mi/mi2-var-display.exp @V@/gdb/testsuite/gdb.mi/mi2vardisplay.exp @V@/gdb/amd64-linux-tdep.c @V@/gdb/amd64-ltdep.c @V@/gdb/amd64-linux-nat.c @V@/gdb/amd64-lnat.c +@V@/gdb/hppa-linux-tdep.c @V@/gdb/palnxtdep.c +@V@/gdb/hppa-linux-nat.c @V@/gdb/palnxnat.c @V@/include/ChangeLog-9103 @V@/include/ChangeLog.9103 @V@/include/coff/ChangeLog-9103 @V@/include/coff/ChangeLog.9103 @V@/include/elf/ChangeLog-9103 @V@/include/elf/ChangeLog.9103 diff --git a/gdb/config/pa/linux.mh b/gdb/config/pa/linux.mh new file mode 100644 index 0000000000..232a4adaeb --- /dev/null +++ b/gdb/config/pa/linux.mh @@ -0,0 +1,9 @@ +# Host: Hewlett-Packard PA-RISC machine, running Linux +XDEPFILES= +XM_FILE= xm-linux.h +NAT_FILE= nm-linux.h +NATDEPFILES= infptrace.o inftarg.o fork-child.o corelow.o gcore.o \ + core-regset.o hppa-linux-nat.o linux-proc.o \ + proc-service.o thread-db.o lin-lwp.o linux-nat.o + +LOADLIBES = -ldl -rdynamic diff --git a/gdb/config/pa/linux.mt b/gdb/config/pa/linux.mt new file mode 100644 index 0000000000..cb67725423 --- /dev/null +++ b/gdb/config/pa/linux.mt @@ -0,0 +1,3 @@ +# Target: HP PA-RISC running Linux +TDEPFILES= hppa-tdep.o hppa-linux-tdep.o glibc-tdep.o solib.o solib-svr4.o +TM_FILE=tm-hppa.h diff --git a/gdb/config/pa/nm-linux.h b/gdb/config/pa/nm-linux.h new file mode 100644 index 0000000000..ee88ba7cd9 --- /dev/null +++ b/gdb/config/pa/nm-linux.h @@ -0,0 +1,29 @@ +/* Native support for GNU/Linux, for GDB, the GNU debugger. + Copyright (C) 2004 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 + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef PA_NM_LINUX_H +#define PA_NM_LINUX_H + +#include "config/nm-linux.h" + +#define U_REGS_OFFSET 0 + +#endif + diff --git a/gdb/config/pa/tm-hppa.h b/gdb/config/pa/tm-hppa.h index a8037194da..7413684f5b 100644 --- a/gdb/config/pa/tm-hppa.h +++ b/gdb/config/pa/tm-hppa.h @@ -52,9 +52,14 @@ extern int hppa_pc_requires_run_before_use (CORE_ADDR pc); #define PCSQ_TAIL_REGNUM 36 /* instruction space queue tail */ #define EIEM_REGNUM 37 /* External Interrupt Enable Mask */ #define IIR_REGNUM 38 /* Interrupt Instruction Register */ +#define ISR_REGNUM 39 /* Interrupt Space Register */ #define IOR_REGNUM 40 /* Interrupt Offset Register */ #define SR4_REGNUM 43 /* space register 4 */ #define RCR_REGNUM 51 /* Recover Counter (also known as cr0) */ +#define PID0_REGNUM 52 /* Protection ID */ +#define PID1_REGNUM 53 /* Protection ID */ +#define PID2_REGNUM 55 /* Protection ID */ +#define PID3_REGNUM 56 /* Protection ID */ #define CCR_REGNUM 54 /* Coprocessor Configuration Register */ #define TR0_REGNUM 57 /* Temporary Registers (cr24 -> cr31) */ #define CR27_REGNUM 60 /* Base register for thread-local storage, cr27 */ diff --git a/gdb/config/pa/xm-linux.h b/gdb/config/pa/xm-linux.h new file mode 100644 index 0000000000..ceb6ed2cae --- /dev/null +++ b/gdb/config/pa/xm-linux.h @@ -0,0 +1,31 @@ +/* Host-dependent definitions for the hppa-linux. + + Copyright 2004 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 + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef XM_HPPA_LINUX_H +#define XM_HPPA_LINUX_H + +#include "floatformat.h" + +#define HOST_FLOAT_FORMAT &floatformat_ieee_single_big +#define HOST_DOUBLE_FORMAT &floatformat_ieee_double_big +#define HOST_LONG_DOUBLE_FORMAT &floatformat_ieee_double_big + +#endif /* xm-linux.h */ diff --git a/gdb/configure.host b/gdb/configure.host index 64d46bd4c6..8839dc4395 100644 --- a/gdb/configure.host +++ b/gdb/configure.host @@ -48,6 +48,7 @@ hppa*-*-hpux10.20) gdb_host=hpux1020 ;; hppa*64*-*-hpux11*) gdb_host=hpux11w ;; hppa*-*-hpux11*) gdb_host=hpux11 ;; hppa*-*-hpux*) gdb_host=hppahpux ;; +hppa*-*-linux*) gdb_host=linux ;; i[34567]86-ncr-*) gdb_host=ncr3000 ;; i[34567]86-*-dgux*) gdb_host=i386v4 ;; diff --git a/gdb/configure.tgt b/gdb/configure.tgt index aee9c7dfd2..646b2588f2 100644 --- a/gdb/configure.tgt +++ b/gdb/configure.tgt @@ -74,6 +74,7 @@ frv-*-*) gdb_target=frv ;; hppa*64*-*-hpux11*) gdb_target=hppa64 ;; hppa*-*-hpux*) gdb_target=hppahpux ;; hppa*-*-hiux*) gdb_target=hppahpux ;; +hppa*-*-linux*) gdb_target=linux ;; hppa*-*-*) gdb_target=hppa ;; i[34567]86-ncr-*) gdb_target=ncr3000 ;; diff --git a/gdb/hppa-linux-nat.c b/gdb/hppa-linux-nat.c new file mode 100644 index 0000000000..e633c06fc7 --- /dev/null +++ b/gdb/hppa-linux-nat.c @@ -0,0 +1,277 @@ +/* Functions specific to running gdb native on HPPA running Linux. + Copyright 2004 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 + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include "defs.h" +#include "gdbcore.h" +#include "regcache.h" +#include "gdb_string.h" + +#include +#include +#include + +/* Prototypes for supply_gregset etc. */ +#include "gregset.h" + +/* These must match the order of the register names. + + Some sort of lookup table is needed because the offsets associated + with the registers are all over the board. */ + +static const int u_offsets[] = + { + /* general registers */ + -1, + PT_GR1, + PT_GR2, + PT_GR3, + PT_GR4, + PT_GR5, + PT_GR6, + PT_GR7, + PT_GR8, + PT_GR9, + PT_GR10, + PT_GR11, + PT_GR12, + PT_GR13, + PT_GR14, + PT_GR15, + PT_GR16, + PT_GR17, + PT_GR18, + PT_GR19, + PT_GR20, + PT_GR21, + PT_GR22, + PT_GR23, + PT_GR24, + PT_GR25, + PT_GR26, + PT_GR27, + PT_GR28, + PT_GR29, + PT_GR30, + PT_GR31, + + PT_SAR, + PT_IAOQ0, + PT_IASQ0, + PT_IAOQ1, + PT_IASQ1, + -1, /* eiem */ + PT_IIR, + PT_ISR, + PT_IOR, + PT_PSW, + -1, /* goto */ + + PT_SR4, + PT_SR0, + PT_SR1, + PT_SR2, + PT_SR3, + PT_SR5, + PT_SR6, + PT_SR7, + + -1, /* cr0 */ + -1, /* pid0 */ + -1, /* pid1 */ + -1, /* ccr */ + -1, /* pid2 */ + -1, /* pid3 */ + -1, /* cr24 */ + -1, /* cr25 */ + -1, /* cr26 */ + PT_CR27, + -1, /* cr28 */ + -1, /* cr29 */ + -1, /* cr30 */ + + /* Floating point regs. */ + PT_FR0, PT_FR0 + 4, + PT_FR1, PT_FR1 + 4, + PT_FR2, PT_FR2 + 4, + PT_FR3, PT_FR3 + 4, + PT_FR4, PT_FR4 + 4, + PT_FR5, PT_FR5 + 4, + PT_FR6, PT_FR6 + 4, + PT_FR7, PT_FR7 + 4, + PT_FR8, PT_FR8 + 4, + PT_FR9, PT_FR9 + 4, + PT_FR10, PT_FR10 + 4, + PT_FR11, PT_FR11 + 4, + PT_FR12, PT_FR12 + 4, + PT_FR13, PT_FR13 + 4, + PT_FR14, PT_FR14 + 4, + PT_FR15, PT_FR15 + 4, + PT_FR16, PT_FR16 + 4, + PT_FR17, PT_FR17 + 4, + PT_FR18, PT_FR18 + 4, + PT_FR19, PT_FR19 + 4, + PT_FR20, PT_FR20 + 4, + PT_FR21, PT_FR21 + 4, + PT_FR22, PT_FR22 + 4, + PT_FR23, PT_FR23 + 4, + PT_FR24, PT_FR24 + 4, + PT_FR25, PT_FR25 + 4, + PT_FR26, PT_FR26 + 4, + PT_FR27, PT_FR27 + 4, + PT_FR28, PT_FR28 + 4, + PT_FR29, PT_FR29 + 4, + PT_FR30, PT_FR30 + 4, + PT_FR31, PT_FR31 + 4, + }; + +CORE_ADDR +register_addr (int regno, CORE_ADDR blockend) +{ + CORE_ADDR addr; + + if ((unsigned) regno >= NUM_REGS) + error ("Invalid register number %d.", regno); + + if (u_offsets[regno] == -1) + addr = 0; + else + { + addr = (CORE_ADDR) u_offsets[regno]; + } + + return addr; +} + +/* + * Registers saved in a coredump: + * gr0..gr31 + * sr0..sr7 + * iaoq0..iaoq1 + * iasq0..iasq1 + * sar, iir, isr, ior, ipsw + * cr0, cr24..cr31 + * cr8,9,12,13 + * cr10, cr15 + */ +#define GR_REGNUM(_n) (R0_REGNUM+_n) +#define TR_REGNUM(_n) (TR0_REGNUM+_n) +static const int greg_map[] = + { + GR_REGNUM(0), GR_REGNUM(1), GR_REGNUM(2), GR_REGNUM(3), + GR_REGNUM(4), GR_REGNUM(5), GR_REGNUM(6), GR_REGNUM(7), + GR_REGNUM(8), GR_REGNUM(9), GR_REGNUM(10), GR_REGNUM(11), + GR_REGNUM(12), GR_REGNUM(13), GR_REGNUM(14), GR_REGNUM(15), + GR_REGNUM(16), GR_REGNUM(17), GR_REGNUM(18), GR_REGNUM(19), + GR_REGNUM(20), GR_REGNUM(21), GR_REGNUM(22), GR_REGNUM(23), + GR_REGNUM(24), GR_REGNUM(25), GR_REGNUM(26), GR_REGNUM(27), + GR_REGNUM(28), GR_REGNUM(29), GR_REGNUM(30), GR_REGNUM(31), + + SR4_REGNUM+1, SR4_REGNUM+2, SR4_REGNUM+3, SR4_REGNUM+4, + SR4_REGNUM, SR4_REGNUM+5, SR4_REGNUM+6, SR4_REGNUM+7, + + PCOQ_HEAD_REGNUM, PCOQ_TAIL_REGNUM, + PCSQ_HEAD_REGNUM, PCSQ_TAIL_REGNUM, + + SAR_REGNUM, IIR_REGNUM, ISR_REGNUM, IOR_REGNUM, + IPSW_REGNUM, RCR_REGNUM, + + TR_REGNUM(0), TR_REGNUM(1), TR_REGNUM(2), TR_REGNUM(3), + TR_REGNUM(4), TR_REGNUM(5), TR_REGNUM(6), TR_REGNUM(7), + + PID0_REGNUM, PID1_REGNUM, PID2_REGNUM, PID3_REGNUM, + CCR_REGNUM, EIEM_REGNUM, + }; + +void +supply_gregset (gdb_gregset_t *gregsetp) +{ + int i; + greg_t *regp = (elf_greg_t *) gregsetp; + + for (i = 0; i < sizeof (greg_map) / sizeof (greg_map[0]); i++, regp++) + { + int regno = greg_map[i]; + int size = register_size (current_gdbarch, regno); + /* When running a 64 bit kernel, a greg_t may be larger than the + actual register, so just pick off the LS bits of big-endian word. */ + supply_register (regno, ((char *) (regp + 1)) - size); + } +} + +void +fill_gregset (gdb_gregset_t *gregsetp, int regno) +{ + int i; + greg_t *regp = (greg_t *) gregsetp; + + memset (gregsetp, 0, sizeof (*gregsetp)); + for (i = 0; i < sizeof (greg_map) / sizeof (greg_map[0]); i++, regp++) + { + int regi = greg_map[i]; + + if (regno == -1 || regi == regno) + { + int rawsize = register_size (current_gdbarch, regi); + regcache_collect (regi, ((char *) (regp + 1)) - rawsize); + } + } +} + +/* Given a pointer to a floating point register set in /proc format + (fpregset_t *), unpack the register contents and supply them as gdb's + idea of the current floating point register values. */ + +void +supply_fpregset (gdb_fpregset_t *fpregsetp) +{ + register int regi; + char *from; + + for (regi = 0; regi <= 31; regi++) + { + from = (char *) &((*fpregsetp)[regi]); + supply_register (2*regi + HPPA_FP0_REGNUM, from); + supply_register (2*regi + HPPA_FP0_REGNUM + 1, from + 4); + } +} + +/* Given a pointer to a floating point register set in /proc format + (fpregset_t *), update the register specified by REGNO from gdb's idea + of the current floating point register set. If REGNO is -1, update + them all. */ + +void +fill_fpregset (gdb_fpregset_t *fpregsetp, int regno) +{ + int i; + + for (i = 0; i < NUM_REGS; i++) + { + if (regno == -1 || regno == i) + { + /* Gross. fpregset_t is double, registers[x] has single + precision reg. */ + char *to = (char *) &((*fpregsetp)[(i - HPPA_FP0_REGNUM) / 2]); + if ((i - HPPA_FP0_REGNUM) & 1) + to += 4; + regcache_collect (i, to); + } + } +} diff --git a/gdb/hppa-linux-tdep.c b/gdb/hppa-linux-tdep.c new file mode 100644 index 0000000000..bdcb4a7455 --- /dev/null +++ b/gdb/hppa-linux-tdep.c @@ -0,0 +1,499 @@ +/* Target-dependent code for Linux running on PA-RISC, for GDB. + + Copyright 2004 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 +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "defs.h" +#include "gdbcore.h" +#include "osabi.h" +#include "target.h" +#include "objfiles.h" +#include "solib-svr4.h" +#include "glibc-tdep.h" +#include "frame-unwind.h" +#include "trad-frame.h" +#include "dwarf2-frame.h" +#include "hppa-tdep.h" + +#if 0 +/* Convert DWARF register number REG to the appropriate register + number used by GDB. */ +static int +hppa_dwarf_reg_to_regnum (int reg) +{ + /* registers 0 - 31 are the same in both sets */ + if (reg < 32) + return reg; + + /* dwarf regs 32 to 85 are fpregs 4 - 31 */ + if (reg >= 32 && reg <= 85) + return FP4_REGNUM + (reg - 32); + + warning ("Unmapped DWARF Register #%d encountered\n", reg); + return -1; +} +#endif + +static void +hppa_linux_target_write_pc (CORE_ADDR v, ptid_t ptid) +{ + /* Probably this should be done by the kernel, but it isn't. */ + write_register_pid (PCOQ_HEAD_REGNUM, v | 0x3, ptid); + write_register_pid (PCOQ_TAIL_REGNUM, (v + 4) | 0x3, ptid); +} + +/* An instruction to match. */ +struct insn_pattern +{ + unsigned int data; /* See if it matches this.... */ + unsigned int mask; /* ... with this mask. */ +}; + +/* See bfd/elf32-hppa.c */ +static struct insn_pattern hppa_long_branch_stub[] = { + /* ldil LR'xxx,%r1 */ + { 0x20200000, 0xffe00000 }, + /* be,n RR'xxx(%sr4,%r1) */ + { 0xe0202002, 0xffe02002 }, + { 0, 0 } +}; + +static struct insn_pattern hppa_long_branch_pic_stub[] = { + /* b,l .+8, %r1 */ + { 0xe8200000, 0xffe00000 }, + /* addil LR'xxx - ($PIC_pcrel$0 - 4), %r1 */ + { 0x28200000, 0xffe00000 }, + /* be,n RR'xxxx - ($PIC_pcrel$0 - 8)(%sr4, %r1) */ + { 0xe0202002, 0xffe02002 }, + { 0, 0 } +}; + +static struct insn_pattern hppa_import_stub[] = { + /* addil LR'xxx, %dp */ + { 0x2b600000, 0xffe00000 }, + /* ldw RR'xxx(%r1), %r21 */ + { 0x48350000, 0xffffb000 }, + /* bv %r0(%r21) */ + { 0xeaa0c000, 0xffffffff }, + /* ldw RR'xxx+4(%r1), %r19 */ + { 0x48330000, 0xffffb000 }, + { 0, 0 } +}; + +static struct insn_pattern hppa_import_pic_stub[] = { + /* addil LR'xxx,%r19 */ + { 0x2a600000, 0xffe00000 }, + /* ldw RR'xxx(%r1),%r21 */ + { 0x48350000, 0xffffb000 }, + /* bv %r0(%r21) */ + { 0xeaa0c000, 0xffffffff }, + /* ldw RR'xxx+4(%r1),%r19 */ + { 0x48330000, 0xffffb000 }, + { 0, 0 }, +}; + +static struct insn_pattern hppa_plt_stub[] = { + /* b,l 1b, %r20 - 1b is 3 insns before here */ + { 0xea9f1fdd, 0xffffffff }, + /* depi 0,31,2,%r20 */ + { 0xd6801c1e, 0xffffffff }, + { 0, 0 } +}; + +static struct insn_pattern hppa_sigtramp[] = { + /* ldi 0, %r25 or ldi 1, %r25 */ + { 0x34190000, 0xfffffffd }, + /* ldi __NR_rt_sigreturn, %r20 */ + { 0x3414015a, 0xffffffff }, + /* be,l 0x100(%sr2, %r0), %sr0, %r31 */ + { 0xe4008200, 0xffffffff }, + /* nop */ + { 0x08000240, 0xffffffff }, + { 0, 0 } +}; + +#define HPPA_MAX_INSN_PATTERN_LEN (4) + +/* Return non-zero if the instructions at PC match the series + described in PATTERN, or zero otherwise. PATTERN is an array of + 'struct insn_pattern' objects, terminated by an entry whose mask is + zero. + + When the match is successful, fill INSN[i] with what PATTERN[i] + matched. */ +static int +insns_match_pattern (CORE_ADDR pc, + struct insn_pattern *pattern, + unsigned int *insn) +{ + int i; + CORE_ADDR npc = pc; + + for (i = 0; pattern[i].mask; i++) + { + insn[i] = read_memory_unsigned_integer (npc, 4); + if ((insn[i] & pattern[i].mask) == pattern[i].data) + npc += 4; + else + return 0; + } + return 1; +} + +static int +hppa_linux_in_dyncall (CORE_ADDR pc) +{ + static CORE_ADDR dyncall = 0; + + /* FIXME: if we switch exec files, dyncall should be reinitialized */ + if (!dyncall) + { + struct minimal_symbol *minsym; + + minsym = lookup_minimal_symbol ("$$dyncall", NULL, NULL); + if (minsym) + dyncall = SYMBOL_VALUE_ADDRESS (minsym); + else + dyncall = -1; + } + + return pc == dyncall; +} + +/* There are several kinds of "trampolines" that we need to deal with: + - long branch stubs: these are inserted by the linker when a branch + target is too far away for a branch insn to reach + - plt stubs: these should go into the .plt section, so are easy to find + - import stubs: used to call from object to shared lib or shared lib to + shared lib; these go in regular text sections. In fact the linker tries + to put them throughout the code because branches have limited reachability. + We use the same mechanism as ppc64 to recognize the stub insn patterns. + - $$dyncall: similar to hpux, hppa-linux uses $$dyncall for indirect function + calls. $$dyncall is exported by libgcc.a */ +static int +hppa_linux_in_solib_call_trampoline (CORE_ADDR pc, char *name) +{ + unsigned int insn[HPPA_MAX_INSN_PATTERN_LEN]; + int r; + + r = in_plt_section (pc, name) + || hppa_linux_in_dyncall (pc) + || insns_match_pattern (pc, hppa_import_stub, insn) + || insns_match_pattern (pc, hppa_import_pic_stub, insn) + || insns_match_pattern (pc, hppa_long_branch_stub, insn) + || insns_match_pattern (pc, hppa_long_branch_pic_stub, insn); + + return r; +} + +static CORE_ADDR +hppa_linux_skip_trampoline_code (CORE_ADDR pc) +{ + unsigned int insn[HPPA_MAX_INSN_PATTERN_LEN]; + int dp_rel, pic_rel; + + /* dyncall handles both PLABELs and direct addresses */ + if (hppa_linux_in_dyncall (pc)) + { + pc = (CORE_ADDR) read_register (22); + + /* PLABELs have bit 30 set; if it's a PLABEL, then dereference it */ + if (pc & 0x2) + pc = (CORE_ADDR) read_memory_integer (pc & ~0x3, TARGET_PTR_BIT / 8); + + return pc; + } + + dp_rel = pic_rel = 0; + if ((dp_rel = insns_match_pattern (pc, hppa_import_stub, insn)) + || (pic_rel = insns_match_pattern (pc, hppa_import_pic_stub, insn))) + { + /* Extract the target address from the addil/ldw sequence. */ + pc = hppa_extract_21 (insn[0]) + hppa_extract_14 (insn[1]); + + if (dp_rel) + pc += (CORE_ADDR) read_register (27); + else + pc += (CORE_ADDR) read_register (19); + + /* fallthrough */ + } + + if (in_plt_section (pc, NULL)) + { + pc = (CORE_ADDR) read_memory_integer (pc, TARGET_PTR_BIT / 8); + + /* if the plt slot has not yet been resolved, the target will + be the plt stub */ + if (in_plt_section (pc, NULL)) + { + /* Sanity check: are we pointing to the plt stub? */ + if (insns_match_pattern (pc, hppa_plt_stub, insn)) + { + /* this should point to the fixup routine */ + pc = (CORE_ADDR) read_memory_integer (pc + 8, TARGET_PTR_BIT / 8); + } + else + { + error ("Cannot resolve plt stub at 0x%s\n", + paddr_nz (pc)); + pc = 0; + } + } + } + + return pc; +} + +/* Signal frames. */ + +/* (This is derived from MD_FALLBACK_FRAME_STATE_FOR in gcc.) + + Unfortunately, because of various bugs and changes to the kernel, + we have several cases to deal with. + + In 2.4, the signal trampoline is 4 bytes, and pc should point directly at + the beginning of the trampoline and struct rt_sigframe. + + In <= 2.6.5-rc2-pa3, the signal trampoline is 9 bytes, and pc points at + the 4th word in the trampoline structure. This is wrong, it should point + at the 5th word. This is fixed in 2.6.5-rc2-pa4. + + To detect these cases, we first take pc, align it to 64-bytes + to get the beginning of the signal frame, and then check offsets 0, 4 + and 5 to see if we found the beginning of the trampoline. This will + tell us how to locate the sigcontext structure. + + Note that with a 2.4 64-bit kernel, the signal context is not properly + passed back to userspace so the unwind will not work correctly. */ +static CORE_ADDR +hppa_linux_sigtramp_find_sigcontext (CORE_ADDR sp) +{ + unsigned int dummy[HPPA_MAX_INSN_PATTERN_LEN]; + int offs = 0; + int try; + /* offsets to try to find the trampoline */ + static int pcoffs[] = { 0, 4*4, 5*4 }; + /* offsets to the rt_sigframe structure */ + static int sfoffs[] = { 4*4, 10*4, 10*4 }; + + /* rt_sigreturn trampoline: + 3419000x ldi 0, %r25 or ldi 1, %r25 (x = 0 or 2) + 3414015a ldi __NR_rt_sigreturn, %r20 + e4008200 be,l 0x100(%sr2, %r0), %sr0, %r31 + 08000240 nop */ + + for (try = 0; try < ARRAY_SIZE (pcoffs); try++) + { + if (insns_match_pattern (sp + pcoffs[try], hppa_sigtramp, dummy)) + { + offs = sfoffs[try]; + break; + } + } + + if (offs == 0) + return 0; + + /* sp + sfoffs[try] points to a struct rt_sigframe, which contains + a struct siginfo and a struct ucontext. struct ucontext contains + a struct sigcontext. Return an offset to this sigcontext here. Too + bad we cannot include system specific headers :-(. + sizeof(struct siginfo) == 128 + offsetof(struct ucontext, uc_mcontext) == 24. */ + return sp + sfoffs[try] + 128 + 24; +} + +struct hppa_linux_sigtramp_unwind_cache +{ + CORE_ADDR base; + struct trad_frame_saved_reg *saved_regs; +}; + +static struct hppa_linux_sigtramp_unwind_cache * +hppa_linux_sigtramp_frame_unwind_cache (struct frame_info *next_frame, + void **this_cache) +{ + struct gdbarch *gdbarch = get_frame_arch (next_frame); + struct hppa_linux_sigtramp_unwind_cache *info; + CORE_ADDR sp, pc, scptr; + int i; + + if (*this_cache) + return *this_cache; + + info = FRAME_OBSTACK_ZALLOC (struct hppa_linux_sigtramp_unwind_cache); + *this_cache = info; + info->saved_regs = trad_frame_alloc_saved_regs (next_frame); + + pc = frame_pc_unwind (next_frame); + sp = (pc & ~63); + scptr = hppa_linux_sigtramp_find_sigcontext (sp); + + /* structure of struct sigcontext: + + struct sigcontext { + unsigned long sc_flags; + unsigned long sc_gr[32]; + unsigned long long sc_fr[32]; + unsigned long sc_iasq[2]; + unsigned long sc_iaoq[2]; + unsigned long sc_sar; */ + + /* Skip sc_flags. */ + scptr += 4; + + /* GR[0] is the psw, we don't restore that. */ + scptr += 4; + + /* General registers. */ + for (i = 1; i < 32; i++) + { + info->saved_regs[R0_REGNUM + i].addr = scptr; + scptr += 4; + } + + /* Pad. */ + scptr += 4; + + /* FP regs; FP0-3 are not restored. */ + scptr += (8 * 4); + + for (i = 4; i < 32; i++) + { + info->saved_regs[HPPA_FP0_REGNUM + (i * 2)].addr = scptr; + scptr += 4; + info->saved_regs[HPPA_FP0_REGNUM + (i * 2) + 1].addr = scptr; + scptr += 4; + } + + /* IASQ/IAOQ. */ + info->saved_regs[PCSQ_HEAD_REGNUM].addr = scptr; + scptr += 4; + info->saved_regs[PCSQ_TAIL_REGNUM].addr = scptr; + scptr += 4; + + info->saved_regs[PCOQ_HEAD_REGNUM].addr = scptr; + scptr += 4; + info->saved_regs[PCOQ_TAIL_REGNUM].addr = scptr; + scptr += 4; + + info->base = read_memory_unsigned_integer ( + info->saved_regs[HPPA_SP_REGNUM].addr, 4); + + return info; +} + +static void +hppa_linux_sigtramp_frame_this_id (struct frame_info *next_frame, + void **this_prologue_cache, + struct frame_id *this_id) +{ + struct hppa_linux_sigtramp_unwind_cache *info + = hppa_linux_sigtramp_frame_unwind_cache (next_frame, this_prologue_cache); + *this_id = frame_id_build (info->base, frame_pc_unwind (next_frame)); +} + +static void +hppa_linux_sigtramp_frame_prev_register (struct frame_info *next_frame, + void **this_prologue_cache, + int regnum, int *optimizedp, + enum lval_type *lvalp, + CORE_ADDR *addrp, + int *realnump, void *bufferp) +{ + struct hppa_linux_sigtramp_unwind_cache *info + = hppa_linux_sigtramp_frame_unwind_cache (next_frame, this_prologue_cache); + int pcoqt = (regnum == PCOQ_TAIL_REGNUM); + + if (pcoqt) + regnum = PCOQ_HEAD_REGNUM; + + trad_frame_prev_register (next_frame, info->saved_regs, regnum, + optimizedp, lvalp, addrp, realnump, bufferp); + + if (pcoqt) + store_unsigned_integer (bufferp, 4, + extract_unsigned_integer (bufferp, 4) + 4); +} + +static const struct frame_unwind hppa_linux_sigtramp_frame_unwind = { + SIGTRAMP_FRAME, + hppa_linux_sigtramp_frame_this_id, + hppa_linux_sigtramp_frame_prev_register +}; + +/* hppa-linux always uses "new-style" rt-signals. The signal handler's return + address should point to a signal trampoline on the stack. The signal + trampoline is embedded in a rt_sigframe structure that is aligned on + the stack. We take advantage of the fact that sp must be 64-byte aligned, + and the trampoline is small, so by rounding down the trampoline address + we can find the beginning of the struct rt_sigframe. */ +static const struct frame_unwind * +hppa_linux_sigtramp_unwind_sniffer (struct frame_info *next_frame) +{ + CORE_ADDR pc = frame_pc_unwind (next_frame); + CORE_ADDR sp = (pc & ~63); + + if (hppa_linux_sigtramp_find_sigcontext (sp)) + return &hppa_linux_sigtramp_frame_unwind; + + return NULL; +} + +/* Forward declarations. */ +extern initialize_file_ftype _initialize_hppa_linux_tdep; + +static void +hppa_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + + /* Linux is always ELF. */ + tdep->is_elf = 1; + + set_gdbarch_write_pc (gdbarch, hppa_linux_target_write_pc); + + frame_unwind_append_sniffer (gdbarch, hppa_linux_sigtramp_unwind_sniffer); + + /* GNU/Linux uses SVR4-style shared libraries. */ + set_solib_svr4_fetch_link_map_offsets + (gdbarch, svr4_ilp32_fetch_link_map_offsets); + + set_gdbarch_in_solib_call_trampoline + (gdbarch, hppa_linux_in_solib_call_trampoline); + set_gdbarch_skip_trampoline_code + (gdbarch, hppa_linux_skip_trampoline_code); + + /* GNU/Linux uses the dynamic linker included in the GNU C Library. */ + set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver); + +#if 0 + /* Dwarf-2 unwinding support. Not yet working. */ + set_gdbarch_dwarf_reg_to_regnum (gdbarch, hppa_dwarf_reg_to_regnum); + set_gdbarch_dwarf2_reg_to_regnum (gdbarch, hppa_dwarf_reg_to_regnum); + frame_unwind_append_sniffer (gdbarch, dwarf2_frame_sniffer); + frame_base_append_sniffer (gdbarch, dwarf2_frame_base_sniffer); +#endif +} + +void +_initialize_hppa_linux_tdep (void) +{ + gdbarch_register_osabi (bfd_arch_hppa, 0, GDB_OSABI_LINUX, hppa_linux_init_abi); +} diff --git a/gdb/hppa-tdep.c b/gdb/hppa-tdep.c index 5e84782ff0..a366717c1f 100644 --- a/gdb/hppa-tdep.c +++ b/gdb/hppa-tdep.c @@ -2332,6 +2332,7 @@ hppa_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) set_gdbarch_sp_regnum (gdbarch, HPPA_SP_REGNUM); set_gdbarch_fp0_regnum (gdbarch, HPPA_FP0_REGNUM); set_gdbarch_cannot_store_register (gdbarch, hppa_cannot_store_register); + set_gdbarch_cannot_fetch_register (gdbarch, hppa_cannot_store_register); set_gdbarch_addr_bits_remove (gdbarch, hppa_smash_text_address); set_gdbarch_smash_text_address (gdbarch, hppa_smash_text_address); set_gdbarch_believe_pcc_promotion (gdbarch, 1); @@ -2384,14 +2385,14 @@ hppa_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) set_gdbarch_unwind_dummy_id (gdbarch, hppa_unwind_dummy_id); set_gdbarch_unwind_pc (gdbarch, hppa_unwind_pc); + /* Hook in ABI-specific overrides, if they have been registered. */ + gdbarch_init_osabi (info, gdbarch); + /* Hook in the default unwinders. */ frame_unwind_append_sniffer (gdbarch, hppa_stub_unwind_sniffer); frame_unwind_append_sniffer (gdbarch, hppa_frame_unwind_sniffer); frame_base_append_sniffer (gdbarch, hppa_frame_base_sniffer); - /* Hook in ABI-specific overrides, if they have been registered. */ - gdbarch_init_osabi (info, gdbarch); - return gdbarch; } -- 2.11.0