+ /* First try the lowest address in the section; we can use it as long
+ as it is "regular" code (i.e. not a stub). */
+ u = find_unwind_entry (obj_section_addr (sec));
+ if (!u || u->stub_unwind.stub_type == 0)
+ return obj_section_addr (sec);
+
+ /* Otherwise, we need to find a symbol for a regular function. We
+ do this by walking the list of msymbols in the objfile. The symbol
+ we find should not be the same as the function that was passed in. */
+
+ /* FIXME: this is broken, because we can find a function that will be
+ called by the dummy call target function, which will still not
+ work. */
+
+ find_pc_partial_function (addr, NULL, &func, NULL);
+ for (i = 0, msym = sec->objfile->msymbols;
+ i < sec->objfile->minimal_symbol_count;
+ i++, msym++)
+ {
+ u = find_unwind_entry (SYMBOL_VALUE_ADDRESS (msym));
+ if (func != SYMBOL_VALUE_ADDRESS (msym)
+ && (!u || u->stub_unwind.stub_type == 0))
+ return SYMBOL_VALUE_ADDRESS (msym);
+ }
+ }
+
+ warning (_("Cannot find suitable address to place dummy breakpoint; nested "
+ "calls may fail."));
+ return addr - 4;
+}
+
+static CORE_ADDR
+hppa_hpux_push_dummy_code (struct gdbarch *gdbarch, CORE_ADDR sp,
+ CORE_ADDR funcaddr,
+ struct value **args, int nargs,
+ struct type *value_type,
+ CORE_ADDR *real_pc, CORE_ADDR *bp_addr,
+ struct regcache *regcache)
+{
+ CORE_ADDR pc, stubaddr;
+ int argreg = 0;
+
+ pc = regcache_read_pc (regcache);
+
+ /* Note: we don't want to pass a function descriptor here; push_dummy_call
+ fills in the PIC register for us. */
+ funcaddr = gdbarch_convert_from_func_ptr_addr (gdbarch, funcaddr, NULL);
+
+ /* The simple case is where we call a function in the same space that we are
+ currently in; in that case we don't really need to do anything. */
+ if (hppa_hpux_sr_for_addr (gdbarch, pc)
+ == hppa_hpux_sr_for_addr (gdbarch, funcaddr))
+ {
+ /* Intraspace call. */
+ *bp_addr = hppa_hpux_find_dummy_bpaddr (pc);
+ *real_pc = funcaddr;
+ regcache_cooked_write_unsigned (regcache, HPPA_RP_REGNUM, *bp_addr);
+
+ return sp;
+ }
+
+ /* In order to make an interspace call, we need to go through a stub.
+ gcc supplies an appropriate stub called "__gcc_plt_call", however, if
+ an application is compiled with HP compilers then this stub is not
+ available. We used to fallback to "__d_plt_call", however that stub
+ is not entirely useful for us because it doesn't do an interspace
+ return back to the caller. Also, on hppa64-hpux, there is no
+ __gcc_plt_call available. In order to keep the code uniform, we
+ instead don't use either of these stubs, but instead write our own
+ onto the stack.
+
+ A problem arises since the stack is located in a different space than
+ code, so in order to branch to a stack stub, we will need to do an
+ interspace branch. Previous versions of gdb did this by modifying code
+ at the current pc and doing single-stepping to set the pcsq. Since this
+ is highly undesirable, we use a different scheme:
+
+ All we really need to do the branch to the stub is a short instruction
+ sequence like this:
+
+ PA1.1:
+ ldsid (rX),r1
+ mtsp r1,sr0
+ be,n (sr0,rX)
+
+ PA2.0:
+ bve,n (sr0,rX)
+
+ Instead of writing these sequences ourselves, we can find it in
+ the instruction stream that belongs to the current space. While this
+ seems difficult at first, we are actually guaranteed to find the sequences
+ in several places:
+
+ For 32-bit code:
+ - in export stubs for shared libraries
+ - in the "noshlibs" routine in the main module
+
+ For 64-bit code:
+ - at the end of each "regular" function
+
+ We cache the address of these sequences in the objfile's private data
+ since these operations can potentially be quite expensive.
+
+ So, what we do is:
+ - write a stack trampoline
+ - look for a suitable instruction sequence in the current space
+ - point the sequence at the trampoline
+ - set the return address of the trampoline to the current space
+ (see hppa_hpux_find_dummy_call_bpaddr)
+ - set the continuing address of the "dummy code" as the sequence. */
+
+ if (IS_32BIT_TARGET (gdbarch))
+ {
+ static unsigned int hppa32_tramp[] = {
+ 0x0fdf1291, /* stw r31,-8(,sp) */
+ 0x02c010a1, /* ldsid (,r22),r1 */
+ 0x00011820, /* mtsp r1,sr0 */
+ 0xe6c00000, /* be,l 0(sr0,r22),%sr0,%r31 */
+ 0x081f0242, /* copy r31,rp */
+ 0x0fd11082, /* ldw -8(,sp),rp */
+ 0x004010a1, /* ldsid (,rp),r1 */
+ 0x00011820, /* mtsp r1,sr0 */
+ 0xe0400000, /* be 0(sr0,rp) */
+ 0x08000240 /* nop */
+ };
+
+ /* for hppa32, we must call the function through a stub so that on
+ return it can return to the space of our trampoline. */
+ stubaddr = hppa_hpux_find_import_stub_for_addr (funcaddr);
+ if (stubaddr == 0)
+ error (_("Cannot call external function not referenced by application "
+ "(no import stub).\n"));
+ regcache_cooked_write_unsigned (regcache, 22, stubaddr);
+
+ write_memory (sp, (char *)&hppa32_tramp, sizeof (hppa32_tramp));
+
+ *bp_addr = hppa_hpux_find_dummy_bpaddr (pc);
+ regcache_cooked_write_unsigned (regcache, 31, *bp_addr);
+
+ *real_pc = hppa32_hpux_search_dummy_call_sequence (gdbarch, pc, &argreg);
+ if (*real_pc == 0)
+ error (_("Cannot make interspace call from here."));
+
+ regcache_cooked_write_unsigned (regcache, argreg, sp);
+
+ sp += sizeof (hppa32_tramp);
+ }
+ else
+ {
+ static unsigned int hppa64_tramp[] = {
+ 0xeac0f000, /* bve,l (r22),%r2 */
+ 0x0fdf12d1, /* std r31,-8(,sp) */
+ 0x0fd110c2, /* ldd -8(,sp),rp */
+ 0xe840d002, /* bve,n (rp) */
+ 0x08000240 /* nop */
+ };
+
+ /* for hppa64, we don't need to call through a stub; all functions
+ return via a bve. */
+ regcache_cooked_write_unsigned (regcache, 22, funcaddr);
+ write_memory (sp, (char *)&hppa64_tramp, sizeof (hppa64_tramp));
+
+ *bp_addr = pc - 4;
+ regcache_cooked_write_unsigned (regcache, 31, *bp_addr);
+
+ *real_pc = hppa64_hpux_search_dummy_call_sequence (gdbarch, pc, &argreg);
+ if (*real_pc == 0)
+ error (_("Cannot make interspace call from here."));
+
+ regcache_cooked_write_unsigned (regcache, argreg, sp);
+
+ sp += sizeof (hppa64_tramp);
+ }
+
+ sp = gdbarch_frame_align (gdbarch, sp);
+
+ return sp;
+}
+
+\f
+
+static void
+hppa_hpux_supply_ss_narrow (struct regcache *regcache,
+ int regnum, const char *save_state)
+{
+ const char *ss_narrow = save_state + HPPA_HPUX_SS_NARROW_OFFSET;
+ int i, offset = 0;
+
+ for (i = HPPA_R1_REGNUM; i < HPPA_FP0_REGNUM; i++)
+ {
+ if (regnum == i || regnum == -1)
+ regcache_raw_supply (regcache, i, ss_narrow + offset);
+
+ offset += 4;
+ }
+}
+
+static void
+hppa_hpux_supply_ss_fpblock (struct regcache *regcache,
+ int regnum, const char *save_state)
+{
+ const char *ss_fpblock = save_state + HPPA_HPUX_SS_FPBLOCK_OFFSET;
+ int i, offset = 0;
+
+ /* FIXME: We view the floating-point state as 64 single-precision
+ registers for 32-bit code, and 32 double-precision register for
+ 64-bit code. This distinction is artificial and should be
+ eliminated. If that ever happens, we should remove the if-clause
+ below. */
+
+ if (register_size (get_regcache_arch (regcache), HPPA_FP0_REGNUM) == 4)
+ {
+ for (i = HPPA_FP0_REGNUM; i < HPPA_FP0_REGNUM + 64; i++)
+ {
+ if (regnum == i || regnum == -1)
+ regcache_raw_supply (regcache, i, ss_fpblock + offset);
+
+ offset += 4;
+ }
+ }
+ else
+ {
+ for (i = HPPA_FP0_REGNUM; i < HPPA_FP0_REGNUM + 32; i++)
+ {
+ if (regnum == i || regnum == -1)
+ regcache_raw_supply (regcache, i, ss_fpblock + offset);
+
+ offset += 8;
+ }
+ }
+}
+
+static void
+hppa_hpux_supply_ss_wide (struct regcache *regcache,
+ int regnum, const char *save_state)
+{
+ const char *ss_wide = save_state + HPPA_HPUX_SS_WIDE_OFFSET;
+ int i, offset = 8;
+
+ if (register_size (get_regcache_arch (regcache), HPPA_R1_REGNUM) == 4)
+ offset += 4;
+
+ for (i = HPPA_R1_REGNUM; i < HPPA_FP0_REGNUM; i++)
+ {
+ if (regnum == i || regnum == -1)
+ regcache_raw_supply (regcache, i, ss_wide + offset);
+
+ offset += 8;
+ }
+}
+
+static void
+hppa_hpux_supply_save_state (const struct regset *regset,
+ struct regcache *regcache,
+ int regnum, const void *regs, size_t len)
+{
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ const char *proc_info = regs;
+ const char *save_state = proc_info + 8;
+ ULONGEST flags;
+
+ flags = extract_unsigned_integer (save_state + HPPA_HPUX_SS_FLAGS_OFFSET,
+ 4, byte_order);
+ if (regnum == -1 || regnum == HPPA_FLAGS_REGNUM)
+ {
+ size_t size = register_size (gdbarch, HPPA_FLAGS_REGNUM);
+ char buf[8];
+
+ store_unsigned_integer (buf, size, byte_order, flags);
+ regcache_raw_supply (regcache, HPPA_FLAGS_REGNUM, buf);
+ }
+
+ /* If the SS_WIDEREGS flag is set, we really do need the full
+ `struct save_state'. */
+ if (flags & HPPA_HPUX_SS_WIDEREGS && len < HPPA_HPUX_SAVE_STATE_SIZE)
+ error (_("Register set contents too small"));
+
+ if (flags & HPPA_HPUX_SS_WIDEREGS)
+ hppa_hpux_supply_ss_wide (regcache, regnum, save_state);
+ else
+ hppa_hpux_supply_ss_narrow (regcache, regnum, save_state);
+
+ hppa_hpux_supply_ss_fpblock (regcache, regnum, save_state);
+}
+
+/* HP-UX register set. */
+
+static struct regset hppa_hpux_regset =
+{
+ NULL,
+ hppa_hpux_supply_save_state
+};
+
+static const struct regset *
+hppa_hpux_regset_from_core_section (struct gdbarch *gdbarch,
+ const char *sect_name, size_t sect_size)
+{
+ if (strcmp (sect_name, ".reg") == 0
+ && sect_size >= HPPA_HPUX_PA89_SAVE_STATE_SIZE + 8)
+ return &hppa_hpux_regset;
+
+ return NULL;
+}
+\f
+
+/* Bit in the `ss_flag' member of `struct save_state' that indicates
+ the state was saved from a system call. From
+ <machine/save_state.h>. */
+#define HPPA_HPUX_SS_INSYSCALL 0x02
+
+static CORE_ADDR
+hppa_hpux_read_pc (struct regcache *regcache)
+{
+ ULONGEST flags;
+
+ /* If we're currently in a system call return the contents of %r31. */
+ regcache_cooked_read_unsigned (regcache, HPPA_FLAGS_REGNUM, &flags);
+ if (flags & HPPA_HPUX_SS_INSYSCALL)
+ {
+ ULONGEST pc;
+ regcache_cooked_read_unsigned (regcache, HPPA_R31_REGNUM, &pc);
+ return pc & ~0x3;
+ }
+
+ return hppa_read_pc (regcache);
+}
+
+static void
+hppa_hpux_write_pc (struct regcache *regcache, CORE_ADDR pc)
+{
+ ULONGEST flags;
+
+ /* If we're currently in a system call also write PC into %r31. */
+ regcache_cooked_read_unsigned (regcache, HPPA_FLAGS_REGNUM, &flags);
+ if (flags & HPPA_HPUX_SS_INSYSCALL)
+ regcache_cooked_write_unsigned (regcache, HPPA_R31_REGNUM, pc | 0x3);
+
+ hppa_write_pc (regcache, pc);
+}
+
+static CORE_ADDR
+hppa_hpux_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+ ULONGEST flags;
+
+ /* If we're currently in a system call return the contents of %r31. */
+ flags = frame_unwind_register_unsigned (next_frame, HPPA_FLAGS_REGNUM);
+ if (flags & HPPA_HPUX_SS_INSYSCALL)
+ return frame_unwind_register_unsigned (next_frame, HPPA_R31_REGNUM) & ~0x3;
+
+ return hppa_unwind_pc (gdbarch, next_frame);
+}
+\f
+
+/* Given the current value of the pc, check to see if it is inside a stub, and
+ if so, change the value of the pc to point to the caller of the stub.
+ THIS_FRAME is the current frame in the current list of frames.
+ BASE contains to stack frame base of the current frame.
+ SAVE_REGS is the register file stored in the frame cache. */
+static void
+hppa_hpux_unwind_adjust_stub (struct frame_info *this_frame, CORE_ADDR base,
+ struct trad_frame_saved_reg *saved_regs)
+{
+ struct gdbarch *gdbarch = get_frame_arch (this_frame);
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ int word_size = gdbarch_ptr_bit (gdbarch) / 8;
+ struct value *pcoq_head_val;
+ ULONGEST pcoq_head;
+ CORE_ADDR stubpc;
+ struct unwind_table_entry *u;
+
+ pcoq_head_val = trad_frame_get_prev_register (this_frame, saved_regs,
+ HPPA_PCOQ_HEAD_REGNUM);
+ pcoq_head =
+ extract_unsigned_integer (value_contents_all (pcoq_head_val),
+ register_size (gdbarch, HPPA_PCOQ_HEAD_REGNUM),
+ byte_order);
+
+ u = find_unwind_entry (pcoq_head);
+ if (u && u->stub_unwind.stub_type == EXPORT)
+ {
+ stubpc = read_memory_integer (base - 24, word_size, byte_order);
+ trad_frame_set_value (saved_regs, HPPA_PCOQ_HEAD_REGNUM, stubpc);
+ }
+ else if (hppa_symbol_address ("__gcc_plt_call")
+ == get_pc_function_start (pcoq_head))
+ {
+ stubpc = read_memory_integer (base - 8, word_size, byte_order);
+ trad_frame_set_value (saved_regs, HPPA_PCOQ_HEAD_REGNUM, stubpc);