X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Fhppa-linux-tdep.c;h=2c6fb02f00c1a25c359e75619594fe649928d29d;hb=303b6f5dea757484ac8074bee1b4d5f7c897a997;hp=1d7d047ae6a670e6c13b0628aa58b2047ea90248;hpb=34f75cc19f3ab1dfb345b07e19fe298d26124396;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/hppa-linux-tdep.c b/gdb/hppa-linux-tdep.c index 1d7d047ae6..2c6fb02f00 100644 --- a/gdb/hppa-linux-tdep.c +++ b/gdb/hppa-linux-tdep.c @@ -1,22 +1,23 @@ -/* Target-dependent code for Linux running on PA-RISC, for GDB. +/* Target-dependent code for GNU/Linux running on PA-RISC, for GDB. - Copyright 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2006 Free Software Foundation, Inc. -This file is part of GDB. + 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 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. + 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. */ + 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. */ #include "defs.h" #include "gdbcore.h" @@ -28,8 +29,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "frame-unwind.h" #include "trad-frame.h" #include "dwarf2-frame.h" +#include "value.h" +#include "regset.h" #include "hppa-tdep.h" +#include "elf/common.h" + #if 0 /* Convert DWARF register number REG to the appropriate register number used by GDB. */ @@ -44,7 +49,7 @@ hppa_dwarf_reg_to_regnum (int reg) if (reg >= 32 && reg <= 85) return HPPA_FP4_REGNUM + (reg - 32); - warning ("Unmapped DWARF Register #%d encountered\n", reg); + warning (_("Unmapped DWARF Register #%d encountered."), reg); return -1; } #endif @@ -64,57 +69,6 @@ struct insn_pattern 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 }, @@ -146,7 +100,10 @@ insns_match_pattern (CORE_ADDR pc, for (i = 0; pattern[i].mask; i++) { - insn[i] = read_memory_unsigned_integer (npc, 4); + char buf[4]; + + read_memory_nobpt (npc, buf, 4); + insn[i] = extract_unsigned_integer (buf, 4); if ((insn[i] & pattern[i].mask) == pattern[i].data) npc += 4; else @@ -155,111 +112,6 @@ insns_match_pattern (CORE_ADDR pc, 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.) @@ -282,7 +134,7 @@ hppa_linux_skip_trampoline_code (CORE_ADDR pc) 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) +hppa_linux_sigtramp_find_sigcontext (CORE_ADDR pc) { unsigned int dummy[HPPA_MAX_INSN_PATTERN_LEN]; int offs = 0; @@ -291,6 +143,12 @@ hppa_linux_sigtramp_find_sigcontext (CORE_ADDR sp) static int pcoffs[] = { 0, 4*4, 5*4 }; /* offsets to the rt_sigframe structure */ static int sfoffs[] = { 4*4, 10*4, 10*4 }; + CORE_ADDR sp; + + /* Most of the time, this will be correct. The one case when this will + fail is if the user defined an alternate stack, in which case the + beginning of the stack will not be align_down (pc, 64). */ + sp = align_down (pc, 64); /* rt_sigreturn trampoline: 3419000x ldi 0, %r25 or ldi 1, %r25 (x = 0 or 2) @@ -308,7 +166,20 @@ hppa_linux_sigtramp_find_sigcontext (CORE_ADDR sp) } if (offs == 0) - return 0; + { + if (insns_match_pattern (pc, hppa_sigtramp, dummy)) + { + /* sigaltstack case: we have no way of knowing which offset to + use in this case; default to new kernel handling. If this is + wrong the unwinding will fail. */ + try = 2; + sp = pc - pcoffs[try]; + } + else + { + return 0; + } + } /* sp + sfoffs[try] points to a struct rt_sigframe, which contains a struct siginfo and a struct ucontext. struct ucontext contains @@ -331,7 +202,7 @@ hppa_linux_sigtramp_frame_unwind_cache (struct frame_info *next_frame, { struct gdbarch *gdbarch = get_frame_arch (next_frame); struct hppa_linux_sigtramp_unwind_cache *info; - CORE_ADDR sp, pc, scptr; + CORE_ADDR pc, scptr; int i; if (*this_cache) @@ -342,8 +213,7 @@ hppa_linux_sigtramp_frame_unwind_cache (struct frame_info *next_frame, 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); + scptr = hppa_linux_sigtramp_find_sigcontext (pc); /* structure of struct sigcontext: @@ -393,8 +263,7 @@ hppa_linux_sigtramp_frame_unwind_cache (struct frame_info *next_frame, info->saved_regs[HPPA_PCOQ_TAIL_REGNUM].addr = scptr; scptr += 4; - info->base = read_memory_unsigned_integer ( - info->saved_regs[HPPA_SP_REGNUM].addr, 4); + info->base = frame_unwind_register_unsigned (next_frame, HPPA_SP_REGNUM); return info; } @@ -415,21 +284,12 @@ hppa_linux_sigtramp_frame_prev_register (struct frame_info *next_frame, int regnum, int *optimizedp, enum lval_type *lvalp, CORE_ADDR *addrp, - int *realnump, void *bufferp) + int *realnump, gdb_byte *valuep) { struct hppa_linux_sigtramp_unwind_cache *info = hppa_linux_sigtramp_frame_unwind_cache (next_frame, this_prologue_cache); - int pcoqt = (regnum == HPPA_PCOQ_TAIL_REGNUM); - - if (pcoqt) - regnum = HPPA_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); + hppa_frame_prev_register_helper (next_frame, info->saved_regs, regnum, + optimizedp, lvalp, addrp, realnump, valuep); } static const struct frame_unwind hppa_linux_sigtramp_frame_unwind = { @@ -448,14 +308,206 @@ 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)) + if (hppa_linux_sigtramp_find_sigcontext (pc)) return &hppa_linux_sigtramp_frame_unwind; return NULL; } +/* Attempt to find (and return) the global pointer for the given + function. + + This is a rather nasty bit of code searchs for the .dynamic section + in the objfile corresponding to the pc of the function we're trying + to call. Once it finds the addresses at which the .dynamic section + lives in the child process, it scans the Elf32_Dyn entries for a + DT_PLTGOT tag. If it finds one of these, the corresponding + d_un.d_ptr value is the global pointer. */ + +static CORE_ADDR +hppa_linux_find_global_pointer (struct value *function) +{ + struct obj_section *faddr_sect; + CORE_ADDR faddr; + + faddr = value_as_address (function); + + /* Is this a plabel? If so, dereference it to get the gp value. */ + if (faddr & 2) + { + int status; + char buf[4]; + + faddr &= ~3; + + status = target_read_memory (faddr + 4, buf, sizeof (buf)); + if (status == 0) + return extract_unsigned_integer (buf, sizeof (buf)); + } + + /* If the address is in the plt section, then the real function hasn't + yet been fixed up by the linker so we cannot determine the gp of + that function. */ + if (in_plt_section (faddr, NULL)) + return 0; + + faddr_sect = find_pc_section (faddr); + if (faddr_sect != NULL) + { + struct obj_section *osect; + + ALL_OBJFILE_OSECTIONS (faddr_sect->objfile, osect) + { + if (strcmp (osect->the_bfd_section->name, ".dynamic") == 0) + break; + } + + if (osect < faddr_sect->objfile->sections_end) + { + CORE_ADDR addr; + + addr = osect->addr; + while (addr < osect->endaddr) + { + int status; + LONGEST tag; + char buf[4]; + + status = target_read_memory (addr, buf, sizeof (buf)); + if (status != 0) + break; + tag = extract_signed_integer (buf, sizeof (buf)); + + if (tag == DT_PLTGOT) + { + CORE_ADDR global_pointer; + + status = target_read_memory (addr + 4, buf, sizeof (buf)); + if (status != 0) + break; + global_pointer = extract_unsigned_integer (buf, sizeof (buf)); + + /* The payoff... */ + return global_pointer; + } + + if (tag == DT_NULL) + break; + + addr += 8; + } + } + } + return 0; +} + +/* + * 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) (HPPA_R0_REGNUM+_n) +#define TR_REGNUM(_n) (HPPA_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), + + HPPA_SR4_REGNUM+1, HPPA_SR4_REGNUM+2, HPPA_SR4_REGNUM+3, HPPA_SR4_REGNUM+4, + HPPA_SR4_REGNUM, HPPA_SR4_REGNUM+5, HPPA_SR4_REGNUM+6, HPPA_SR4_REGNUM+7, + + HPPA_PCOQ_HEAD_REGNUM, HPPA_PCOQ_TAIL_REGNUM, + HPPA_PCSQ_HEAD_REGNUM, HPPA_PCSQ_TAIL_REGNUM, + + HPPA_SAR_REGNUM, HPPA_IIR_REGNUM, HPPA_ISR_REGNUM, HPPA_IOR_REGNUM, + HPPA_IPSW_REGNUM, HPPA_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), + + HPPA_PID0_REGNUM, HPPA_PID1_REGNUM, HPPA_PID2_REGNUM, HPPA_PID3_REGNUM, + HPPA_CCR_REGNUM, HPPA_EIEM_REGNUM, + }; + +static void +hppa_linux_supply_regset (const struct regset *regset, + struct regcache *regcache, + int regnum, const void *regs, size_t len) +{ + struct gdbarch *arch = get_regcache_arch (regcache); + struct gdbarch_tdep *tdep = gdbarch_tdep (arch); + const char *buf = regs; + int i, offset; + + offset = 0; + for (i = 0; i < ARRAY_SIZE (greg_map); i++) + { + if (regnum == greg_map[i] || regnum == -1) + regcache_raw_supply (regcache, greg_map[i], buf + offset); + + offset += tdep->bytes_per_address; + } +} + +static void +hppa_linux_supply_fpregset (const struct regset *regset, + struct regcache *regcache, + int regnum, const void *regs, size_t len) +{ + const char *buf = regs; + int i, offset; + + offset = 0; + for (i = 0; i < 31; i++) + { + if (regnum == HPPA_FP0_REGNUM + i || regnum == -1) + regcache_raw_supply (regcache, HPPA_FP0_REGNUM + i, + buf + offset); + offset += 8; + } +} + +/* HPPA Linux kernel register set. */ +static struct regset hppa_linux_regset = +{ + NULL, + hppa_linux_supply_regset +}; + +static struct regset hppa_linux_fpregset = +{ + NULL, + hppa_linux_supply_fpregset +}; + +static const struct regset * +hppa_linux_regset_from_core_section (struct gdbarch *gdbarch, + const char *sect_name, + size_t sect_size) +{ + if (strcmp (sect_name, ".reg") == 0) + return &hppa_linux_regset; + else if (strcmp (sect_name, ".reg2") == 0) + return &hppa_linux_fpregset; + + return NULL; +} + + /* Forward declarations. */ extern initialize_file_ftype _initialize_hppa_linux_tdep; @@ -464,9 +516,11 @@ hppa_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) { struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); - /* Linux is always ELF. */ + /* GNU/Linux is always ELF. */ tdep->is_elf = 1; + tdep->find_global_pointer = hppa_linux_find_global_pointer; + set_gdbarch_write_pc (gdbarch, hppa_linux_target_write_pc); frame_unwind_append_sniffer (gdbarch, hppa_linux_sigtramp_unwind_sniffer); @@ -475,14 +529,20 @@ hppa_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) 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); + tdep->in_solib_call_trampoline = hppa_in_solib_call_trampoline; + set_gdbarch_skip_trampoline_code (gdbarch, hppa_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); + /* On hppa-linux, currently, sizeof(long double) == 8. There has been + some discussions to support 128-bit long double, but it requires some + more work in gcc and glibc first. */ + set_gdbarch_long_double_bit (gdbarch, 64); + + set_gdbarch_regset_from_core_section + (gdbarch, hppa_linux_regset_from_core_section); + #if 0 /* Dwarf-2 unwinding support. Not yet working. */ set_gdbarch_dwarf_reg_to_regnum (gdbarch, hppa_dwarf_reg_to_regnum); @@ -490,10 +550,15 @@ hppa_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) frame_unwind_append_sniffer (gdbarch, dwarf2_frame_sniffer); frame_base_append_sniffer (gdbarch, dwarf2_frame_base_sniffer); #endif + + /* Enable TLS support. */ + set_gdbarch_fetch_tls_load_module_address (gdbarch, + svr4_fetch_objfile_link_map); } void _initialize_hppa_linux_tdep (void) { gdbarch_register_osabi (bfd_arch_hppa, 0, GDB_OSABI_LINUX, hppa_linux_init_abi); + gdbarch_register_osabi (bfd_arch_hppa, bfd_mach_hppa20w, GDB_OSABI_LINUX, hppa_linux_init_abi); }