4.4BSD/usr/src/contrib/gdb-4.7.lbl/gdb/sparc-tcmn.c
/*
* Copyright (c) 1992 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Lawrence Berkeley Laboratory,
* Berkeley, CA. The name of the University may not be used to
* endorse or promote products derived from this software without
* specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef lint
static char rcsid[] =
"@(#) $Header: sparc-tcmn.c,v 1.2 93/02/19 15:25:05 mccanne Exp $ (LBL)";
#endif
#include <sys/param.h>
#include <stdio.h>
#include "defs.h"
#include "target.h"
#include "frame.h"
#include "inferior.h"
#include "value.h"
/*
* Sparc instruction format per Fig 4-1 of the Sun SPARC Architecture
* Manual Part No: 800-1399-08 Rev A, Oct 22 1987.
*/
union sparcinsn {
u_long code;
struct {
u_int op:2;
u_int disp30:30;
} f1;
struct insn_f2a {
u_int op:2;
u_int rd:5;
u_int op2:3;
int imm22:22;
} f2a;
struct insn_f2b {
u_int op:2;
u_int a:1;
u_int cond:4;
u_int op2:3;
int disp22:22;
} f2b;
struct insn_f3a {
u_int op:2;
u_int rd:5;
u_int op3:6;
u_int rs1:5;
u_int i:1;
u_int asi:8;
u_int rs2:5;
} f3a;
struct insn_f3b {
u_int op:2;
u_int rd:5;
u_int op3:6;
u_int rs1:5;
u_int i:1;
int simm13:13;
} f3b;
};
/*
* For the instruction INSN at PC, return the address, other than NPC, that
* could possibly be executed next. If the only possibility is npc, return 0.
* (There is only one such "other" possible address.)
*/
CORE_ADDR
annulled_dest(insn, pc, npc)
union sparcinsn insn;
CORE_ADDR pc, npc;
{
register struct insn_f2b i = insn.f2b;
/*
* The only time we can branch somewhere other than npc is
* on a branch intruction with the anull bit set. In this
* case, the other possible target is the npc + 4 (because
* npc was anulled and the branch was not taken) or the
* branch destination (because npc was anulled and it was
* a branch always).
*/
if (i.op == 0 && i.a && (i.op2 == 2 || i.op2 == 6 || i.op2 == 7)) {
if (i.cond == 8)
return (pc + 4 * i.disp22);
else
return (npc + 4);
}
return (0);
}
/*
* Non-zero if we just simulated a single-step ptrace call. This is
* needed because we cannot remove the breakpoints in the inferior
* process until after the `wait' in `wait_for_inferior'.
* Used for sun4.
*/
int one_stepped;
/*
* single_step() is called just before we want to resume the inferior,
* if we want to single-step it but there is no hardware or kernel single-step
* support (as on all SPARCs). We find all the possible targets of the
* coming instruction and breakpoint them.
*
* single_step is also called just after the inferior stops. If we had
* set up a simulated single-step, we undo our damage.
*
* Code written by Gary Beihl (beihl@mcc.com); modified by Steven McCanne
* (mccanne@ee.lbl.gov).
*/
void
single_step(signal)
int signal;
{
CORE_ADDR pc;
union sparcinsn insn;
/*
* Storage for temporary breakpoints. XXX There should be a uniform
* interface for breakpoints, so that we could just set one then
* clear it. Note that we need only two outstanding breakpoints,
* since there can be at most two possiblilities for control flow.
*/
static CORE_ADDR target0;
static CORE_ADDR target1;
static char shadow0[4];
static char shadow1[4];
pc = read_register(PC_REGNUM);
insn.code = read_memory_integer(pc, 4);
if (!one_stepped) {
/*
* This is a hack to special case call instructions.
* If we are stepping over subroutines, find each call
* and trap on return, rather than single step until
* wait_for_inferior() discovers that we hit a new routine.
* The reason is that stepping over functions in a remote
* kernel can have bad results when the function being
* stepped over is used by the kernel in between traps.
* (i.e., a trap instruction gets poked into the function
* being stepped over).
*/
if (step_over_calls > 0 &&
((insn.code & 0xc0000000) == 0x40000000 ||
(insn.code & 0xfff80000) == 0x9fc00000)) {
target0 = PC_ADJUST(pc);
target1 = 0;
} else {
target0 = read_register(NPC_REGNUM);
target1 = annulled_dest(insn, pc, target0);
if (target1 == target0)
target1 = 0;
}
target_insert_breakpoint(target0, shadow0);
if (target1)
target_insert_breakpoint(target1, shadow1);
one_stepped = 1;
} else {
/* Remove breakpoints */
if (target1)
target_remove_breakpoint(target1, shadow1);
target_remove_breakpoint(target0, shadow0);
one_stepped = 0;
}
}
/* XXX this looks polluted */
#define FRAME_SAVED_L0 0 /* Byte offset from SP */
#define FRAME_SAVED_I0 32 /* Byte offset from SP */
/*
* Find the previous stack frame pointer, given the current frame.
* On a sparc, this is just the i6 register that was flushed to
* the kernel save area (pointed to by the previous frame's sp,
* which is the current frame's fp).
*/
CORE_ADDR
sparc_frame_chain(frame)
FRAME frame;
{
CORE_ADDR retval;
CORE_ADDR addr;
addr = frame->frame + FRAME_SAVED_I0 +
REGISTER_RAW_SIZE(FP_REGNUM) * (FP_REGNUM - I0_REGNUM);
if (target_read_memory(addr, (char *)&retval, sizeof(retval)))
return (0);
SWAP_TARGET_AND_HOST(&retval, sizeof(retval));
return (retval);
}
/*
* Read the the first word after the kernel save area of the current
* frame. This is where Sun cc puts a pointer to storage for a structure
* return value. Return 0 on read errors.
*/
CORE_ADDR
sparc_extract_struct_value_address(regs)
char *regs;
{
CORE_ADDR addr, v;
bcopy(®s[REGISTER_BYTE(SP_REGNUM)], (char *)&addr, sizeof(addr));
if (target_read_memory(addr, (char *)&v, sizeof(v)))
return (0);
SWAP_TARGET_AND_HOST(&v, sizeof(v));
return (v);
}
/*
* Find the pc saved in frame FRAME.
*/
CORE_ADDR
frame_saved_pc(frame)
FRAME frame;
{
return (PC_ADJUST(read_memory_integer(addr_of_pc(frame), 4)));
}
FRAME
setup_arbitrary_frame(fp, sp)
FRAME_ADDR fp, sp;
{
FRAME f = create_new_frame(fp, 0);
f->bottom = sp;
f->pc = frame_saved_pc(f);
return (f);
}
static int restore_code[] = {
0x81e80000, /* restore */
0x91d02001, /* t 1 */
0x01000000 /* nop */
};
static void
do_restore_insn()
{
/* From infrun.c */
extern int stop_after_trap;
CORE_ADDR sp = read_register(SP_REGNUM);
CORE_ADDR pc = sp - sizeof(restore_code);
write_memory(pc, (char *)restore_code, sizeof(restore_code));
clear_proceed_status ();
stop_after_trap = 1;
proceed(pc, 0, 0);
}
/*
* True iff sparc insn INSN is a save instruction.
*/
#define SAVE_INSN_P(i) ((insn).f3b.op == 2 && (insn).f3b.op3 == 60)
/*
* True iff sparc insn INSN is a store instruction of an input register
* into the arg overflow area, which starts at [fp + 64] and has a
* length of 24 bytes (enough for i0 through i5).
*/
#define ARGSTORE_INSN_P(insn)\
((insn).f3b.op == 3 && ((insn).f3b.op3 & 0x3c) == 4 && \
(insn).f3b.rs1 == FP_REGNUM && (insn).f3b.i == 1 && \
(insn).f3b.simm13 >= 64 && (insn).f3b.simm13 < 64 + 24)
CORE_ADDR
skip_prologue(addr, frameless_p)
CORE_ADDR addr;
int frameless_p;
{
register union sparcinsn insn;
register CORE_ADDR pc = addr;
register int n = 3;
while (--n >= 0) {
insn.code = read_memory_integer(pc, 4);
pc += 4;
if (SAVE_INSN_P(insn)) {
if (frameless_p)
return (pc);
insn.code = read_memory_integer(pc, 4);
while (ARGSTORE_INSN_P(insn)) {
pc += 4;
insn.code = read_memory_integer(pc, 4);
}
}
return (pc);
}
/*
* Couldn't find a save instruction.
*/
return (addr);
}
void
sparc_pop_frame()
{
register CORE_ADDR pc;
pc = read_register(I7_REGNUM);
do_restore_insn();
/*
* Return address in %i7 --
* adjust it, then restore PC and NPC from it.
*/
pc = PC_ADJUST(read_memory_integer(pc, 4));
write_register(PC_REGNUM, pc);
write_register(NPC_REGNUM, pc + 4);
flush_cached_frames();
set_current_frame(create_new_frame(read_register(FP_REGNUM), pc));
}
/*
* Sun's cc leaves a hole after a call to function returning a structure
* (after the delay slot). Given a function call at PC, return the
* address to which the called function will return.
*/
CORE_ADDR
sparc_pc_adjust(pc)
register CORE_ADDR pc;
{
u_long op;
if (target_read_memory(pc + 8, (char *)&op, 4) == 0) {
SWAP_TARGET_AND_HOST(&op, 4);
if ((op & ~0x1ff) == 0)
/*
* The 20 high bits of this opcode are 0, which
* does not conform to a valid instruction.
* It must be the Sun structure length hack.
*/
return (pc + 12);
}
return (pc + 8);
}
int dummy_code[] = {
0xd003a044, /* ld [%sp + 68], %o0 */
0xd203a048, /* ld [%sp + 72], %o1 */
0xd403a04c, /* ld [%sp + 76], %o2 */
0xd603a050, /* ld [%sp + 80], %o3 */
0xd803a054, /* ld [%sp + 84], %o4 */
#define DUMMY_CALL_INDEX 5
0x40000000, /* call . */
0xda03a058, /* ld [%sp + 88], %o5 */
#define DUMMY_STRUCTLEN_INDEX 7
0x01000000, /* nop - extra insn for Sun cc */
0x91d02001, /* ta 1 */
0x01000000, /* nop */
};
/*
* Leave room on the stack for the kernel save area and the pointer
* for structure return values.
*/
#define KSA_AND_STRUCT_ADJUST 68
/*
* Build `dummy' call instructions on inferior's stack to cause
* it to call a subroutine.
*
* N.B. - code in wait_for_inferior requires that sp < pc < fp when
* we take the trap 2 above so it will recognize that we stopped
* at a `dummy' call. So, after the call sp is *not* decremented
* to clean the arguments, code & other stuff we lay on the stack.
* Since the regs are restored to saved values at the breakpoint,
* sp will get reset correctly. Also, this restore means we don't
* have to construct frame linkage info to save pc & fp. The lack
* of frame linkage means we can't do a backtrace, etc., if the
* called function gets a fault or hits a breakpoint but code in
* run_stack_dummy makes this impossible anyway.
*/
CORE_ADDR
setup_dummy(sp, funaddr, nargs, args, struct_return_bytes, gcc_p)
CORE_ADDR sp;
CORE_ADDR funaddr;
int nargs;
value *args;
int struct_return_bytes;
int gcc_p;
{
int len, padding, i;
CORE_ADDR top = sp, struct_addr, pc;
pc = sp - sizeof(dummy_code);
len = value_arg_bytes(nargs, args) + KSA_AND_STRUCT_ADJUST +
sizeof(dummy_code) + struct_return_bytes;
padding = STACK_ALIGN(len) - len;
sp = pc - padding - struct_return_bytes;
struct_addr = sp;
for (i = 0; i < nargs; ++i) {
/* pushfn doesn't actually change SP_REGNUM */
sp = value_arg_push(sp, args[i]);
}
sp -= KSA_AND_STRUCT_ADJUST;
if (struct_return_bytes) {
write_memory(sp + KSA_AND_STRUCT_ADJUST - 4,
(char *)&struct_addr, 4);
/* Appease Sun's twisted convention for struct returns. */
if (!gcc_p)
dummy_code[DUMMY_STRUCTLEN_INDEX] =
struct_return_bytes & 0x1fff;
} else
/* nop */
dummy_code[DUMMY_STRUCTLEN_INDEX] = 0x01000000;
write_register(SP_REGNUM, sp);
dummy_code[DUMMY_CALL_INDEX] = 0x40000000 |
((unsigned)(funaddr - (pc + 4 * DUMMY_CALL_INDEX)) >> 2);
write_memory(pc, (char *)dummy_code, sizeof(dummy_code));
return pc;
}
/*
* Only worry about KSA.
*/
frame_find_saved_regs(fi, srp)
struct frame_info *fi;
struct frame_saved_regs *srp;
{
register int i;
FRAME_ADDR frame;
bzero ((char *)srp, sizeof *srp);
frame = fi->bottom;
/* XXX There's no excuse for this. */
if (frame == 0)
abort();
/* ins and locals */
for (i = 16; i < 32; i++)
srp->regs[i] = frame + (i-16) * 4;
if (fi->next != 0) {
frame = fi->next->bottom;
/* outs */
for (i = 8; i < 16; i++)
srp->regs[i] = frame + i * 4;
}
srp->regs[PC_REGNUM] = addr_of_pc(fi);
}
static void
fix_leaf_frame(arg, from_tty)
char* arg;
int from_tty;
{
int arg1 = 0, level;
FRAME fid, nfid;
struct frame_info *fi, *nfi;
if (!target_has_stack)
error ("No inferior or core file.");
if (arg) {
while (*arg && *arg == ' ')
++arg;
if (*arg)
arg1 = parse_and_eval_address(arg);
}
level = arg1;
fid = find_relative_frame(get_current_frame(), &level);
if (level != 0)
error("Can't find frame %d", arg1);
fi = get_frame_info(fid);
nfid = create_new_frame(fi->frame, fi->pc);
nfi = get_frame_info(nfid);
*nfi = *fi;
nfi->pc = read_register(RP_REGNUM);
nfi->next = fi;
fi->prev = nfi;
}
void
_initialize_sparctcmn()
{
add_com("fix-leaf", class_stack, fix_leaf_frame,
"fix up the call stack to account for a leaf routine\n\
A new frame is inserted for the routine that called the leaf.");
}