#include <stddef.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
extern void kerTaskEntry(void);
#define SKIP_FRAME 1
#define PC_ADJUST -4
#define STOP_FRAME \
(current == NULL \
|| ((CORE_ADDR) &kerTaskEntry >= PROC_LOW_ADDR (current->proc_desc) \
&& current->pc >= (CORE_ADDR) &kerTaskEntry))
#define T7_REGNUM 8
#define GCC_FP_REGNUM 15
#define T9_REGNUM 23
#define SP_REGNUM 30
#define RA_REGNUM 26
#define FP0_REGNUM 32
#define PC_REGNUM 64
#define NUM_REGS 66
#define VM_MIN_ADDRESS (CORE_ADDR)0x120000000
#define SIZEOF_FRAME_SAVED_REGS (sizeof (CORE_ADDR) * (NUM_REGS))
#define INIT_EXTRA_FRAME_INFO(fromleaf, fci) init_extra_frame_info(fci)
#define FRAME_CHAIN(thisframe) (CORE_ADDR) alpha_frame_chain (thisframe)
#define FRAME_CHAIN_VALID(CHAIN, THISFRAME) \
((CHAIN) != 0 \
&& !inside_entry_file (FRAME_SAVED_PC (THISFRAME)))
#define FRAME_SAVED_PC(FRAME) (alpha_frame_saved_pc (FRAME))
#define FRAME_CHAIN_COMBINE(CHAIN, THISFRAME) (CHAIN)
#define INIT_FRAME_PC(FROMLEAF, PREV)
#define INIT_FRAME_PC_FIRST(FROMLEAF, PREV) \
(PREV)->pc = ((FROMLEAF) ? SAVED_PC_AFTER_CALL ((PREV)->next) \
: (PREV)->next ? FRAME_SAVED_PC ((PREV)->next) : read_pc ());
#define SAVED_PC_AFTER_CALL(FRAME) alpha_saved_pc_after_call (FRAME)
typedef unsigned long long int bfd_vma;
typedef bfd_vma CORE_ADDR;
typedef struct pdr
{
bfd_vma adr;
long isym;
long iline;
long regmask;
long regoffset;
long iopt;
long fregmask;
long fregoffset;
long frameoffset;
short framereg;
short pcreg;
long lnLow;
long lnHigh;
bfd_vma cbLineOffset;
unsigned gp_prologue : 8;
unsigned gp_used : 1;
unsigned reg_frame : 1;
unsigned prof : 1;
unsigned reserved : 13;
unsigned localoff : 8;
} PDR;
typedef struct alpha_extra_func_info
{
long numargs;
PDR pdr;
}
*alpha_extra_func_info_t;
struct frame_info
{
CORE_ADDR frame;
CORE_ADDR pc;
CORE_ADDR *saved_regs;
int localoff;
int pc_reg;
alpha_extra_func_info_t proc_desc;
struct frame_info *next, *prev;
};
struct frame_saved_regs
{
CORE_ADDR regs[NUM_REGS];
};
static CORE_ADDR theRegisters[32];
static CORE_ADDR read_next_frame_reg (struct frame_info *, int);
static CORE_ADDR heuristic_proc_start (CORE_ADDR);
static int alpha_about_to_return (CORE_ADDR pc);
static void init_extra_frame_info (struct frame_info *);
static CORE_ADDR alpha_frame_chain (struct frame_info *);
static CORE_ADDR alpha_frame_saved_pc (struct frame_info *frame);
static void *trace_alloc (unsigned int);
static struct frame_info *create_new_frame (CORE_ADDR, CORE_ADDR);
static alpha_extra_func_info_t
heuristic_proc_desc (CORE_ADDR, CORE_ADDR, struct frame_info *,
struct frame_saved_regs *);
static alpha_extra_func_info_t
find_proc_desc (CORE_ADDR, struct frame_info *, struct frame_saved_regs *);
static unsigned int heuristic_fence_post = 1<<16;
#define PROC_LOW_ADDR(PROC) ((PROC)->pdr.adr)
#define PROC_HIGH_ADDR(PROC) ((PROC)->pdr.iline)
#define PROC_DUMMY_FRAME(PROC) ((PROC)->pdr.cbLineOffset)
#define PROC_FRAME_OFFSET(PROC) ((PROC)->pdr.frameoffset)
#define PROC_FRAME_REG(PROC) ((PROC)->pdr.framereg)
#define PROC_REG_MASK(PROC) ((PROC)->pdr.regmask)
#define PROC_FREG_MASK(PROC) ((PROC)->pdr.fregmask)
#define PROC_REG_OFFSET(PROC) ((PROC)->pdr.regoffset)
#define PROC_FREG_OFFSET(PROC) ((PROC)->pdr.fregoffset)
#define PROC_PC_REG(PROC) ((PROC)->pdr.pcreg)
#define PROC_LOCALOFF(PROC) ((PROC)->pdr.localoff)
struct alloc_chain
{
struct alloc_chain *next;
double x[0];
};
struct alloc_chain *trace_alloc_chain;
static void *
trace_alloc (unsigned int n)
{
struct alloc_chain * result = malloc (n + sizeof(struct alloc_chain));
result->next = trace_alloc_chain;
trace_alloc_chain = result;
return (void*) result->x;
}
static void
free_trace_alloc (void)
{
while (trace_alloc_chain != 0)
{
struct alloc_chain *old = trace_alloc_chain;
trace_alloc_chain = trace_alloc_chain->next;
free (old);
}
}
static int
read_memory_safe4 (CORE_ADDR addr, unsigned int *dest)
{
*dest = *((unsigned int*) addr);
return 0;
}
static int
read_memory_safe8 (CORE_ADDR addr, CORE_ADDR *dest)
{
*dest = *((CORE_ADDR*) addr);
return 0;
}
static CORE_ADDR
read_register (int regno)
{
if (regno >= 0 && regno < 31)
return theRegisters[regno];
return (CORE_ADDR) 0;
}
static void
frame_saved_regs_zalloc (struct frame_info *fi)
{
fi->saved_regs = (CORE_ADDR *) trace_alloc (SIZEOF_FRAME_SAVED_REGS);
memset (fi->saved_regs, 0, SIZEOF_FRAME_SAVED_REGS);
}
static void *
frame_obstack_alloc (unsigned long size)
{
return (void *) trace_alloc (size);
}
static int
inside_entry_file (CORE_ADDR addr)
{
if (addr == 0)
return 1;
else
return 0;
}
static CORE_ADDR
alpha_saved_pc_after_call (struct frame_info *frame)
{
CORE_ADDR pc = frame->pc;
alpha_extra_func_info_t proc_desc;
int pcreg;
proc_desc = find_proc_desc (pc, frame->next, NULL);
pcreg = proc_desc ? PROC_PC_REG (proc_desc) : RA_REGNUM;
return read_register (pcreg);
}
static void
alpha_find_saved_regs (struct frame_info *frame)
{
int ireg;
CORE_ADDR reg_position;
unsigned long mask;
alpha_extra_func_info_t proc_desc;
int returnreg;
frame_saved_regs_zalloc (frame);
#ifndef SIGFRAME_PC_OFF
#define SIGFRAME_PC_OFF (2 * 8)
#define SIGFRAME_REGSAVE_OFF (4 * 8)
#define SIGFRAME_FPREGSAVE_OFF (SIGFRAME_REGSAVE_OFF + 32 * 8 + 8)
#endif
proc_desc = frame->proc_desc;
if (proc_desc == NULL)
return;
reg_position = frame->frame + PROC_REG_OFFSET (proc_desc);
mask = PROC_REG_MASK (proc_desc);
returnreg = PROC_PC_REG (proc_desc);
if (mask & (1 << returnreg))
{
frame->saved_regs[returnreg] = reg_position;
reg_position += 8;
mask &= ~(1 << returnreg);
}
for (ireg = 0; ireg <= 31; ireg++)
if (mask & (1 << ireg))
{
frame->saved_regs[ireg] = reg_position;
reg_position += 8;
}
reg_position = frame->frame + PROC_FREG_OFFSET (proc_desc);
mask = PROC_FREG_MASK (proc_desc);
for (ireg = 0; ireg <= 31; ireg++)
if (mask & (1 << ireg))
{
frame->saved_regs[FP0_REGNUM + ireg] = reg_position;
reg_position += 8;
}
frame->saved_regs[PC_REGNUM] = frame->saved_regs[returnreg];
}
static CORE_ADDR
read_next_frame_reg (struct frame_info *fi, int regno)
{
CORE_ADDR result;
for (; fi; fi = fi->next)
{
if (regno == SP_REGNUM)
return fi->frame;
else
{
if (fi->saved_regs == 0)
alpha_find_saved_regs (fi);
if (fi->saved_regs[regno])
{
if (read_memory_safe8 (fi->saved_regs[regno], &result) == 0)
return result;
else
return 0;
}
}
}
return read_register (regno);
}
static CORE_ADDR
alpha_frame_saved_pc (struct frame_info *frame)
{
return read_next_frame_reg (frame, frame->pc_reg);
}
static struct alpha_extra_func_info temp_proc_desc;
static int
alpha_about_to_return (CORE_ADDR pc)
{
int inst;
read_memory_safe4 (pc, &inst);
return inst == 0x6bfa8001;
}
static CORE_ADDR
heuristic_proc_start (CORE_ADDR pc)
{
CORE_ADDR start_pc = pc;
CORE_ADDR fence = start_pc - heuristic_fence_post;
if (start_pc == 0)
return 0;
if (heuristic_fence_post == UINT_MAX
|| fence < VM_MIN_ADDRESS)
fence = VM_MIN_ADDRESS;
for (start_pc -= 4; ; start_pc -= 4)
{
if (start_pc < fence)
return 0;
else if (alpha_about_to_return (start_pc))
break;
}
start_pc += 4;
return start_pc;
}
static alpha_extra_func_info_t
heuristic_proc_desc (CORE_ADDR start_pc,
CORE_ADDR limit_pc,
struct frame_info *next_frame,
struct frame_saved_regs *saved_regs_p)
{
CORE_ADDR sp = read_next_frame_reg (next_frame, SP_REGNUM);
CORE_ADDR cur_pc;
int frame_size;
int has_frame_reg = 0;
unsigned long reg_mask = 0;
int pcreg = -1;
if (start_pc == 0)
return 0;
memset (&temp_proc_desc, '\0', sizeof (temp_proc_desc));
if (saved_regs_p != 0)
memset (saved_regs_p, '\0', sizeof (struct frame_saved_regs));
PROC_LOW_ADDR (&temp_proc_desc) = start_pc;
if (start_pc + 200 < limit_pc)
limit_pc = start_pc + 200;
frame_size = 0;
for (cur_pc = start_pc; cur_pc < limit_pc; cur_pc += 4)
{
unsigned int word;
int status;
status = read_memory_safe4 (cur_pc, &word);
if (status)
return 0;
if ((word & 0xffff0000) == 0x23de0000)
{
if (word & 0x8000)
frame_size += (-word) & 0xffff;
else
break;
}
else if ((word & 0xfc1f0000) == 0xb41e0000
&& (word & 0xffff0000) != 0xb7fe0000)
{
int reg = (word & 0x03e00000) >> 21;
reg_mask |= 1 << reg;
if (saved_regs_p != 0)
saved_regs_p->regs[reg] = sp + (short) word;
if (pcreg == -1
&& cur_pc < (start_pc + 80)
&& (reg == T7_REGNUM || reg == T9_REGNUM || reg == RA_REGNUM))
pcreg = reg;
}
else if ((word & 0xffe0ffff) == 0x6be08001)
pcreg = (word >> 16) & 0x1f;
else if (word == 0x47de040f)
has_frame_reg = 1;
}
if (pcreg == -1)
{
while (cur_pc < (limit_pc + 80) && cur_pc < (start_pc + 80))
{
unsigned int word;
if (read_memory_safe4 (cur_pc, &word))
break;
cur_pc += 4;
if ((word & 0xfc1f0000) == 0xb41e0000
&& (word & 0xffff0000) != 0xb7fe0000)
{
int reg = (word & 0x03e00000) >> 21;
if (reg == T7_REGNUM || reg == T9_REGNUM || reg == RA_REGNUM)
{
pcreg = reg;
break;
}
}
else if ((word & 0xffe0ffff) == 0x6be08001)
{
pcreg = (word >> 16) & 0x1f;
break;
}
}
}
if (has_frame_reg)
PROC_FRAME_REG (&temp_proc_desc) = GCC_FP_REGNUM;
else
PROC_FRAME_REG (&temp_proc_desc) = SP_REGNUM;
PROC_FRAME_OFFSET (&temp_proc_desc) = frame_size;
PROC_REG_MASK (&temp_proc_desc) = reg_mask;
PROC_PC_REG (&temp_proc_desc) = (pcreg == -1) ? RA_REGNUM : pcreg;
PROC_LOCALOFF (&temp_proc_desc) = 0;
return &temp_proc_desc;
}
static alpha_extra_func_info_t
find_proc_desc (CORE_ADDR pc,
struct frame_info *next_frame,
struct frame_saved_regs *saved_regs)
{
CORE_ADDR startaddr;
startaddr = heuristic_proc_start (pc);
return heuristic_proc_desc (startaddr, pc, next_frame, saved_regs);
}
static CORE_ADDR
alpha_frame_chain (struct frame_info *frame)
{
alpha_extra_func_info_t proc_desc;
CORE_ADDR saved_pc = FRAME_SAVED_PC (frame);
if (saved_pc == 0 || inside_entry_file (saved_pc))
return 0;
proc_desc = find_proc_desc (saved_pc, frame, NULL);
if (!proc_desc)
return 0;
if (PROC_FRAME_REG (proc_desc) == SP_REGNUM
&& PROC_FRAME_OFFSET (proc_desc) == 0)
return 0;
else
return read_next_frame_reg (frame, PROC_FRAME_REG (proc_desc))
+ PROC_FRAME_OFFSET (proc_desc);
}
static void
init_extra_frame_info (struct frame_info *frame)
{
struct frame_saved_regs temp_saved_regs;
alpha_extra_func_info_t proc_desc =
find_proc_desc (frame->pc, frame->next, &temp_saved_regs);
frame->saved_regs = NULL;
frame->localoff = 0;
frame->pc_reg = RA_REGNUM;
frame->proc_desc = proc_desc;
if (proc_desc)
{
frame->localoff = PROC_LOCALOFF (proc_desc);
frame->pc_reg = PROC_PC_REG (proc_desc);
if (frame->pc == PROC_LOW_ADDR (proc_desc))
frame->frame = read_next_frame_reg (frame->next, SP_REGNUM);
else
frame->frame
= (read_next_frame_reg (frame->next, PROC_FRAME_REG (proc_desc))
+ PROC_FRAME_OFFSET (proc_desc));
frame->saved_regs
= (CORE_ADDR *) frame_obstack_alloc (SIZEOF_FRAME_SAVED_REGS);
memcpy
(frame->saved_regs, temp_saved_regs.regs, SIZEOF_FRAME_SAVED_REGS);
frame->saved_regs[PC_REGNUM] = frame->saved_regs[RA_REGNUM];
}
}
static struct frame_info *
create_new_frame (CORE_ADDR addr, CORE_ADDR pc)
{
struct frame_info *fi;
fi = (struct frame_info *)
trace_alloc (sizeof (struct frame_info));
fi->next = NULL;
fi->prev = NULL;
fi->frame = addr;
fi->pc = pc;
#ifdef INIT_EXTRA_FRAME_INFO
INIT_EXTRA_FRAME_INFO (0, fi);
#endif
return fi;
}
static CORE_ADDR current_pc;
static void
set_current_pc (void)
{
current_pc = (CORE_ADDR) __builtin_return_address (0);
}
static CORE_ADDR
read_pc (void)
{
return current_pc;
}
static struct frame_info *
get_current_frame (void)
{
return create_new_frame (0, read_pc ());
}
static struct frame_info *
get_prev_frame (struct frame_info *next_frame)
{
CORE_ADDR address = 0;
struct frame_info *prev;
int fromleaf = 0;
if (next_frame->prev)
return next_frame->prev;
address = FRAME_CHAIN (next_frame);
if (!FRAME_CHAIN_VALID (address, next_frame))
return 0;
address = FRAME_CHAIN_COMBINE (address, next_frame);
if (address == 0)
return 0;
prev = (struct frame_info *) trace_alloc (sizeof (struct frame_info));
prev->saved_regs = NULL;
if (next_frame)
next_frame->prev = prev;
prev->next = next_frame;
prev->prev = (struct frame_info *) 0;
prev->frame = address;
#ifdef INIT_FRAME_PC_FIRST
INIT_FRAME_PC_FIRST (fromleaf, prev);
#endif
#ifdef INIT_EXTRA_FRAME_INFO
INIT_EXTRA_FRAME_INFO (fromleaf, prev);
#endif
INIT_FRAME_PC (fromleaf, prev);
if (next_frame != NULL)
{
if (prev->frame == next_frame->frame
&& prev->pc == next_frame->pc)
{
next_frame->prev = NULL;
free (prev);
return NULL;
}
}
return prev;
}
#define SAVE(regno,disp) \
"stq $" #regno ", " #disp "(%0)\n"
int
__gnat_backtrace (void **array,
int size,
void *exclude_min,
void *exclude_max,
int skip_frames)
{
struct frame_info* top;
struct frame_info* current;
int cnt;
(*Lock_Task) ();
asm volatile (
SAVE (9,72)
SAVE (10,80)
SAVE (11,88)
SAVE (12,96)
SAVE (13,104)
SAVE (14,112)
SAVE (15,120)
SAVE (16,128)
SAVE (17,136)
SAVE (18,144)
SAVE (19,152)
SAVE (20,160)
SAVE (21,168)
SAVE (22,176)
SAVE (23,184)
SAVE (24,192)
SAVE (25,200)
SAVE (26,208)
SAVE (27,216)
SAVE (28,224)
SAVE (29,232)
SAVE (30,240)
: : "r" (&theRegisters));
trace_alloc_chain = NULL;
set_current_pc ();
top = current = get_current_frame ();
cnt = 0;
for (cnt = 0; cnt < skip_frames; cnt += 1) {
current = get_prev_frame (current);
}
cnt = 0;
while (cnt < size)
{
if (STOP_FRAME)
break;
if (current->pc < (CORE_ADDR) exclude_min
|| current->pc > (CORE_ADDR) exclude_max)
array[cnt++] = (void*) (current->pc + PC_ADJUST);
current = get_prev_frame (current);
}
free_trace_alloc ();
(*Unlock_Task) ();
return cnt;
}