ppc-macosx-frameinfo.c [plain text]
#include "ppc-macosx-regs.h"
#include "ppc-macosx-regnums.h"
#include "ppc-macosx-frameinfo.h"
#include "ppc-macosx-tdep.h"
#include "defs.h"
#include "frame.h"
#include "inferior.h"
#include "symtab.h"
#include "target.h"
#include "gdbcore.h"
#include "symfile.h"
#include "objfiles.h"
#include "command.h"
#include "regcache.h"
#include "ppc-tdep.h"
static int max_skip_non_prologue_insns = 2;
static inline long
extract_ds (unsigned long insn)
{
return ((insn & 0xfffc) ^ 0x8000) - 0x8000;
}
static inline int
SIGNED_SHORT (long x)
{
if (sizeof (short) == 2)
{
return ((short) x);
}
else
{
return (((x & 0xffff) ^ 0x8000) - 0x8000);
}
}
static inline int
GET_SRC_REG (long x)
{
return (x >> 21) & 0x1f;
}
void
ppc_print_boundaries (ppc_function_boundaries * bounds)
{
if (bounds->prologue_start != INVALID_ADDRESS)
{
printf_filtered
(" The function prologue begins at 0x%s.\n",
paddr_nz (bounds->prologue_start));
}
if (bounds->body_start != INVALID_ADDRESS)
{
printf_filtered
(" The function body begins at 0x%s.\n", paddr_nz (bounds->body_start));
}
if (bounds->epilogue_start != INVALID_ADDRESS)
{
printf_filtered
(" The function epilogue begins at 0x%s.\n",
paddr_nz (bounds->epilogue_start));
}
if (bounds->function_end != INVALID_ADDRESS)
{
printf_filtered
(" The function ends at 0x%s.\n",
paddr_nz (bounds->function_end));
}
}
void
ppc_print_properties (ppc_function_properties * props)
{
if (props->frameless)
{
printf_filtered (" No stack frame has been allocated.\n");
}
else
{
printf_filtered (" A stack frame has been allocated.\n");
}
if (props->frameptr_reg >= 0)
{
printf_filtered
(" The stack pointer has been saved by alloca() in r%d.\n",
props->frameptr_reg);
}
if (props->offset < 0)
{
printf_filtered (" No registers have been saved.\n");
}
else
{
if (props->offset >= 0)
{
printf_filtered
(" %d bytes of integer and floating-point registers have been saved:\n",
props->offset);
printf_filtered (" 0x%s is the stack setup address.\n",
paddr_nz (props->stack_offset_pc));
}
if (props->saved_gpr >= 0)
{
printf_filtered
(" General-purpose registers r%d--r%d have been saved at offset 0x%x.\n",
props->saved_gpr, 31, props->gpr_offset);
}
else
{
printf_filtered
(" No general-purpose registers have been saved.\n");
}
if (props->saved_fpr >= 0)
{
printf_filtered
(" Floating-point registers r%d--r%d have been saved at offset 0x%x.\n",
props->saved_fpr, 31, props->fpr_offset);
}
else
{
printf_filtered (" No floating-point registers have been saved.\n");
}
}
if (props->lr_saved)
{
printf_filtered
(" The link register has been saved at offset 0x%x.\n",
props->lr_offset);
}
else
{
if (props->lr_invalid != 0)
printf_filtered (" The link register is still valid.\n");
else if (props->lr_reg > -1)
printf_filtered (" The link register is stored in r%d.\n",
props->lr_reg);
else
printf_filtered
(" I have no idea where the link register is stored.\n");
}
if (props->cr_saved)
{
printf_filtered
(" The condition register has been saved at offset 0x%x.\n",
props->cr_offset);
}
}
CORE_ADDR
ppc_parse_instructions (CORE_ADDR start, CORE_ADDR end,
ppc_function_properties * props)
{
CORE_ADDR pc = start;
CORE_ADDR last_recognized_insn = start;
int unrecognized_insn_count = 0;
int max_insn = 6;
int saw_pic_base_setup = 0;
unsigned int lr_reg = 0xffffffff;
unsigned int lr_64_reg = 0xffffffff;
unsigned int cr_reg = 0xffffffff;
int offset2 = 0;
CHECK_FATAL (props != NULL);
ppc_clear_function_properties (props);
CHECK_FATAL (start != INVALID_ADDRESS);
CHECK_FATAL ((start % 4) == 0);
CHECK_FATAL ((end == INVALID_ADDRESS) || (end % 4) == 0);
CHECK_FATAL ((end >= start) || (end == INVALID_ADDRESS));
for (pc = start; (end == INVALID_ADDRESS) || (pc < end);
pc += 4)
{
ULONGEST op = 0;
int insn_recognized = 1;
if (!safe_read_memory_unsigned_integer (pc, 4, &op))
{
ppc_debug ("ppc_parse_instructions: Got an error reading at 0x%s",
paddr_nz (pc));
return last_recognized_insn;
}
if ((op & 0xfe000005) == 0x42000005)
{
props->lr_invalid = pc;
saw_pic_base_setup = 1;
props->pic_base_address = pc + 4;
goto processed_insn;
}
if (!saw_pic_base_setup && (op == 0x7d9f6378 || op == 0x7d9e6378))
{
saw_pic_base_setup = 1;
props->pic_base_reg = (op & 0x1f0000) >> 16;
props->pic_base_address = start;
goto processed_insn;
}
else if ((op & 0xfc000003) == 0x48000001)
{
struct minimal_symbol *msymbol;
LONGEST branch_target;
int recognized_fn_in_prolog = 0;
if (props->lr_invalid == 0)
props->lr_invalid = pc;
branch_target = (op & 0x03fffffc);
if ((branch_target & 0x02000000) == 0x02000000)
{
int addrsize = 26;
ULONGEST valmask;
valmask = (((ULONGEST) 1) << addrsize) - 1;
branch_target &= valmask;
if (branch_target & (valmask ^ (valmask >> 1)))
{
branch_target |= ~valmask;
}
}
branch_target += pc;
msymbol = lookup_minimal_symbol_by_pc (branch_target);
if (msymbol)
{
if (strcmp (SYMBOL_LINKAGE_NAME (msymbol), "save_world") == 0)
{
unsigned int i;
props->frameless = 0;
props->frameptr_used = 0;
props->lr_saved = pc;
props->lr_offset = TARGET_PTR_BIT / 4;
lr_reg = 0;
lr_64_reg = 0;
props->cr_saved = 1;
props->cr_offset = 4;
cr_reg = 0;
props->saved_gpr = 13;
props->gpr_offset = -220;
for (i = 13; i < 32; i++)
props->gpr_bitmap[i] = 1;
props->saved_fpr = 14;
props->fpr_offset = -144;
for (i = 14; i < 32; i++)
props->fpr_bitmap[i] = 1;
recognized_fn_in_prolog = 1;
}
else if (strcmp (SYMBOL_LINKAGE_NAME (msymbol), "saveFP") == 0)
{
ULONGEST store_insn = 0;
int reg;
if (!safe_read_memory_unsigned_integer (branch_target, 4,
&store_insn))
{
ppc_debug
("ppc_parse_instructions: Got an error reading at 0x%s",
paddr_nz (pc));
return pc;
}
reg = GET_SRC_REG (store_insn);
if ((props->saved_fpr == -1) || (props->saved_fpr > reg))
{
unsigned int i;
props->saved_fpr = reg;
for (i = reg; i < 32; i++)
props->fpr_bitmap[i] = 1;
props->fpr_offset = SIGNED_SHORT (store_insn) + offset2;
}
props->lr_saved = pc;
props->lr_offset = TARGET_PTR_BIT / 4;
lr_reg = 0;
lr_64_reg = 0;
recognized_fn_in_prolog = 1;
}
else if (strcmp (SYMBOL_LINKAGE_NAME (msymbol), "saveVec") == 0)
{
recognized_fn_in_prolog = 1;
}
}
if (!recognized_fn_in_prolog)
break;
else
goto processed_insn;
}
else if ((op & 0xfc1fffff) == 0x7c0802a6)
{
int target_reg = (op & 0x03e00000) >> 21;
if (saw_pic_base_setup)
{
props->pic_base_reg = target_reg;
}
else
{
props->lr_reg = target_reg;
lr_reg = (op & 0x03e00000) | 0x90010000;
lr_64_reg = (op & 0x03e00000) | 0xf8010000;
}
goto processed_insn;
}
else if ((op & 0xfc1fffff) == 0x7c0803a6)
{
if (!props->lr_saved && (props->lr_reg > -1))
{
props->lr_valid_again = pc;
}
goto processed_insn;
}
else if ((op & 0xfc1fffff) == 0x7c000026)
{
int target_reg = (op & 0x03e000000) >> 21;
if (target_reg == 0)
{
cr_reg = (op & 0x03e00000) | 0x90010000;
}
goto processed_insn;
}
else if ((op & 0xfc1f0000) == 0xd8010000)
{
int reg = GET_SRC_REG (op);
props->fpr_bitmap[reg] = 1;
if ((props->saved_fpr == -1) || (props->saved_fpr > reg))
{
props->saved_fpr = reg;
props->fpr_offset = SIGNED_SHORT (op) + offset2;
}
goto processed_insn;
}
else if ((op & 0xfc1f0000) == 0xbc010000)
{
unsigned int reg = GET_SRC_REG (op);
if ((props->saved_gpr == -1) || (props->saved_gpr > reg))
{
unsigned int i;
props->saved_gpr = reg;
props->gpr_offset = SIGNED_SHORT (op) + offset2;
for (i = reg; i < 32; i++)
props->gpr_bitmap[i] = 1;
}
goto processed_insn;
}
else if (
(((op & 0xfc1f0000) == 0x90010000)
&& (op & 0x03e00000) >= 0x01a00000)
||
(((op & 0xfc1f0000) == 0xf8010000
&& (op & 0x03e00000) >= 0x01a00000))
)
{
unsigned int reg = GET_SRC_REG (op);
props->gpr_bitmap[reg] = 1;
if ((props->saved_gpr == -1) || (props->saved_gpr > reg))
{
props->saved_gpr = reg;
props->gpr_offset = SIGNED_SHORT (op) + offset2;
}
goto processed_insn;
}
else if ((op & 0xffff0000) == lr_reg || (op & 0xffff0000) == lr_64_reg)
{
props->lr_saved = pc;
props->lr_offset = SIGNED_SHORT (op) + offset2;
lr_reg = 0;
lr_64_reg = 0;
goto processed_insn;
}
else if ((op & 0xffff0000) == cr_reg)
{
props->cr_saved = 1;
props->cr_offset = SIGNED_SHORT (op) + offset2;
cr_reg = 0;
goto processed_insn;
}
else if ((op & 0xffff0000) == 0xf8210000)
{
props->frameless = 0;
props->offset = extract_ds (op);
props->stack_offset_pc = pc;
offset2 = props->offset;
goto processed_insn;
}
else if ((op & 0xffff0000) == 0x94210000
|| (op & 0xffff0000) == 0xf8210000)
{
props->frameless = 0;
props->offset = SIGNED_SHORT (op);
props->stack_offset_pc = pc;
offset2 = props->offset;
goto processed_insn;
}
else if ((op & 0xffff0000) == 0x3c000000)
{
props->offset = (op & 0x0000ffff) << 16;
props->stack_offset_pc = pc;
props->frameless = 0;
goto processed_insn;
}
else if (op == 0x60000000)
{
goto processed_insn;
}
else if ((op & 0xffff0000) == 0x60000000)
{
props->offset |= (op & 0x0000ffff);
props->stack_offset_pc = pc;
props->frameless = 0;
goto processed_insn;
}
else if (op == 0x7c21016e)
{
props->frameless = 0;
offset2 = props->offset;
props->stack_offset_pc = pc;
goto processed_insn;
}
else if (op == 0x7c3e0b78)
{
props->frameptr_used = 1;
props->frameptr_reg = 30;
props->frameptr_pc = pc;
goto processed_insn;
}
else if ((props->frameptr_used && props->frameptr_reg == 30) && ((op & 0xfc1f0000) == 0x901e0000 ||
(op & 0xfc1f0000) == 0xd81e0000 ||
(op & 0xfc1f0000) == 0xfc1e0000))
{
goto processed_insn;
}
else if (op == 0x48000005)
{
goto processed_insn;
}
else if (op == 0x48000004)
{
break;
}
else if ((op & 0xf8000001) == 0x48000000)
{
ULONGEST peekahead_op = 0;
if (props->lr_invalid == 1
&& (end != INVALID_ADDRESS || pc + 4 < end)
&& safe_read_memory_unsigned_integer (pc + 4, 4, &peekahead_op)
&& peekahead_op == 0x0)
{
props->lr_offset = 8;
props->lr_saved = 1;
props->lr_reg = PPC_MACOSX_LR_REGNUM;
props->lr_invalid = 0;
props->frameless = 0;
return pc + 4;
}
break;
}
else if (op == 0x44000002)
{
break;
}
else if ((op >> 22) == 0x20f && !props->minimal_toc_loaded)
{
props->minimal_toc_loaded = 1;
goto processed_insn;
}
else if ((op & 0xfc1f0000) == 0x90010000 ||
(op & 0xfc1f0000) == 0xd8010000 ||
(op & 0xfc1f0000) == 0xfc010000)
{
goto processed_insn;
}
else if ((props->frameptr_used && props->frameptr_reg == 31) && ((op & 0xfc1f0000) == 0x901f0000 ||
(op & 0xfc1f0000) == 0xd81f0000 ||
(op & 0xfc1f0000) == 0xfc1f0000))
{
goto processed_insn;
}
else if (((op & 0xffff0000) == 0x801e0000
|| op == 0x7fc0f214)
&& lr_reg == 0x901e0000)
{
goto processed_insn;
}
else if ((op & 0xffff0000) == 0x3fc00000
|| (op & 0xffff0000) == 0x3bde0000)
{
goto processed_insn;
}
else if (op == 0x603f0000
|| op == 0x7c3f0b78)
{
props->frameptr_used = 1;
props->frameptr_reg = 31;
props->frameptr_pc = pc;
goto processed_insn;
}
else if (op == 0x7d9f6378)
{
goto processed_insn;
}
else if (op == 0x603e0000)
{
props->frameptr_used = 1;
props->frameptr_reg = 30;
props->frameptr_pc = pc;
goto processed_insn;
}
else if ((op & 0xfc1fffff) == 0x38010000)
{
props->frameptr_used = 1;
props->frameptr_reg = (op & ~0x38010000) >> 21;
props->frameptr_pc = pc;
goto processed_insn;
}
else
{
insn_recognized = 0;
unrecognized_insn_count++;
if (unrecognized_insn_count > max_insn)
{
break;
}
}
processed_insn:
if (insn_recognized)
last_recognized_insn = pc;
}
if (unrecognized_insn_count > max_insn || pc >= end)
{
int cleanup_length = 6;
if ((!props->lr_saved
&& props->lr_reg != 0xffffffff
&& props->lr_invalid != 0
&& props->lr_valid_again == INVALID_ADDRESS)
|| (props->saved_gpr != -1 && props->offset == -1))
{
for (; cleanup_length > 0; pc += 4, cleanup_length--)
{
ULONGEST op = 0;
if (!safe_read_memory_unsigned_integer (pc, 4, &op))
break;
if ((op & 0xfc1fffff) == 0x7c0803a6)
{
props->lr_valid_again = pc;
last_recognized_insn = pc;
}
else if ((op & 0xffff0000) == 0x94210000
|| (op & 0xffff0000) == 0xf8210000)
{
props->frameless = 0;
props->stack_offset_pc = pc;
props->offset = SIGNED_SHORT (op);
last_recognized_insn = pc;
}
}
}
}
if (props->offset != -1)
{
props->offset = -props->offset;
}
if (last_recognized_insn == start)
return start;
else
return last_recognized_insn + 4;
}
void
ppc_clear_function_boundaries_request (ppc_function_boundaries_request *
request)
{
request->min_start = INVALID_ADDRESS;
request->max_end = INVALID_ADDRESS;
request->contains_pc = INVALID_ADDRESS;
request->prologue_start = INVALID_ADDRESS;
request->body_start = INVALID_ADDRESS;
request->epilogue_start = INVALID_ADDRESS;
request->function_end = INVALID_ADDRESS;
}
void
ppc_clear_function_boundaries (ppc_function_boundaries * boundaries)
{
boundaries->prologue_start = INVALID_ADDRESS;
boundaries->body_start = INVALID_ADDRESS;
boundaries->epilogue_start = INVALID_ADDRESS;
boundaries->function_end = INVALID_ADDRESS;
}
void
ppc_clear_function_properties (ppc_function_properties * properties)
{
unsigned int i;
for (i = 0; i < 32; i++)
{
properties->gpr_bitmap[i] = 0;
properties->fpr_bitmap[i] = 0;
}
properties->offset = -1;
properties->stack_offset_pc = INVALID_ADDRESS;
properties->saved_gpr = -1;
properties->saved_fpr = -1;
properties->gpr_offset = 0;
properties->fpr_offset = 0;
properties->frameptr_reg = -1;
properties->frameptr_used = 0;
properties->frameptr_pc = INVALID_ADDRESS;
properties->frameless = 1;
properties->lr_saved = 0;
properties->lr_offset = -1;
properties->lr_reg = -1;
properties->lr_invalid = 0;
properties->lr_valid_again = INVALID_ADDRESS;
properties->cr_saved = 0;
properties->cr_offset = -1;
properties->minimal_toc_loaded = 0;
properties->pic_base_reg = 0;
properties->pic_base_address = INVALID_ADDRESS;
}
int
ppc_find_function_boundaries (ppc_function_boundaries_request *request,
ppc_function_boundaries *reply)
{
ppc_function_properties props;
CORE_ADDR lim_pc;
CHECK_FATAL (request != NULL);
CHECK_FATAL (reply != NULL);
if (request->prologue_start != INVALID_ADDRESS)
{
reply->prologue_start = request->prologue_start;
}
else if (request->contains_pc != INVALID_ADDRESS)
{
reply->prologue_start = get_pc_function_start (request->contains_pc);
if (reply->prologue_start == 0)
return -1;
}
CHECK_FATAL (reply->prologue_start != INVALID_ADDRESS);
if ((reply->prologue_start % 4) != 0)
return -1;
lim_pc = refine_prologue_limit
(reply->prologue_start, 0, max_skip_non_prologue_insns);
if (lim_pc != 0)
reply->body_start = lim_pc;
else
lim_pc = INVALID_ADDRESS;
reply->body_start = ppc_parse_instructions
(reply->prologue_start, lim_pc, &props);
return 0;
}
CORE_ADDR *
ppc_frame_saved_regs (struct frame_info *next_frame, void **this_cache)
{
struct ppc_frame_cache *cache;
ppc_function_properties *props;
CORE_ADDR *saved_regs;
CORE_ADDR prev_sp, this_pc, this_func;
int i;
cache = ppc_frame_cache (next_frame, this_cache);
if (cache->saved_regs_valid)
return cache->saved_regs;
saved_regs = cache->saved_regs;
props = ppc_frame_function_properties (next_frame, this_cache);
if (props == NULL)
return NULL;
prev_sp = ppc_frame_find_prev_sp (next_frame, this_cache);
if (props->cr_saved)
{
saved_regs[PPC_MACOSX_CR_REGNUM] = prev_sp + props->cr_offset;
}
if (props->lr_saved)
{
saved_regs[PPC_MACOSX_LR_REGNUM] = prev_sp + props->lr_offset;
}
if (props->frameless
&& ((props->saved_fpr != -1) || (props->saved_gpr != -1)))
{
ppc_debug ("frame_find_saved_regs: "
"registers marked as saved in frameless function; ignoring\n");
return saved_regs;
}
if (props->saved_fpr >= 0)
{
for (i = props->saved_fpr; i < 32; i++)
{
int fpr = PPC_MACOSX_FIRST_FP_REGNUM + i;
if (props->fpr_bitmap[i])
{
long offset = props->fpr_offset +
((i - props->saved_fpr) * register_size
(current_gdbarch, PPC_MACOSX_FIRST_FP_REGNUM));
saved_regs[fpr] = prev_sp + offset;
}
}
}
if (props->saved_gpr >= 0)
{
for (i = props->saved_gpr; i < 32; i++)
{
int gpr = PPC_MACOSX_FIRST_GP_REGNUM + i;
int wordsize = (gdbarch_tdep (current_gdbarch))->wordsize;
if (props->gpr_bitmap[i])
{
long offset =
props->gpr_offset + ((i - props->saved_gpr) * wordsize);
saved_regs[gpr] = prev_sp + offset;
}
}
}
this_pc = frame_pc_unwind (next_frame);
this_func = frame_func_unwind (next_frame);
if (props->frameptr_used && props->frameptr_reg > 0
&& props->frameptr_pc != INVALID_ADDRESS
&& this_pc >= this_func
&& this_pc <= props->frameptr_pc)
{
saved_regs[props->frameptr_reg] = -1;
}
cache->saved_regs_valid = 1;
return saved_regs;
}
struct ppc_function_properties *
ppc_frame_function_properties (struct frame_info *next_frame,
void **this_cache)
{
struct ppc_frame_cache *cache = ppc_frame_cache (next_frame, this_cache);
struct ppc_function_properties *props = &cache->properties;
ppc_function_boundaries *bounds;
ppc_function_boundaries lbounds;
struct frame_info *this_frame = get_prev_frame (next_frame);
ppc_clear_function_properties (props);
bounds = ppc_frame_function_boundaries (next_frame, this_cache);
if (bounds != NULL)
lbounds = *bounds;
else
{
lbounds.prologue_start = this_frame ? get_frame_pc (this_frame) :
INVALID_ADDRESS;
lbounds.body_start = INVALID_ADDRESS;
lbounds.epilogue_start = INVALID_ADDRESS;
lbounds.function_end = INVALID_ADDRESS;
}
if ((lbounds.prologue_start % 4) != 0)
{
if (frame_relative_level (this_frame) == 1)
{
props->lr_saved = 0;
props->lr_offset = 0;
return props;
}
else
{
props->lr_offset = 8;
props->lr_saved = 1;
props->lr_reg = PPC_MACOSX_LR_REGNUM;
props->lr_invalid = 0;
props->frameless = 0;
ppc_debug ("ppc_frame_cache_properties: %s\n",
"Couldn't find function boundaries/parse fn prologue;"
"overriding and fetching $lr from link area");
return props;
}
}
ppc_parse_instructions (lbounds.prologue_start, lbounds.body_start, props);
#if 0
if (get_next_frame (frame) != NULL
&& (get_frame_type (get_next_frame (frame)) == SIGTRAMP_FRAME)
&& ppc_frameless_function_invocation (frame))
{
return sp;
}
#endif
if ((frame_relative_level (next_frame) >= 0)
&& (get_frame_type (next_frame) != DUMMY_FRAME)
&& (get_frame_type (next_frame) != SIGTRAMP_FRAME))
{
props->lr_offset = TARGET_PTR_BIT / 4;
props->lr_saved = 1;
props->lr_reg = PPC_MACOSX_LR_REGNUM;
props->lr_invalid = 0;
props->frameless = 0;
ppc_debug ("ppc_frame_cache_properties: %s\n",
"a non-leaf frame appeared not to save $lr;"
"overriding and fetching from link area");
}
return props;
}
struct ppc_function_boundaries *
ppc_frame_function_boundaries (struct frame_info *next_frame,
void **this_cache)
{
struct ppc_frame_cache *cache = ppc_frame_cache (next_frame, this_cache);
struct ppc_function_boundaries *bounds = &cache->boundaries;
ppc_function_boundaries_request request;
int ret;
if (cache->boundaries_status == 1)
return bounds;
else if (cache->boundaries_status == -1)
return NULL;
ppc_clear_function_boundaries (bounds);
ppc_clear_function_boundaries_request (&request);
request.contains_pc = frame_unwind_address_in_block (next_frame);
if (request.contains_pc == INVALID_ADDRESS)
{
cache->boundaries_status = -1;
return NULL;
}
ret = ppc_find_function_boundaries (&request, bounds);
if (ret != 0)
{
cache->boundaries_status = -1;
return NULL;
}
cache->boundaries_status = 1;
return bounds;
}