-/* Machine-dependent code which would otherwise be in inflow.c and core.c,
- for GDB, the GNU debugger. This code is for the HP PA-RISC cpu.
- Copyright 1986, 1987, 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+/* Target-dependent code for the HP PA architecture, for GDB.
+ Copyright 1986, 1987, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996
+ Free Software Foundation, Inc.
Contributed by the Center for Software Science at the
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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#include "defs.h"
#include "frame.h"
#endif
#include <sys/param.h>
-#include <sys/dir.h>
#include <signal.h>
-#include <sys/ioctl.h>
#ifdef COFF_ENCAPSULATE
#include "a.out.encap.h"
#else
-#include <a.out.h>
#endif
#ifndef N_SET_MAGIC
#define N_SET_MAGIC(exec, val) ((exec).a_magic = (val))
/*#include <sys/user.h> After a.out.h */
#include <sys/file.h>
-#include <sys/stat.h>
-#include <machine/psl.h>
+#include "gdb_stat.h"
#include "wait.h"
#include "gdbcore.h"
#include "symfile.h"
#include "objfiles.h"
-static int restore_pc_queue PARAMS ((struct frame_saved_regs *fsr));
-static int hppa_alignof PARAMS ((struct type *arg));
-CORE_ADDR frame_saved_pc PARAMS ((FRAME frame));
+static int restore_pc_queue PARAMS ((struct frame_saved_regs *));
+
+static int hppa_alignof PARAMS ((struct type *));
+
+CORE_ADDR frame_saved_pc PARAMS ((struct frame_info *));
+
static int prologue_inst_adjust_sp PARAMS ((unsigned long));
+
static int is_branch PARAMS ((unsigned long));
+
static int inst_saves_gr PARAMS ((unsigned long));
+
static int inst_saves_fr PARAMS ((unsigned long));
+
static int pc_in_interrupt_handler PARAMS ((CORE_ADDR));
+
static int pc_in_linker_stub PARAMS ((CORE_ADDR));
-static int compare_unwind_entries PARAMS ((const struct unwind_table_entry *,
- const struct unwind_table_entry *));
+
+static int compare_unwind_entries PARAMS ((const void *, const void *));
+
static void read_unwind_info PARAMS ((struct objfile *));
+
static void internalize_unwinds PARAMS ((struct objfile *,
struct unwind_table_entry *,
asection *, unsigned int,
- unsigned int));
+ unsigned int, CORE_ADDR));
+static void pa_print_registers PARAMS ((char *, int, int));
+static void pa_print_fp_reg PARAMS ((int));
\f
/* Routines to extract various sized constants out of hppa
(word & 0x1) << 11, 12) << 2;
}
+/* Deposit a 17 bit constant in an instruction (like bl). */
+
+unsigned int
+deposit_17 (opnd, word)
+ unsigned opnd, word;
+{
+ word |= GET_FIELD (opnd, 15 + 0, 15 + 0); /* w */
+ word |= GET_FIELD (opnd, 15 + 1, 15 + 5) << 16; /* w1 */
+ word |= GET_FIELD (opnd, 15 + 6, 15 + 6) << 2; /* w2[10] */
+ word |= GET_FIELD (opnd, 15 + 7, 15 + 16) << 3; /* w2[0..9] */
+
+ return word;
+}
+
/* extract a 17 bit constant from branch instructions, returning the
19 bit signed value. */
larger than the first, and zero if they are equal. */
static int
-compare_unwind_entries (a, b)
- const struct unwind_table_entry *a;
- const struct unwind_table_entry *b;
+compare_unwind_entries (arg1, arg2)
+ const void *arg1;
+ const void *arg2;
{
+ const struct unwind_table_entry *a = arg1;
+ const struct unwind_table_entry *b = arg2;
+
if (a->region_start > b->region_start)
return 1;
else if (a->region_start < b->region_start)
}
static void
-internalize_unwinds (objfile, table, section, entries, size)
+internalize_unwinds (objfile, table, section, entries, size, text_offset)
struct objfile *objfile;
struct unwind_table_entry *table;
asection *section;
unsigned int entries, size;
+ CORE_ADDR text_offset;
{
/* We will read the unwind entries into temporary memory, then
fill in the actual unwind table. */
{
table[i].region_start = bfd_get_32 (objfile->obfd,
(bfd_byte *)buf);
+ table[i].region_start += text_offset;
buf += 4;
table[i].region_end = bfd_get_32 (objfile->obfd, (bfd_byte *)buf);
+ table[i].region_end += text_offset;
buf += 4;
tmp = bfd_get_32 (objfile->obfd, (bfd_byte *)buf);
buf += 4;
- table[i].Cannot_unwind = (tmp >> 31) & 0x1;;
+ table[i].Cannot_unwind = (tmp >> 31) & 0x1;
table[i].Millicode = (tmp >> 30) & 0x1;
table[i].Millicode_save_sr0 = (tmp >> 29) & 0x1;
table[i].Region_description = (tmp >> 27) & 0x3;
unsigned unwind_size, elf_unwind_size, stub_unwind_size, total_size;
unsigned index, unwind_entries, elf_unwind_entries;
unsigned stub_entries, total_entries;
+ CORE_ADDR text_offset;
struct obj_unwind_info *ui;
- ui = obstack_alloc (&objfile->psymbol_obstack,
- sizeof (struct obj_unwind_info));
+ text_offset = ANOFFSET (objfile->section_offsets, 0);
+ ui = (struct obj_unwind_info *)obstack_alloc (&objfile->psymbol_obstack,
+ sizeof (struct obj_unwind_info));
ui->table = NULL;
ui->cache = NULL;
/* Internalize the standard unwind entries. */
index = 0;
internalize_unwinds (objfile, &ui->table[index], unwind_sec,
- unwind_entries, unwind_size);
+ unwind_entries, unwind_size, text_offset);
index += unwind_entries;
internalize_unwinds (objfile, &ui->table[index], elf_unwind_sec,
- elf_unwind_entries, elf_unwind_size);
+ elf_unwind_entries, elf_unwind_size, text_offset);
index += elf_unwind_entries;
/* Now internalize the stub unwind entries. */
Stuff away the stub type into "reserved" fields. */
ui->table[index].region_start = bfd_get_32 (objfile->obfd,
(bfd_byte *) buf);
+ ui->table[index].region_start += text_offset;
buf += 4;
ui->table[index].stub_type = bfd_get_8 (objfile->obfd,
(bfd_byte *) buf);
contains a sorted list of struct unwind_table_entry. Since we do a binary
search of the unwind tables, we depend upon them to be sorted. */
-static struct unwind_table_entry *
+struct unwind_table_entry *
find_unwind_entry(pc)
CORE_ADDR pc;
{
return NULL;
}
-/* start-sanitize-hpread */
/* Return the adjustment necessary to make for addresses on the stack
as presented by hpread.c.
else
return u->Total_frame_size << 3;
}
-/* end-sanitize-hpread */
/* Called to determine if PC is in an interrupt handler of some
kind. */
switch (u->stub_type)
{
case EXPORT:
+ case IMPORT:
return -24;
case PARAMETER_RELOCATION:
return -8;
\f
int
frameless_function_invocation (frame)
- FRAME frame;
+ struct frame_info *frame;
{
struct unwind_table_entry *u;
CORE_ADDR
saved_pc_after_call (frame)
- FRAME frame;
+ struct frame_info *frame;
{
int ret_regnum;
CORE_ADDR pc;
\f
CORE_ADDR
frame_saved_pc (frame)
- FRAME frame;
+ struct frame_info *frame;
{
CORE_ADDR pc = get_frame_pc (frame);
struct unwind_table_entry *u;
if (pc_in_interrupt_handler (pc))
return read_memory_integer (frame->frame + PC_REGNUM * 4, 4) & ~0x3;
+#ifdef FRAME_SAVED_PC_IN_SIGTRAMP
/* Deal with signal handler caller frames too. */
if (frame->signal_handler_caller)
{
CORE_ADDR rp;
FRAME_SAVED_PC_IN_SIGTRAMP (frame, &rp);
- return rp;
+ return rp & ~0x3;
}
+#endif
if (frameless_function_invocation (frame))
{
&& (frame->next->signal_handler_caller
|| pc_in_interrupt_handler (frame->next->pc)))
{
- struct frame_info *fi;
struct frame_saved_regs saved_regs;
- fi = get_frame_info (frame->next);
- get_frame_saved_regs (fi, &saved_regs);
+ get_frame_saved_regs (frame->next, &saved_regs);
if (read_memory_integer (saved_regs.regs[FLAGS_REGNUM], 4) & 0x2)
- pc = read_memory_integer (saved_regs.regs[31], 4) & ~0x3;
+ {
+ pc = read_memory_integer (saved_regs.regs[31], 4) & ~0x3;
+
+ /* Syscalls are really two frames. The syscall stub itself
+ with a return pointer in %rp and the kernel call with
+ a return pointer in %r31. We return the %rp variant
+ if %r31 is the same as frame->pc. */
+ if (pc == frame->pc)
+ pc = read_memory_integer (saved_regs.regs[RP_REGNUM], 4) & ~0x3;
+ }
else
pc = read_memory_integer (saved_regs.regs[RP_REGNUM], 4) & ~0x3;
}
&& (frame->next->signal_handler_caller
|| pc_in_interrupt_handler (frame->next->pc)))
{
- struct frame_info *fi;
struct frame_saved_regs saved_regs;
- fi = get_frame_info (frame->next);
- get_frame_saved_regs (fi, &saved_regs);
+ get_frame_saved_regs (frame->next, &saved_regs);
if (read_memory_integer (saved_regs.regs[FLAGS_REGNUM], 4) & 0x2)
- pc = read_memory_integer (saved_regs.regs[31], 4) & ~0x3;
+ {
+ pc = read_memory_integer (saved_regs.regs[31], 4) & ~0x3;
+
+ /* Syscalls are really two frames. The syscall stub itself
+ with a return pointer in %rp and the kernel call with
+ a return pointer in %r31. We return the %rp variant
+ if %r31 is the same as frame->pc. */
+ if (pc == frame->pc)
+ pc = read_memory_integer (saved_regs.regs[RP_REGNUM], 4) & ~0x3;
+ }
else
pc = read_memory_integer (saved_regs.regs[RP_REGNUM], 4) & ~0x3;
}
}
/* If PC is inside a linker stub, then dig out the address the stub
- will return to. */
+ will return to.
+
+ Don't do this for long branch stubs. Why? For some unknown reason
+ _start is marked as a long branch stub in hpux10. */
u = find_unwind_entry (pc);
- if (u && u->stub_type != 0)
- goto restart;
+ if (u && u->stub_type != 0
+ && u->stub_type != LONG_BRANCH)
+ {
+ unsigned int insn;
+
+ /* If this is a dynamic executable, and we're in a signal handler,
+ then the call chain will eventually point us into the stub for
+ _sigreturn. Unlike most cases, we'll be pointed to the branch
+ to the real sigreturn rather than the code after the real branch!.
+
+ Else, try to dig the address the stub will return to in the normal
+ fashion. */
+ insn = read_memory_integer (pc, 4);
+ if ((insn & 0xfc00e000) == 0xe8000000)
+ return (pc + extract_17 (insn) + 8) & ~0x3;
+ else
+ goto restart;
+ }
return pc;
}
This may involve searching through prologues for several functions
at boundaries where GCC calls HP C code, or where code which has
a frame pointer calls code without a frame pointer. */
-
-FRAME_ADDR
+CORE_ADDR
frame_chain (frame)
struct frame_info *frame;
{
int my_framesize, caller_framesize;
struct unwind_table_entry *u;
CORE_ADDR frame_base;
+ struct frame_info *tmp_frame;
/* Handle HPUX, BSD, and OSF1 style interrupt frames first. These
are easy; at *sp we have a full save state strucutre which we can
code to dig a saved PC out of the save state structure. */
if (pc_in_interrupt_handler (frame->pc))
frame_base = read_memory_integer (frame->frame + SP_REGNUM * 4, 4);
+#ifdef FRAME_BASE_BEFORE_SIGTRAMP
else if (frame->signal_handler_caller)
{
FRAME_BASE_BEFORE_SIGTRAMP (frame, &frame_base);
}
+#endif
else
frame_base = frame->frame;
We use information from unwind descriptors to determine if %r3
is saved into the stack (Entry_GR field has this information). */
- while (frame)
+ tmp_frame = frame;
+ while (tmp_frame)
{
- u = find_unwind_entry (frame->pc);
+ u = find_unwind_entry (tmp_frame->pc);
if (!u)
{
think anyone has actually written any tools (not even "strip")
which leave them out of an executable, so maybe this is a moot
point. */
- warning ("Unable to find unwind for PC 0x%x -- Help!", frame->pc);
+ warning ("Unable to find unwind for PC 0x%x -- Help!", tmp_frame->pc);
return 0;
}
/* Entry_GR specifies the number of callee-saved general registers
saved in the stack. It starts at %r3, so %r3 would be 1. */
if (u->Entry_GR >= 1 || u->Save_SP
- || frame->signal_handler_caller
- || pc_in_interrupt_handler (frame->pc))
+ || tmp_frame->signal_handler_caller
+ || pc_in_interrupt_handler (tmp_frame->pc))
break;
else
- frame = frame->next;
+ tmp_frame = tmp_frame->next;
}
- if (frame)
+ if (tmp_frame)
{
/* We may have walked down the chain into a function with a frame
pointer. */
if (u->Save_SP
- && !frame->signal_handler_caller
- && !pc_in_interrupt_handler (frame->pc))
- return read_memory_integer (frame->frame, 4);
+ && !tmp_frame->signal_handler_caller
+ && !pc_in_interrupt_handler (tmp_frame->pc))
+ return read_memory_integer (tmp_frame->frame, 4);
/* %r3 was saved somewhere in the stack. Dig it out. */
else
{
- struct frame_info *fi;
struct frame_saved_regs saved_regs;
- fi = get_frame_info (frame);
- get_frame_saved_regs (fi, &saved_regs);
+ /* Sick.
+
+ For optimization purposes many kernels don't have the
+ callee saved registers into the save_state structure upon
+ entry into the kernel for a syscall; the optimization
+ is usually turned off if the process is being traced so
+ that the debugger can get full register state for the
+ process.
+
+ This scheme works well except for two cases:
+
+ * Attaching to a process when the process is in the
+ kernel performing a system call (debugger can't get
+ full register state for the inferior process since
+ the process wasn't being traced when it entered the
+ system call).
+
+ * Register state is not complete if the system call
+ causes the process to core dump.
+
+
+ The following heinous code is an attempt to deal with
+ the lack of register state in a core dump. It will
+ fail miserably if the function which performs the
+ system call has a variable sized stack frame. */
+
+ get_frame_saved_regs (tmp_frame, &saved_regs);
+
+ /* Abominable hack. */
+ if (current_target.to_has_execution == 0
+ && ((saved_regs.regs[FLAGS_REGNUM]
+ && (read_memory_integer (saved_regs.regs[FLAGS_REGNUM], 4)
+ & 0x2))
+ || (saved_regs.regs[FLAGS_REGNUM] == 0
+ && read_register (FLAGS_REGNUM) & 0x2)))
+ {
+ u = find_unwind_entry (FRAME_SAVED_PC (frame));
+ if (!u)
+ return read_memory_integer (saved_regs.regs[FP_REGNUM], 4);
+ else
+ return frame_base - (u->Total_frame_size << 3);
+ }
+
return read_memory_integer (saved_regs.regs[FP_REGNUM], 4);
}
}
else
{
+ struct frame_saved_regs saved_regs;
+
+ /* Get the innermost frame. */
+ tmp_frame = frame;
+ while (tmp_frame->next != NULL)
+ tmp_frame = tmp_frame->next;
+
+ get_frame_saved_regs (tmp_frame, &saved_regs);
+ /* Abominable hack. See above. */
+ if (current_target.to_has_execution == 0
+ && ((saved_regs.regs[FLAGS_REGNUM]
+ && (read_memory_integer (saved_regs.regs[FLAGS_REGNUM], 4)
+ & 0x2))
+ || (saved_regs.regs[FLAGS_REGNUM] == 0
+ && read_register (FLAGS_REGNUM) & 0x2)))
+ {
+ u = find_unwind_entry (FRAME_SAVED_PC (frame));
+ if (!u)
+ return read_memory_integer (saved_regs.regs[FP_REGNUM], 4);
+ else
+ return frame_base - (u->Total_frame_size << 3);
+ }
+
/* The value in %r3 was never saved into the stack (thus %r3 still
holds the value of the previous frame pointer). */
return read_register (FP_REGNUM);
int
frame_chain_valid (chain, thisframe)
- FRAME_ADDR chain;
- FRAME thisframe;
+ CORE_ADDR chain;
+ struct frame_info *thisframe;
{
struct minimal_symbol *msym_us;
struct minimal_symbol *msym_start;
struct unwind_table_entry *u, *next_u = NULL;
- FRAME next;
+ struct frame_info *next;
if (!chain)
return 0;
which is (legitimately, since it is in the user's namespace)
named Ltext_end, so we can't just ignore it. */
msym_us = lookup_minimal_symbol_by_pc (FRAME_SAVED_PC (thisframe));
- msym_start = lookup_minimal_symbol ("_start", NULL);
+ msym_start = lookup_minimal_symbol ("_start", NULL, NULL);
+ if (msym_us
+ && msym_start
+ && SYMBOL_VALUE_ADDRESS (msym_us) == SYMBOL_VALUE_ADDRESS (msym_start))
+ return 0;
+
+ /* Grrrr. Some new idiot decided that they don't want _start for the
+ PRO configurations; $START$ calls main directly.... Deal with it. */
+ msym_start = lookup_minimal_symbol ("$START$", NULL, NULL);
if (msym_us
&& msym_start
&& SYMBOL_VALUE_ADDRESS (msym_us) == SYMBOL_VALUE_ADDRESS (msym_start))
* to be aligned to a 64-byte boundary.
*/
-int
-push_dummy_frame ()
+void
+push_dummy_frame (inf_status)
+ struct inferior_status *inf_status;
{
- register CORE_ADDR sp;
+ CORE_ADDR sp, pc, pcspace;
register int regnum;
int int_buffer;
double freg_buffer;
+ /* Oh, what a hack. If we're trying to perform an inferior call
+ while the inferior is asleep, we have to make sure to clear
+ the "in system call" bit in the flag register (the call will
+ start after the syscall returns, so we're no longer in the system
+ call!) This state is kept in "inf_status", change it there.
+
+ We also need a number of horrid hacks to deal with lossage in the
+ PC queue registers (apparently they're not valid when the in syscall
+ bit is set). */
+ pc = target_read_pc (inferior_pid);
+ int_buffer = read_register (FLAGS_REGNUM);
+ if (int_buffer & 0x2)
+ {
+ unsigned int sid;
+ int_buffer &= ~0x2;
+ memcpy (inf_status->registers, &int_buffer, 4);
+ memcpy (inf_status->registers + REGISTER_BYTE (PCOQ_HEAD_REGNUM), &pc, 4);
+ pc += 4;
+ memcpy (inf_status->registers + REGISTER_BYTE (PCOQ_TAIL_REGNUM), &pc, 4);
+ pc -= 4;
+ sid = (pc >> 30) & 0x3;
+ if (sid == 0)
+ pcspace = read_register (SR4_REGNUM);
+ else
+ pcspace = read_register (SR4_REGNUM + 4 + sid);
+ memcpy (inf_status->registers + REGISTER_BYTE (PCSQ_HEAD_REGNUM),
+ &pcspace, 4);
+ memcpy (inf_status->registers + REGISTER_BYTE (PCSQ_TAIL_REGNUM),
+ &pcspace, 4);
+ }
+ else
+ pcspace = read_register (PCSQ_HEAD_REGNUM);
+
/* Space for "arguments"; the RP goes in here. */
sp = read_register (SP_REGNUM) + 48;
int_buffer = read_register (RP_REGNUM) | 0x3;
}
sp = push_word (sp, read_register (IPSW_REGNUM));
sp = push_word (sp, read_register (SAR_REGNUM));
- sp = push_word (sp, read_register (PCOQ_HEAD_REGNUM));
- sp = push_word (sp, read_register (PCSQ_HEAD_REGNUM));
- sp = push_word (sp, read_register (PCOQ_TAIL_REGNUM));
- sp = push_word (sp, read_register (PCSQ_TAIL_REGNUM));
+ sp = push_word (sp, pc);
+ sp = push_word (sp, pcspace);
+ sp = push_word (sp, pc + 4);
+ sp = push_word (sp, pcspace);
write_register (SP_REGNUM, sp);
}
+void
find_dummy_frame_regs (frame, frame_saved_regs)
struct frame_info *frame;
struct frame_saved_regs *frame_saved_regs;
frame_saved_regs->regs[PCSQ_TAIL_REGNUM] = fp + 20;
}
-int
+void
hppa_pop_frame ()
{
- register FRAME frame = get_current_frame ();
- register CORE_ADDR fp;
+ register struct frame_info *frame = get_current_frame ();
+ register CORE_ADDR fp, npc, target_pc;
register int regnum;
struct frame_saved_regs fsr;
- struct frame_info *fi;
double freg_buffer;
- fi = get_frame_info (frame);
- fp = fi->frame;
- get_frame_saved_regs (fi, &fsr);
+ fp = FRAME_FP (frame);
+ get_frame_saved_regs (frame, &fsr);
#ifndef NO_PC_SPACE_QUEUE_RESTORE
if (fsr.regs[IPSW_REGNUM]) /* Restoring a call dummy frame */
/* If the PC was explicitly saved, then just restore it. */
if (fsr.regs[PCOQ_TAIL_REGNUM])
- write_register (PCOQ_TAIL_REGNUM,
- read_memory_integer (fsr.regs[PCOQ_TAIL_REGNUM], 4));
-
+ {
+ npc = read_memory_integer (fsr.regs[PCOQ_TAIL_REGNUM], 4);
+ write_register (PCOQ_TAIL_REGNUM, npc);
+ }
/* Else use the value in %rp to set the new PC. */
else
- target_write_pc (read_register (RP_REGNUM), 0);
+ {
+ npc = read_register (RP_REGNUM);
+ target_write_pc (npc, 0);
+ }
write_register (FP_REGNUM, read_memory_integer (fp, 4));
else
write_register (SP_REGNUM, fp);
+ /* The PC we just restored may be inside a return trampoline. If so
+ we want to restart the inferior and run it through the trampoline.
+
+ Do this by setting a momentary breakpoint at the location the
+ trampoline returns to.
+
+ Don't skip through the trampoline if we're popping a dummy frame. */
+ target_pc = SKIP_TRAMPOLINE_CODE (npc & ~0x3) & ~0x3;
+ if (target_pc && !fsr.regs[IPSW_REGNUM])
+ {
+ struct symtab_and_line sal;
+ struct breakpoint *breakpoint;
+ struct cleanup *old_chain;
+
+ /* Set up our breakpoint. Set it to be silent as the MI code
+ for "return_command" will print the frame we returned to. */
+ sal = find_pc_line (target_pc, 0);
+ sal.pc = target_pc;
+ breakpoint = set_momentary_breakpoint (sal, NULL, bp_finish);
+ breakpoint->silent = 1;
+
+ /* So we can clean things up. */
+ old_chain = make_cleanup (delete_breakpoint, breakpoint);
+
+ /* Start up the inferior. */
+ clear_proceed_status ();
+ proceed_to_finish = 1;
+ proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 0);
+
+ /* Perform our cleanups. */
+ do_cleanups (old_chain);
+ }
flush_cached_frames ();
}
{
CORE_ADDR pc = read_pc ();
CORE_ADDR new_pc = read_memory_integer (fsr->regs[PCOQ_HEAD_REGNUM], 4);
- int pid;
struct target_waitstatus w;
int insn_count;
for (i = 0; i < nargs; i++)
{
- /* Coerce chars to int & float to double if necessary */
- args[i] = value_arg_coerce (args[i]);
-
cum += TYPE_LENGTH (VALUE_TYPE (args[i]));
/* value must go at proper alignment. Assume alignment is a
struct type *type;
int gcc_p;
{
- CORE_ADDR dyncall_addr, sr4export_addr;
+ CORE_ADDR dyncall_addr;
struct minimal_symbol *msymbol;
+ struct minimal_symbol *trampoline;
int flags = read_register (FLAGS_REGNUM);
struct unwind_table_entry *u;
- msymbol = lookup_minimal_symbol ("$$dyncall", (struct objfile *) NULL);
+ trampoline = NULL;
+ msymbol = lookup_minimal_symbol ("$$dyncall", NULL, NULL);
if (msymbol == NULL)
error ("Can't find an address for $$dyncall trampoline");
at *fun. */
fun = (CORE_ADDR) read_memory_integer (fun & ~0x3, 4);
}
+ else
+ {
+
+#ifndef GDB_TARGET_IS_PA_ELF
+ /* FUN could be either an export stub, or the real address of a
+ function in a shared library. We must call an import stub
+ rather than the export stub or real function for lazy binding
+ to work correctly. */
+ if (som_solib_get_got_by_pc (fun))
+ {
+ struct objfile *objfile;
+ struct minimal_symbol *funsymbol, *stub_symbol;
+ CORE_ADDR newfun = 0;
+
+ funsymbol = lookup_minimal_symbol_by_pc (fun);
+ if (!funsymbol)
+ error ("Unable to find minimal symbol for target fucntion.\n");
+
+ /* Search all the object files for an import symbol with the
+ right name. */
+ ALL_OBJFILES (objfile)
+ {
+ stub_symbol = lookup_minimal_symbol (SYMBOL_NAME (funsymbol),
+ NULL, objfile);
+ /* Found a symbol with the right name. */
+ if (stub_symbol)
+ {
+ struct unwind_table_entry *u;
+ /* It must be a shared library trampoline. */
+ if (SYMBOL_TYPE (stub_symbol) != mst_solib_trampoline)
+ continue;
+
+ /* It must also be an import stub. */
+ u = find_unwind_entry (SYMBOL_VALUE (stub_symbol));
+ if (!u || u->stub_type != IMPORT)
+ continue;
+
+ /* OK. Looks like the correct import stub. */
+ newfun = SYMBOL_VALUE (stub_symbol);
+ fun = newfun;
+ }
+ }
+ if (newfun == 0)
+ write_register (19, som_solib_get_got_by_pc (fun));
+ }
+#endif
+ }
/* If we are calling an import stub (eg calling into a dynamic library)
then have sr4export call the magic __d_plt_call routine which is linked
if (u && u->stub_type == IMPORT)
{
CORE_ADDR new_fun;
- msymbol = lookup_minimal_symbol ("__d_plt_call", (struct objfile *) NULL);
- if (msymbol == NULL)
- error ("Can't find an address for __d_plt_call trampoline");
- /* This is where sr4export will jump to. */
- new_fun = SYMBOL_VALUE_ADDRESS (msymbol);
+ /* Prefer __gcc_plt_call over the HP supplied routine because
+ __gcc_plt_call works for any number of arguments. */
+ trampoline = lookup_minimal_symbol ("__gcc_plt_call", NULL, NULL);
+ if (trampoline == NULL)
+ trampoline = lookup_minimal_symbol ("__d_plt_call", NULL, NULL);
- /* We have to store the address of the stub in __shlib_funcptr. */
- msymbol = lookup_minimal_symbol ("__shlib_funcptr",
- (struct objfile *)NULL);
- if (msymbol == NULL)
- error ("Can't find an address for __shlib_funcptr");
+ if (trampoline == NULL)
+ error ("Can't find an address for __d_plt_call or __gcc_plt_call trampoline");
- target_write_memory (SYMBOL_VALUE_ADDRESS (msymbol), (char *)&fun, 4);
- fun = new_fun;
+ /* This is where sr4export will jump to. */
+ new_fun = SYMBOL_VALUE_ADDRESS (trampoline);
+ if (strcmp (SYMBOL_NAME (trampoline), "__d_plt_call") == 0)
+ {
+ /* We have to store the address of the stub in __shlib_funcptr. */
+ msymbol = lookup_minimal_symbol ("__shlib_funcptr", NULL,
+ (struct objfile *)NULL);
+ if (msymbol == NULL)
+ error ("Can't find an address for __shlib_funcptr");
+
+ target_write_memory (SYMBOL_VALUE_ADDRESS (msymbol), (char *)&fun, 4);
+
+ /* We want sr4export to call __d_plt_call, so we claim it is
+ the final target. Clear trampoline. */
+ fun = new_fun;
+ trampoline = NULL;
+ }
}
- /* We still need sr4export's address too. */
- msymbol = lookup_minimal_symbol ("_sr4export", (struct objfile *) NULL);
- if (msymbol == NULL)
- error ("Can't find an address for _sr4export trampoline");
-
- sr4export_addr = SYMBOL_VALUE_ADDRESS (msymbol);
-
+ /* Store upper 21 bits of function address into ldil. fun will either be
+ the final target (most cases) or __d_plt_call when calling into a shared
+ library and __gcc_plt_call is not available. */
store_unsigned_integer
- (&dummy[9*REGISTER_SIZE],
- REGISTER_SIZE,
+ (&dummy[FUNC_LDIL_OFFSET],
+ INSTRUCTION_SIZE,
deposit_21 (fun >> 11,
- extract_unsigned_integer (&dummy[9*REGISTER_SIZE],
- REGISTER_SIZE)));
+ extract_unsigned_integer (&dummy[FUNC_LDIL_OFFSET],
+ INSTRUCTION_SIZE)));
+
+ /* Store lower 11 bits of function address into ldo */
store_unsigned_integer
- (&dummy[10*REGISTER_SIZE],
- REGISTER_SIZE,
+ (&dummy[FUNC_LDO_OFFSET],
+ INSTRUCTION_SIZE,
deposit_14 (fun & MASK_11,
- extract_unsigned_integer (&dummy[10*REGISTER_SIZE],
- REGISTER_SIZE)));
- store_unsigned_integer
- (&dummy[12*REGISTER_SIZE],
- REGISTER_SIZE,
- deposit_21 (sr4export_addr >> 11,
- extract_unsigned_integer (&dummy[12*REGISTER_SIZE],
- REGISTER_SIZE)));
- store_unsigned_integer
- (&dummy[13*REGISTER_SIZE],
- REGISTER_SIZE,
- deposit_14 (sr4export_addr & MASK_11,
- extract_unsigned_integer (&dummy[13*REGISTER_SIZE],
- REGISTER_SIZE)));
+ extract_unsigned_integer (&dummy[FUNC_LDO_OFFSET],
+ INSTRUCTION_SIZE)));
+#ifdef SR4EXPORT_LDIL_OFFSET
+
+ {
+ CORE_ADDR trampoline_addr;
+
+ /* We may still need sr4export's address too. */
+
+ if (trampoline == NULL)
+ {
+ msymbol = lookup_minimal_symbol ("_sr4export", NULL, NULL);
+ if (msymbol == NULL)
+ error ("Can't find an address for _sr4export trampoline");
+
+ trampoline_addr = SYMBOL_VALUE_ADDRESS (msymbol);
+ }
+ else
+ trampoline_addr = SYMBOL_VALUE_ADDRESS (trampoline);
+
+
+ /* Store upper 21 bits of trampoline's address into ldil */
+ store_unsigned_integer
+ (&dummy[SR4EXPORT_LDIL_OFFSET],
+ INSTRUCTION_SIZE,
+ deposit_21 (trampoline_addr >> 11,
+ extract_unsigned_integer (&dummy[SR4EXPORT_LDIL_OFFSET],
+ INSTRUCTION_SIZE)));
+
+ /* Store lower 11 bits of trampoline's address into ldo */
+ store_unsigned_integer
+ (&dummy[SR4EXPORT_LDO_OFFSET],
+ INSTRUCTION_SIZE,
+ deposit_14 (trampoline_addr & MASK_11,
+ extract_unsigned_integer (&dummy[SR4EXPORT_LDO_OFFSET],
+ INSTRUCTION_SIZE)));
+ }
+#endif
write_register (22, pc);
directly. $$dyncall is not needed as the kernel sets up the
space id registers properly based on the value in %r31. In
fact calling $$dyncall will not work because the value in %r22
- will be clobbered on the syscall exit path. */
+ will be clobbered on the syscall exit path.
+
+ Similarly if the current PC is in a shared library. Note however,
+ this scheme won't work if the shared library isn't mapped into
+ the same space as the stack. */
if (flags & 2)
return pc;
+#ifndef GDB_TARGET_IS_PA_ELF
+ else if (som_solib_get_got_by_pc (target_read_pc (inferior_pid)))
+ return pc;
+#endif
else
return dyncall_addr;
/* Get the PC from %r31 if currently in a syscall. Also mask out privilege
bits. */
+
CORE_ADDR
target_read_pc (pid)
int pid;
{
int flags = read_register (FLAGS_REGNUM);
- if (flags & 2)
+ if (flags & 2) {
return read_register (31) & ~0x3;
+ }
return read_register (PC_REGNUM) & ~0x3;
}
/* Write out the PC. If currently in a syscall, then also write the new
PC value into %r31. */
+
void
target_write_pc (v, pid)
CORE_ADDR v;
alignment required by their fields. */
static int
-hppa_alignof (arg)
- struct type *arg;
+hppa_alignof (type)
+ struct type *type;
{
int max_align, align, i;
- switch (TYPE_CODE (arg))
+ CHECK_TYPEDEF (type);
+ switch (TYPE_CODE (type))
{
case TYPE_CODE_PTR:
case TYPE_CODE_INT:
case TYPE_CODE_FLT:
- return TYPE_LENGTH (arg);
+ return TYPE_LENGTH (type);
case TYPE_CODE_ARRAY:
- return hppa_alignof (TYPE_FIELD_TYPE (arg, 0));
+ return hppa_alignof (TYPE_FIELD_TYPE (type, 0));
case TYPE_CODE_STRUCT:
case TYPE_CODE_UNION:
max_align = 2;
- for (i = 0; i < TYPE_NFIELDS (arg); i++)
+ for (i = 0; i < TYPE_NFIELDS (type); i++)
{
/* Bit fields have no real alignment. */
- if (!TYPE_FIELD_BITPOS (arg, i))
+ if (!TYPE_FIELD_BITPOS (type, i))
{
- align = hppa_alignof (TYPE_FIELD_TYPE (arg, i));
+ align = hppa_alignof (TYPE_FIELD_TYPE (type, i));
max_align = max (max_align, align);
}
}
/* Print the register regnum, or all registers if regnum is -1 */
+void
pa_do_registers_info (regnum, fpregs)
int regnum;
int fpregs;
pa_print_fp_reg (regnum);
}
+static void
pa_print_registers (raw_regs, regnum, fpregs)
char *raw_regs;
int regnum;
int fpregs;
{
- int i;
+ int i,j;
+ long val;
for (i = 0; i < 18; i++)
- printf_unfiltered ("%8.8s: %8x %8.8s: %8x %8.8s: %8x %8.8s: %8x\n",
- reg_names[i],
- *(int *)(raw_regs + REGISTER_BYTE (i)),
- reg_names[i + 18],
- *(int *)(raw_regs + REGISTER_BYTE (i + 18)),
- reg_names[i + 36],
- *(int *)(raw_regs + REGISTER_BYTE (i + 36)),
- reg_names[i + 54],
- *(int *)(raw_regs + REGISTER_BYTE (i + 54)));
-
+ {
+ for (j = 0; j < 4; j++)
+ {
+ val =
+ extract_signed_integer (raw_regs + REGISTER_BYTE (i+(j*18)), 4);
+ printf_unfiltered ("%8.8s: %8x ", reg_names[i+(j*18)], val);
+ }
+ printf_unfiltered ("\n");
+ }
+
if (fpregs)
for (i = 72; i < NUM_REGS; i++)
pa_print_fp_reg (i);
}
+static void
pa_print_fp_reg (i)
int i;
{
}
}
+/* Return one if PC is in the call path of a trampoline, else return zero.
+
+ Note we return one for *any* call trampoline (long-call, arg-reloc), not
+ just shared library trampolines (import, export). */
+
+int
+in_solib_call_trampoline (pc, name)
+ CORE_ADDR pc;
+ char *name;
+{
+ struct minimal_symbol *minsym;
+ struct unwind_table_entry *u;
+ static CORE_ADDR dyncall = 0;
+ static CORE_ADDR sr4export = 0;
+
+/* FIXME XXX - dyncall and sr4export must be initialized whenever we get a
+ new exec file */
+
+ /* First see if PC is in one of the two C-library trampolines. */
+ if (!dyncall)
+ {
+ minsym = lookup_minimal_symbol ("$$dyncall", NULL, NULL);
+ if (minsym)
+ dyncall = SYMBOL_VALUE_ADDRESS (minsym);
+ else
+ dyncall = -1;
+ }
+
+ if (!sr4export)
+ {
+ minsym = lookup_minimal_symbol ("_sr4export", NULL, NULL);
+ if (minsym)
+ sr4export = SYMBOL_VALUE_ADDRESS (minsym);
+ else
+ sr4export = -1;
+ }
+
+ if (pc == dyncall || pc == sr4export)
+ return 1;
+
+ /* Get the unwind descriptor corresponding to PC, return zero
+ if no unwind was found. */
+ u = find_unwind_entry (pc);
+ if (!u)
+ return 0;
+
+ /* If this isn't a linker stub, then return now. */
+ if (u->stub_type == 0)
+ return 0;
+
+ /* By definition a long-branch stub is a call stub. */
+ if (u->stub_type == LONG_BRANCH)
+ return 1;
+
+ /* The call and return path execute the same instructions within
+ an IMPORT stub! So an IMPORT stub is both a call and return
+ trampoline. */
+ if (u->stub_type == IMPORT)
+ return 1;
+
+ /* Parameter relocation stubs always have a call path and may have a
+ return path. */
+ if (u->stub_type == PARAMETER_RELOCATION
+ || u->stub_type == EXPORT)
+ {
+ CORE_ADDR addr;
+
+ /* Search forward from the current PC until we hit a branch
+ or the end of the stub. */
+ for (addr = pc; addr <= u->region_end; addr += 4)
+ {
+ unsigned long insn;
+
+ insn = read_memory_integer (addr, 4);
+
+ /* Does it look like a bl? If so then it's the call path, if
+ we find a bv or be first, then we're on the return path. */
+ if ((insn & 0xfc00e000) == 0xe8000000)
+ return 1;
+ else if ((insn & 0xfc00e001) == 0xe800c000
+ || (insn & 0xfc000000) == 0xe0000000)
+ return 0;
+ }
+
+ /* Should never happen. */
+ warning ("Unable to find branch in parameter relocation stub.\n");
+ return 0;
+ }
+
+ /* Unknown stub type. For now, just return zero. */
+ return 0;
+}
+
+/* Return one if PC is in the return path of a trampoline, else return zero.
+
+ Note we return one for *any* call trampoline (long-call, arg-reloc), not
+ just shared library trampolines (import, export). */
+
+int
+in_solib_return_trampoline (pc, name)
+ CORE_ADDR pc;
+ char *name;
+{
+ struct unwind_table_entry *u;
+
+ /* Get the unwind descriptor corresponding to PC, return zero
+ if no unwind was found. */
+ u = find_unwind_entry (pc);
+ if (!u)
+ return 0;
+
+ /* If this isn't a linker stub or it's just a long branch stub, then
+ return zero. */
+ if (u->stub_type == 0 || u->stub_type == LONG_BRANCH)
+ return 0;
+
+ /* The call and return path execute the same instructions within
+ an IMPORT stub! So an IMPORT stub is both a call and return
+ trampoline. */
+ if (u->stub_type == IMPORT)
+ return 1;
+
+ /* Parameter relocation stubs always have a call path and may have a
+ return path. */
+ if (u->stub_type == PARAMETER_RELOCATION
+ || u->stub_type == EXPORT)
+ {
+ CORE_ADDR addr;
+
+ /* Search forward from the current PC until we hit a branch
+ or the end of the stub. */
+ for (addr = pc; addr <= u->region_end; addr += 4)
+ {
+ unsigned long insn;
+
+ insn = read_memory_integer (addr, 4);
+
+ /* Does it look like a bl? If so then it's the call path, if
+ we find a bv or be first, then we're on the return path. */
+ if ((insn & 0xfc00e000) == 0xe8000000)
+ return 0;
+ else if ((insn & 0xfc00e001) == 0xe800c000
+ || (insn & 0xfc000000) == 0xe0000000)
+ return 1;
+ }
+
+ /* Should never happen. */
+ warning ("Unable to find branch in parameter relocation stub.\n");
+ return 0;
+ }
+
+ /* Unknown stub type. For now, just return zero. */
+ return 0;
+
+}
+
/* Figure out if PC is in a trampoline, and if so find out where
the trampoline will jump to. If not in a trampoline, return zero.
if (!dyncall)
{
- msym = lookup_minimal_symbol ("$$dyncall", NULL);
+ msym = lookup_minimal_symbol ("$$dyncall", NULL, NULL);
if (msym)
dyncall = SYMBOL_VALUE_ADDRESS (msym);
else
if (!sr4export)
{
- msym = lookup_minimal_symbol ("_sr4export", NULL);
+ msym = lookup_minimal_symbol ("_sr4export", NULL, NULL);
if (msym)
sr4export = SYMBOL_VALUE_ADDRESS (msym);
else
}
/* Addresses passed to dyncall may *NOT* be the actual address
- of the funtion. So we may have to do something special. */
+ of the function. So we may have to do something special. */
if (pc == dyncall)
{
pc = (CORE_ADDR) read_register (22);
}
}
+ /* Does it look like a be 0(sr0,%r21)? That's the branch from an
+ import stub to an export stub.
+
+ It is impossible to determine the target of the branch via
+ simple examination of instructions and/or data (consider
+ that the address in the plabel may be the address of the
+ bind-on-reference routine in the dynamic loader).
+
+ So we have try an alternative approach.
+
+ Get the name of the symbol at our current location; it should
+ be a stub symbol with the same name as the symbol in the
+ shared library.
+
+ Then lookup a minimal symbol with the same name; we should
+ get the minimal symbol for the target routine in the shared
+ library as those take precedence of import/export stubs. */
+ if (curr_inst == 0xe2a00000)
+ {
+ struct minimal_symbol *stubsym, *libsym;
+
+ stubsym = lookup_minimal_symbol_by_pc (loc);
+ if (stubsym == NULL)
+ {
+ warning ("Unable to find symbol for 0x%x", loc);
+ return orig_pc == pc ? 0 : pc & ~0x3;
+ }
+
+ libsym = lookup_minimal_symbol (SYMBOL_NAME (stubsym), NULL, NULL);
+ if (libsym == NULL)
+ {
+ warning ("Unable to find library symbol for %s\n",
+ SYMBOL_NAME (stubsym));
+ return orig_pc == pc ? 0 : pc & ~0x3;
+ }
+
+ return SYMBOL_VALUE (libsym);
+ }
+
/* Does it look like bl X,%rp or bl X,%r0? Another way to do a
branch from the stub to the actual function. */
else if ((curr_inst & 0xffe0e000) == 0xe8400000
CORE_ADDR pc;
{
char buf[4];
+ CORE_ADDR orig_pc = pc;
unsigned long inst, stack_remaining, save_gr, save_fr, save_rp, save_sp;
- unsigned long args_stored, status, i;
+ unsigned long args_stored, status, i, restart_gr, restart_fr;
struct unwind_table_entry *u;
+ restart_gr = 0;
+ restart_fr = 0;
+
+restart:
u = find_unwind_entry (pc);
if (!u)
return pc;
/* An indication that args may be stored into the stack. Unfortunately
the HPUX compilers tend to set this in cases where no args were
stored too!. */
- args_stored = u->Args_stored;
+ args_stored = 1;
/* Turn the Entry_GR field into a bitmask. */
save_gr = 0;
save_gr |= (1 << i);
}
+ save_gr &= ~restart_gr;
/* Turn the Entry_FR field into a bitmask too. */
save_fr = 0;
for (i = 12; i < u->Entry_FR + 12; i++)
save_fr |= (1 << i);
+ save_fr &= ~restart_fr;
/* Loop until we find everything of interest or hit a branch.
{
unsigned int reg_num;
unsigned long old_stack_remaining, old_save_gr, old_save_fr;
- unsigned long old_save_rp, old_save_sp, old_args_stored, next_inst;
+ unsigned long old_save_rp, old_save_sp, next_inst;
/* Save copies of all the triggers so we can compare them later
(only for HPC). */
pc += 4;
}
+ /* We've got a tenative location for the end of the prologue. However
+ because of limitations in the unwind descriptor mechanism we may
+ have went too far into user code looking for the save of a register
+ that does not exist. So, if there registers we expected to be saved
+ but never were, mask them out and restart.
+
+ This should only happen in optimized code, and should be very rare. */
+ if (save_gr || save_fr
+ && ! (restart_fr || restart_gr))
+ {
+ pc = orig_pc;
+ restart_gr = save_gr;
+ restart_fr = save_fr;
+ goto restart;
+ }
+
return pc;
}
for the saved registers. */
if ((frame_info->pc >= frame_info->frame
&& frame_info->pc <= (frame_info->frame + CALL_DUMMY_LENGTH
- + 32 * 4 + (NUM_REGS - FP0_REGNUM) * 8
- + 6 * 4)))
+ + 32 * 4 + (NUM_REGS - FP0_REGNUM) * 8
+ + 6 * 4)))
find_dummy_frame_regs (frame_info, frame_saved_regs);
/* Interrupt handlers are special too. They lay out the register
return;
}
+#ifdef FRAME_FIND_SAVED_REGS_IN_SIGTRAMP
/* Handle signal handler callers. */
if (frame_info->signal_handler_caller)
{
FRAME_FIND_SAVED_REGS_IN_SIGTRAMP (frame_info, frame_saved_regs);
return;
}
+#endif
/* Get the starting address of the function referred to by the PC
- saved in frame_info. */
+ saved in frame. */
pc = get_pc_function_start (frame_info->pc);
/* Yow! */
int from_tty;
{
CORE_ADDR address;
- union
- {
- int *foo;
- struct unwind_table_entry *u;
- } xxx;
+ struct unwind_table_entry *u;
/* If we have an expression, evaluate it and use it as the address. */
else
return;
- xxx.u = find_unwind_entry (address);
+ u = find_unwind_entry (address);
- if (!xxx.u)
+ if (!u)
{
- printf_unfiltered ("Can't find unwind table entry for PC 0x%x\n", address);
+ printf_unfiltered ("Can't find unwind table entry for %s\n", exp);
return;
}
- printf_unfiltered ("%08x\n%08X\n%08X\n%08X\n", xxx.foo[0], xxx.foo[1], xxx.foo[2],
- xxx.foo[3]);
+ printf_unfiltered ("unwind_table_entry (0x%x):\n", u);
+
+ printf_unfiltered ("\tregion_start = ");
+ print_address (u->region_start, gdb_stdout);
+
+ printf_unfiltered ("\n\tregion_end = ");
+ print_address (u->region_end, gdb_stdout);
+
+#ifdef __STDC__
+#define pif(FLD) if (u->FLD) printf_unfiltered (" "#FLD);
+#else
+#define pif(FLD) if (u->FLD) printf_unfiltered (" FLD");
+#endif
+
+ printf_unfiltered ("\n\tflags =");
+ pif (Cannot_unwind);
+ pif (Millicode);
+ pif (Millicode_save_sr0);
+ pif (Entry_SR);
+ pif (Args_stored);
+ pif (Variable_Frame);
+ pif (Separate_Package_Body);
+ pif (Frame_Extension_Millicode);
+ pif (Stack_Overflow_Check);
+ pif (Two_Instruction_SP_Increment);
+ pif (Ada_Region);
+ pif (Save_SP);
+ pif (Save_RP);
+ pif (Save_MRP_in_frame);
+ pif (extn_ptr_defined);
+ pif (Cleanup_defined);
+ pif (MPE_XL_interrupt_marker);
+ pif (HP_UX_interrupt_marker);
+ pif (Large_frame);
+
+ putchar_unfiltered ('\n');
+
+#ifdef __STDC__
+#define pin(FLD) printf_unfiltered ("\t"#FLD" = 0x%x\n", u->FLD);
+#else
+#define pin(FLD) printf_unfiltered ("\tFLD = 0x%x\n", u->FLD);
+#endif
+
+ pin (Region_description);
+ pin (Entry_FR);
+ pin (Entry_GR);
+ pin (Total_frame_size);
}
#endif /* MAINTENANCE_CMDS */
void
_initialize_hppa_tdep ()
{
+ tm_print_insn = print_insn_hppa;
+
#ifdef MAINTENANCE_CMDS
add_cmd ("unwind", class_maintenance, unwind_command,
"Print unwind table entry at given address.",