|  | /* Target-dependent code for GNU/Linux running on PA-RISC, for GDB. | 
|  |  | 
|  | Copyright (C) 2004-2023 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 3 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, see <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | #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 "value.h" | 
|  | #include "regset.h" | 
|  | #include "regcache.h" | 
|  | #include "hppa-tdep.h" | 
|  | #include "linux-tdep.h" | 
|  | #include "elf/common.h" | 
|  |  | 
|  | /* Map DWARF DBX register numbers to GDB register numbers.  */ | 
|  | static int | 
|  | hppa_dwarf_reg_to_regnum (struct gdbarch *gdbarch, int reg) | 
|  | { | 
|  | /* The general registers and the sar are the same in both sets.  */ | 
|  | if (reg >= 0 && reg <= 32) | 
|  | return reg; | 
|  |  | 
|  | /* fr4-fr31 (left and right halves) are mapped from 72.  */ | 
|  | if (reg >= 72 && reg <= 72 + 28 * 2) | 
|  | return HPPA_FP4_REGNUM + (reg - 72); | 
|  |  | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | static void | 
|  | hppa_linux_target_write_pc (struct regcache *regcache, CORE_ADDR v) | 
|  | { | 
|  | /* Probably this should be done by the kernel, but it isn't.  */ | 
|  | regcache_cooked_write_unsigned (regcache, HPPA_PCOQ_HEAD_REGNUM, v | 0x3); | 
|  | regcache_cooked_write_unsigned (regcache, | 
|  | HPPA_PCOQ_TAIL_REGNUM, (v + 4) | 0x3); | 
|  | } | 
|  |  | 
|  | /* An instruction to match.  */ | 
|  | struct insn_pattern | 
|  | { | 
|  | unsigned int data;            /* See if it matches this....  */ | 
|  | unsigned int mask;            /* ... with this mask.  */ | 
|  | }; | 
|  |  | 
|  | 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 (struct gdbarch *gdbarch, CORE_ADDR pc, | 
|  | struct insn_pattern *pattern, | 
|  | unsigned int *insn) | 
|  | { | 
|  | enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); | 
|  | int i; | 
|  | CORE_ADDR npc = pc; | 
|  |  | 
|  | for (i = 0; pattern[i].mask; i++) | 
|  | { | 
|  | gdb_byte buf[4]; | 
|  |  | 
|  | target_read_memory (npc, buf, 4); | 
|  | insn[i] = extract_unsigned_integer (buf, 4, byte_order); | 
|  | if ((insn[i] & pattern[i].mask) == pattern[i].data) | 
|  | npc += 4; | 
|  | else | 
|  | return 0; | 
|  | } | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* 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 (struct gdbarch *gdbarch, CORE_ADDR pc) | 
|  | { | 
|  | unsigned int dummy[HPPA_MAX_INSN_PATTERN_LEN]; | 
|  | int offs = 0; | 
|  | int attempt; | 
|  | /* 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 }; | 
|  | 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) | 
|  | 3414015a ldi __NR_rt_sigreturn, %r20 | 
|  | e4008200 be,l 0x100(%sr2, %r0), %sr0, %r31 | 
|  | 08000240 nop  */ | 
|  |  | 
|  | for (attempt = 0; attempt < ARRAY_SIZE (pcoffs); attempt++) | 
|  | { | 
|  | if (insns_match_pattern (gdbarch, sp + pcoffs[attempt], | 
|  | hppa_sigtramp, dummy)) | 
|  | { | 
|  | offs = sfoffs[attempt]; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (offs == 0) | 
|  | { | 
|  | if (insns_match_pattern (gdbarch, 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.  */ | 
|  | attempt = 2; | 
|  | sp = pc - pcoffs[attempt]; | 
|  | } | 
|  | else | 
|  | 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[attempt] + 128 + 24; | 
|  | } | 
|  |  | 
|  | struct hppa_linux_sigtramp_unwind_cache | 
|  | { | 
|  | CORE_ADDR base; | 
|  | trad_frame_saved_reg *saved_regs; | 
|  | }; | 
|  |  | 
|  | static struct hppa_linux_sigtramp_unwind_cache * | 
|  | hppa_linux_sigtramp_frame_unwind_cache (frame_info_ptr this_frame, | 
|  | void **this_cache) | 
|  | { | 
|  | struct gdbarch *gdbarch = get_frame_arch (this_frame); | 
|  | struct hppa_linux_sigtramp_unwind_cache *info; | 
|  | CORE_ADDR pc, scptr; | 
|  | int i; | 
|  |  | 
|  | if (*this_cache) | 
|  | return (struct hppa_linux_sigtramp_unwind_cache *) *this_cache; | 
|  |  | 
|  | info = FRAME_OBSTACK_ZALLOC (struct hppa_linux_sigtramp_unwind_cache); | 
|  | *this_cache = info; | 
|  | info->saved_regs = trad_frame_alloc_saved_regs (this_frame); | 
|  |  | 
|  | pc = get_frame_pc (this_frame); | 
|  | scptr = hppa_linux_sigtramp_find_sigcontext (gdbarch, pc); | 
|  |  | 
|  | /* 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.  */ | 
|  | info->saved_regs[HPPA_IPSW_REGNUM].set_addr (scptr); | 
|  | scptr += 4; | 
|  |  | 
|  | /* General registers.  */ | 
|  | for (i = 1; i < 32; i++) | 
|  | { | 
|  | info->saved_regs[HPPA_R0_REGNUM + i].set_addr (scptr); | 
|  | scptr += 4; | 
|  | } | 
|  |  | 
|  | /* Pad to long long boundary.  */ | 
|  | 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)].set_addr (scptr); | 
|  | scptr += 4; | 
|  | info->saved_regs[HPPA_FP0_REGNUM + (i * 2) + 1].set_addr (scptr); | 
|  | scptr += 4; | 
|  | } | 
|  |  | 
|  | /* IASQ/IAOQ.  */ | 
|  | info->saved_regs[HPPA_PCSQ_HEAD_REGNUM].set_addr (scptr); | 
|  | scptr += 4; | 
|  | info->saved_regs[HPPA_PCSQ_TAIL_REGNUM].set_addr (scptr); | 
|  | scptr += 4; | 
|  |  | 
|  | info->saved_regs[HPPA_PCOQ_HEAD_REGNUM].set_addr (scptr); | 
|  | scptr += 4; | 
|  | info->saved_regs[HPPA_PCOQ_TAIL_REGNUM].set_addr (scptr); | 
|  | scptr += 4; | 
|  |  | 
|  | info->saved_regs[HPPA_SAR_REGNUM].set_addr (scptr); | 
|  |  | 
|  | info->base = get_frame_register_unsigned (this_frame, HPPA_SP_REGNUM); | 
|  |  | 
|  | return info; | 
|  | } | 
|  |  | 
|  | static void | 
|  | hppa_linux_sigtramp_frame_this_id (frame_info_ptr this_frame, | 
|  | void **this_prologue_cache, | 
|  | struct frame_id *this_id) | 
|  | { | 
|  | struct hppa_linux_sigtramp_unwind_cache *info | 
|  | = hppa_linux_sigtramp_frame_unwind_cache (this_frame, this_prologue_cache); | 
|  | *this_id = frame_id_build (info->base, get_frame_pc (this_frame)); | 
|  | } | 
|  |  | 
|  | static struct value * | 
|  | hppa_linux_sigtramp_frame_prev_register (frame_info_ptr this_frame, | 
|  | void **this_prologue_cache, | 
|  | int regnum) | 
|  | { | 
|  | struct hppa_linux_sigtramp_unwind_cache *info | 
|  | = hppa_linux_sigtramp_frame_unwind_cache (this_frame, this_prologue_cache); | 
|  | return hppa_frame_prev_register_helper (this_frame, | 
|  | info->saved_regs, regnum); | 
|  | } | 
|  |  | 
|  | /* 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 int | 
|  | hppa_linux_sigtramp_frame_sniffer (const struct frame_unwind *self, | 
|  | frame_info_ptr this_frame, | 
|  | void **this_prologue_cache) | 
|  | { | 
|  | struct gdbarch *gdbarch = get_frame_arch (this_frame); | 
|  | CORE_ADDR pc = get_frame_pc (this_frame); | 
|  |  | 
|  | if (hppa_linux_sigtramp_find_sigcontext (gdbarch, pc)) | 
|  | return 1; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static const struct frame_unwind hppa_linux_sigtramp_frame_unwind = { | 
|  | "hppa linux sigtramp", | 
|  | SIGTRAMP_FRAME, | 
|  | default_frame_unwind_stop_reason, | 
|  | hppa_linux_sigtramp_frame_this_id, | 
|  | hppa_linux_sigtramp_frame_prev_register, | 
|  | NULL, | 
|  | hppa_linux_sigtramp_frame_sniffer | 
|  | }; | 
|  |  | 
|  | /* 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 gdbarch *gdbarch, | 
|  | struct value *function) | 
|  | { | 
|  | enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); | 
|  | 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; | 
|  | gdb_byte buf[4]; | 
|  |  | 
|  | faddr &= ~3; | 
|  |  | 
|  | status = target_read_memory (faddr + 4, buf, sizeof (buf)); | 
|  | if (status == 0) | 
|  | return extract_unsigned_integer (buf, sizeof (buf), byte_order); | 
|  | } | 
|  |  | 
|  | /* 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)) | 
|  | return 0; | 
|  |  | 
|  | faddr_sect = find_pc_section (faddr); | 
|  | if (faddr_sect != NULL) | 
|  | { | 
|  | for (obj_section *osect : faddr_sect->objfile->sections ()) | 
|  | { | 
|  | if (strcmp (osect->the_bfd_section->name, ".dynamic") == 0) | 
|  | { | 
|  | CORE_ADDR addr, endaddr; | 
|  |  | 
|  | addr = osect->addr (); | 
|  | endaddr = osect->endaddr (); | 
|  |  | 
|  | while (addr < endaddr) | 
|  | { | 
|  | int status; | 
|  | LONGEST tag; | 
|  | gdb_byte buf[4]; | 
|  |  | 
|  | status = target_read_memory (addr, buf, sizeof (buf)); | 
|  | if (status != 0) | 
|  | break; | 
|  | tag = extract_signed_integer (buf, byte_order); | 
|  |  | 
|  | 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), | 
|  | byte_order); | 
|  | /* The payoff...  */ | 
|  | return global_pointer; | 
|  | } | 
|  |  | 
|  | if (tag == DT_NULL) | 
|  | break; | 
|  |  | 
|  | addr += 8; | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | 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 | 
|  | */ | 
|  |  | 
|  | static const struct regcache_map_entry hppa_linux_gregmap[] = | 
|  | { | 
|  | { 32, HPPA_R0_REGNUM }, | 
|  | { 1, HPPA_SR4_REGNUM+1 }, | 
|  | { 1, HPPA_SR4_REGNUM+2 }, | 
|  | { 1, HPPA_SR4_REGNUM+3 }, | 
|  | { 1, HPPA_SR4_REGNUM+4 }, | 
|  | { 1, HPPA_SR4_REGNUM }, | 
|  | { 1, HPPA_SR4_REGNUM+5 }, | 
|  | { 1, HPPA_SR4_REGNUM+6 }, | 
|  | { 1, HPPA_SR4_REGNUM+7 }, | 
|  | { 1, HPPA_PCOQ_HEAD_REGNUM }, | 
|  | { 1, HPPA_PCOQ_TAIL_REGNUM }, | 
|  | { 1, HPPA_PCSQ_HEAD_REGNUM }, | 
|  | { 1, HPPA_PCSQ_TAIL_REGNUM }, | 
|  | { 1, HPPA_SAR_REGNUM }, | 
|  | { 1, HPPA_IIR_REGNUM }, | 
|  | { 1, HPPA_ISR_REGNUM }, | 
|  | { 1, HPPA_IOR_REGNUM }, | 
|  | { 1, HPPA_IPSW_REGNUM }, | 
|  | { 1, HPPA_RCR_REGNUM }, | 
|  | { 8, HPPA_TR0_REGNUM }, | 
|  | { 4, HPPA_PID0_REGNUM }, | 
|  | { 1, HPPA_CCR_REGNUM }, | 
|  | { 1, HPPA_EIEM_REGNUM }, | 
|  | { 0 } | 
|  | }; | 
|  |  | 
|  | static const struct regcache_map_entry hppa_linux_fpregmap[] = | 
|  | { | 
|  | /* FIXME: Only works for 32-bit mode.  In 64-bit mode there should | 
|  | be 32 fpregs, 8 bytes each.  */ | 
|  | { 64, HPPA_FP0_REGNUM, 4 }, | 
|  | { 0 } | 
|  | }; | 
|  |  | 
|  | /* HPPA Linux kernel register set.  */ | 
|  | static const struct regset hppa_linux_regset = | 
|  | { | 
|  | hppa_linux_gregmap, | 
|  | regcache_supply_regset, regcache_collect_regset | 
|  | }; | 
|  |  | 
|  | static const struct regset hppa_linux_fpregset = | 
|  | { | 
|  | hppa_linux_fpregmap, | 
|  | regcache_supply_regset, regcache_collect_regset | 
|  | }; | 
|  |  | 
|  | static void | 
|  | hppa_linux_iterate_over_regset_sections (struct gdbarch *gdbarch, | 
|  | iterate_over_regset_sections_cb *cb, | 
|  | void *cb_data, | 
|  | const struct regcache *regcache) | 
|  | { | 
|  | hppa_gdbarch_tdep *tdep = gdbarch_tdep<hppa_gdbarch_tdep> (gdbarch); | 
|  |  | 
|  | cb (".reg", 80 * tdep->bytes_per_address, 80 * tdep->bytes_per_address, | 
|  | &hppa_linux_regset, NULL, cb_data); | 
|  | cb (".reg2", 64 * 4, 64 * 4, &hppa_linux_fpregset, NULL, cb_data); | 
|  | } | 
|  |  | 
|  | static void | 
|  | hppa_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) | 
|  | { | 
|  | hppa_gdbarch_tdep *tdep = gdbarch_tdep<hppa_gdbarch_tdep> (gdbarch); | 
|  |  | 
|  | linux_init_abi (info, gdbarch, 0); | 
|  |  | 
|  | /* 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_unwinder (gdbarch, &hppa_linux_sigtramp_frame_unwind); | 
|  |  | 
|  | /* GNU/Linux uses SVR4-style shared libraries.  */ | 
|  | set_solib_svr4_fetch_link_map_offsets | 
|  | (gdbarch, linux_ilp32_fetch_link_map_offsets); | 
|  |  | 
|  | 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_long_double_format (gdbarch, floatformats_ieee_double); | 
|  |  | 
|  | set_gdbarch_iterate_over_regset_sections | 
|  | (gdbarch, hppa_linux_iterate_over_regset_sections); | 
|  |  | 
|  | set_gdbarch_dwarf2_reg_to_regnum (gdbarch, hppa_dwarf_reg_to_regnum); | 
|  |  | 
|  | /* Enable TLS support.  */ | 
|  | set_gdbarch_fetch_tls_load_module_address (gdbarch, | 
|  | svr4_fetch_objfile_link_map); | 
|  | } | 
|  |  | 
|  | void _initialize_hppa_linux_tdep (); | 
|  | void | 
|  | _initialize_hppa_linux_tdep () | 
|  | { | 
|  | 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); | 
|  | } |