ppc-macosx-frameinfo.c [plain text]
#include "ppc-macosx-regs.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"
static int max_skip_non_prologue_insns = 2;
inline int SIGNED_SHORT (long x)
{
if (sizeof (short) == 2) {
return ((short) x);
} else {
return (((x & 0xffff) ^ 0x8000) - 0x8000);
}
}
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 %s.\n",
core_addr_to_string (bounds->prologue_start));
}
if (bounds->body_start != INVALID_ADDRESS) {
printf_filtered
(" The function body begins at %s.\n",
core_addr_to_string (bounds->body_start));
}
if (bounds->epilogue_start != INVALID_ADDRESS) {
printf_filtered
(" The function epilogue begins at %s.\n",
core_addr_to_string (bounds->epilogue_start));
}
if (bounds->function_end != INVALID_ADDRESS) {
printf_filtered
(" The function ends at %s.\n",
core_addr_to_string (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);
}
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);
}
}
struct read_memory_unsigned_int_args
{
CORE_ADDR addr;
int len;
unsigned long ret_val;
};
int wrap_read_memory_unsigned_integer (void *in_args)
{
struct read_memory_unsigned_int_args *args
= (struct read_memory_unsigned_int_args *) in_args;
args->ret_val = read_memory_unsigned_integer (args->addr, args->len);
return 1;
}
int
safe_read_memory_unsigned_integer (CORE_ADDR addr, int len,
unsigned long *val)
{
struct read_memory_unsigned_int_args args;
args.addr = addr;
args.len = len;
if (! catch_errors (wrap_read_memory_unsigned_integer,
&args,"", RETURN_MASK_ERROR))
{
return 0;
}
*val = args.ret_val;
return 1;
}
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 insn_count = 1;
int max_insn = 6;
int saw_pic_base_setup = 0;
int lr_reg = -1;
int cr_reg = -1;
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, insn_count++) {
unsigned long op;
int insn_recognized = 1;
if (!safe_read_memory_unsigned_integer (pc, 4, &op))
{
ppc_debug ("ppc_parse_instructions: Got an error reading at 0x%lx",
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 + 4;
msymbol = lookup_minimal_symbol_by_pc (branch_target);
if (msymbol)
{
if (strcmp (SYMBOL_SOURCE_NAME (msymbol), "save_world") == 0)
{
props->frameless = 0;
props->frameptr_used = 0;
props->lr_saved = pc;
props->lr_offset = 8;
lr_reg = 0;
props->cr_saved = 1;
props->cr_offset = 4;
cr_reg = 0;
props->saved_gpr = 13;
props->gpr_offset = -220;
props->saved_fpr = 14;
props->fpr_offset = -144;
recognized_fn_in_prolog = 1;
}
else if (strcmp (SYMBOL_SOURCE_NAME (msymbol), "saveFP") == 0)
{
unsigned long store_insn;
int reg;
if (!safe_read_memory_unsigned_integer (branch_target, 4,
&store_insn))
{
ppc_debug ("ppc_parse_instructions: Got an error reading at 0x%lx",
pc);
return pc;
}
reg = GET_SRC_REG (store_insn);
if ((props->saved_fpr == -1)
|| (props->saved_fpr > reg)) {
props->saved_fpr = reg;
props->fpr_offset = SIGNED_SHORT (store_insn)
+ offset2;
}
props->lr_saved = pc;
props->lr_offset = 8;
lr_reg = 0;
recognized_fn_in_prolog = 1;
}
else if (strcmp (SYMBOL_SOURCE_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;
}
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);
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)
|| ((op & 0xfc1f0000) == 0x90010000
&& (op & 0x03e00000) >= 0x01a00000))
{
int reg = GET_SRC_REG (op);
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)
{
props->lr_saved = pc;
props->lr_offset = SIGNED_SHORT (op) + offset2;
lr_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) == 0x94210000)
{
props->frameless = 0;
props->offset = SIGNED_SHORT (op);
offset2 = props->offset;
props->sp_setup_pc = pc;
goto processed_insn;
}
else if ((op & 0xffff0000) == 0x3c000000)
{
props->offset = (op & 0x0000ffff) << 16;
props->frameless = 0;
goto processed_insn;
}
else if (op == 0x60000000)
{
goto processed_insn;
}
else if ((op & 0xffff0000) == 0x60000000)
{
props->offset |= (op & 0x0000ffff);
props->frameless = 0;
goto processed_insn;
}
else if (op == 0x7c21016e)
{
props->frameless = 0;
offset2 = props->offset;
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)
{
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;
if (insn_count > max_insn)
{
int cleanup_length = 6;
if (!props->lr_saved
&& props->lr_reg != -1
&& props->lr_invalid != 0
&& props->lr_valid_again == INVALID_ADDRESS)
{
for (; cleanup_length > 0;
pc += 4, cleanup_length--)
{
unsigned long op;
if (!safe_read_memory_unsigned_integer (pc, 4, &op))
break;
if ((op & 0xfc1fffff) == 0x7c0803a6)
{
props->lr_valid_again = pc;
last_recognized_insn = pc;
break;
}
}
}
break;
}
}
processed_insn:
if (insn_recognized)
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)
{
properties->offset = -1;
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->sp_setup_pc = 0;
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;
}
int
ppc_frame_cache_boundaries (struct frame_info *frame,
struct ppc_function_boundaries *retbounds)
{
if (!frame->extra_info->bounds) {
if (ppc_is_dummy_frame (frame)) {
frame->extra_info->bounds = (struct ppc_function_boundaries *)
frame_obstack_zalloc (sizeof (ppc_function_boundaries));
CHECK_FATAL (frame->extra_info->bounds != NULL);
ppc_clear_function_boundaries (frame->extra_info->bounds);
frame->extra_info->bounds->prologue_start = INVALID_ADDRESS;
frame->extra_info->bounds->body_start = INVALID_ADDRESS;
frame->extra_info->bounds->epilogue_start = INVALID_ADDRESS;
frame->extra_info->bounds->function_end = INVALID_ADDRESS;
} else {
ppc_function_boundaries_request request;
ppc_function_boundaries lbounds;
int ret;
ppc_clear_function_boundaries (&lbounds);
ppc_clear_function_boundaries_request (&request);
request.contains_pc = frame_address_in_block (frame);
if (request.contains_pc == INVALID_ADDRESS)
return -1;
ret = ppc_find_function_boundaries (&request, &lbounds);
if (ret != 0)
return ret;
frame->extra_info->bounds = (struct ppc_function_boundaries *)
frame_obstack_zalloc (sizeof (ppc_function_boundaries));
CHECK_FATAL (frame->extra_info->bounds != NULL);
memcpy (frame->extra_info->bounds, &lbounds,
sizeof (ppc_function_boundaries));
}
}
if (retbounds != NULL) {
memcpy (retbounds, frame->extra_info->bounds,
sizeof (ppc_function_boundaries));
}
return 0;
}
int
ppc_frame_cache_properties (struct frame_info *frame,
struct ppc_function_properties *retprops)
{
if (! frame->extra_info)
return 1;
if (! frame->extra_info->props)
{
if (ppc_is_dummy_frame (frame))
{
ppc_function_properties *props;
frame->extra_info->props = (struct ppc_function_properties *)
frame_obstack_zalloc (sizeof (ppc_function_properties));
CHECK_FATAL (frame->extra_info->props != NULL);
props = frame->extra_info->props;
ppc_clear_function_properties (props);
props->offset = 0;
props->saved_gpr = -1;
props->saved_fpr = -1;
props->gpr_offset = -1;
props->fpr_offset = -1;
props->frameless = 0;
props->frameptr_used = 0;
props->frameptr_reg = -1;
props->frameptr_pc = INVALID_ADDRESS;
props->lr_saved = 1;
props->lr_offset = DEFAULT_LR_SAVE;
props->cr_saved = 0;
props->cr_offset = -1;
props->minimal_toc_loaded = 0;
}
else
{
int ret;
ppc_function_properties *props;
ppc_function_boundaries *bounds;
ret = ppc_frame_cache_boundaries (frame, NULL);
if (ret != 0) { return ret; }
bounds = frame->extra_info->bounds;
if ((bounds->prologue_start % 4) != 0)
return -1;
if ((frame->pc % 4) != 0)
return -1;
frame->extra_info->props = (struct ppc_function_properties *)
frame_obstack_zalloc (sizeof (ppc_function_properties));
CHECK_FATAL (frame->extra_info->props != NULL);
props = frame->extra_info->props;
ppc_clear_function_properties (props);
ppc_parse_instructions (bounds->prologue_start,
bounds->body_start, props);
if (get_next_frame (frame) != NULL)
{
props->lr_offset = 8;
props->lr_saved = 1;
props->lr_reg = LR_REGNUM;
props->lr_invalid = 0;
ppc_debug ("ppc_frame_cache_properties: %s\n",
"a non-leaf frame appeared not to save $lr;"
"overriding and fetching from link area");
}
}
}
else if (get_next_frame (frame) != NULL)
{
ppc_function_properties *props = frame->extra_info->props;
props->lr_offset = 8;
props->lr_saved = 1;
props->lr_reg = LR_REGNUM;
props->lr_invalid = 0;
ppc_debug ("ppc_frame_cache_properties: %s\n",
"a non-leaf frame appeared not to save $lr;"
"overriding and fetching from link area");
}
if (retprops != NULL) {
memcpy (retprops, frame->extra_info->props,
sizeof (ppc_function_properties));
}
return 0;
}