diff options
Diffstat (limited to 'gdb/arm-tdep.c')
-rw-r--r-- | gdb/arm-tdep.c | 299 |
1 files changed, 156 insertions, 143 deletions
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c index dbe1483..ca15251 100644 --- a/gdb/arm-tdep.c +++ b/gdb/arm-tdep.c @@ -1334,137 +1334,6 @@ arm_fix_call_dummy (char *dummy, CORE_ADDR pc, CORE_ADDR fun, int nargs, write_register (4, fun); } -/* Note: ScottB - - This function does not support passing parameters using the FPA - variant of the APCS. It passes any floating point arguments in the - general registers and/or on the stack. */ - -static CORE_ADDR -arm_push_arguments (int nargs, struct value **args, CORE_ADDR sp, - int struct_return, CORE_ADDR struct_addr) -{ - CORE_ADDR fp; - int argnum; - int argreg; - int nstack; - int simd_argreg; - int second_pass; - struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); - - /* Walk through the list of args and determine how large a temporary - stack is required. Need to take care here as structs may be - passed on the stack, and we have to to push them. On the second - pass, do the store. */ - nstack = 0; - fp = sp; - for (second_pass = 0; second_pass < 2; second_pass++) - { - /* Compute the FP using the information computed during the - first pass. */ - if (second_pass) - fp = sp - nstack; - - simd_argreg = 0; - argreg = ARM_A1_REGNUM; - nstack = 0; - - /* The struct_return pointer occupies the first parameter - passing register. */ - if (struct_return) - { - if (second_pass) - { - if (arm_debug) - fprintf_unfiltered (gdb_stdlog, - "struct return in %s = 0x%s\n", - REGISTER_NAME (argreg), - paddr (struct_addr)); - write_register (argreg, struct_addr); - } - argreg++; - } - - for (argnum = 0; argnum < nargs; argnum++) - { - int len; - struct type *arg_type; - struct type *target_type; - enum type_code typecode; - char *val; - - arg_type = check_typedef (VALUE_TYPE (args[argnum])); - len = TYPE_LENGTH (arg_type); - target_type = TYPE_TARGET_TYPE (arg_type); - typecode = TYPE_CODE (arg_type); - val = VALUE_CONTENTS (args[argnum]); - - /* If the argument is a pointer to a function, and it is a - Thumb function, create a LOCAL copy of the value and set - the THUMB bit in it. */ - if (second_pass - && TYPE_CODE_PTR == typecode - && target_type != NULL - && TYPE_CODE_FUNC == TYPE_CODE (target_type)) - { - CORE_ADDR regval = extract_address (val, len); - if (arm_pc_is_thumb (regval)) - { - val = alloca (len); - store_address (val, len, MAKE_THUMB_ADDR (regval)); - } - } - - /* Copy the argument to general registers or the stack in - register-sized pieces. Large arguments are split between - registers and stack. */ - while (len > 0) - { - int partial_len = len < REGISTER_SIZE ? len : REGISTER_SIZE; - - if (argreg <= ARM_LAST_ARG_REGNUM) - { - /* The argument is being passed in a general purpose - register. */ - if (second_pass) - { - CORE_ADDR regval = extract_address (val, - partial_len); - if (arm_debug) - fprintf_unfiltered (gdb_stdlog, - "arg %d in %s = 0x%s\n", - argnum, - REGISTER_NAME (argreg), - phex (regval, REGISTER_SIZE)); - write_register (argreg, regval); - } - argreg++; - } - else - { - if (second_pass) - { - /* Push the arguments onto the stack. */ - if (arm_debug) - fprintf_unfiltered (gdb_stdlog, - "arg %d @ 0x%s + %d\n", - argnum, paddr (fp), nstack); - write_memory (fp + nstack, val, REGISTER_SIZE); - } - nstack += REGISTER_SIZE; - } - - len -= partial_len; - val += partial_len; - } - - } - } - - /* Return the bottom of the argument list (pointed to by fp). */ - return fp; -} - /* Pop the current frame. So long as the frame info has been initialized properly (see arm_init_extra_frame_info), this code works for dummy frames as well as regular frames. I.e, there's no @@ -1499,6 +1368,161 @@ arm_pop_frame (void) flush_cached_frames (); } +/* When arguments must be pushed onto the stack, they go on in reverse + order. The code below implements a FILO (stack) to do this. */ + +struct stack_item +{ + int len; + struct stack_item *prev; + void *data; +}; + +static struct stack_item * +push_stack_item (struct stack_item *prev, void *contents, int len) +{ + struct stack_item *si; + si = xmalloc (sizeof (struct stack_item)); + si->data = malloc (len); + si->len = len; + si->prev = prev; + memcpy (si->data, contents, len); + return si; +} + +static struct stack_item * +pop_stack_item (struct stack_item *si) +{ + struct stack_item *dead = si; + si = si->prev; + xfree (dead->data); + xfree (dead); + return si; +} + +/* We currently only support passing parameters in integer registers. This + conforms with GCC's default model. Several other variants exist and + we should probably support some of them based on the selected ABI. */ + +static CORE_ADDR +arm_push_dummy_call (struct gdbarch *gdbarch, struct regcache *regcache, + CORE_ADDR dummy_addr, int nargs, struct value **args, + CORE_ADDR sp, int struct_return, CORE_ADDR struct_addr) +{ + int argnum; + int argreg; + int nstack; + struct stack_item *si = NULL; + + /* Set the return address. For the ARM, the return breakpoint is always + at DUMMY_ADDR. */ + /* XXX Fix for Thumb. */ + regcache_cooked_write_unsigned (regcache, ARM_LR_REGNUM, dummy_addr); + + /* Walk through the list of args and determine how large a temporary + stack is required. Need to take care here as structs may be + passed on the stack, and we have to to push them. */ + nstack = 0; + + argreg = ARM_A1_REGNUM; + nstack = 0; + + /* Some platforms require a double-word aligned stack. Make sure sp + is correctly aligned before we start. We always do this even if + it isn't really needed -- it can never hurt things. */ + sp &= ~(CORE_ADDR)(2 * REGISTER_SIZE - 1); + + /* The struct_return pointer occupies the first parameter + passing register. */ + if (struct_return) + { + if (arm_debug) + fprintf_unfiltered (gdb_stdlog, "struct return in %s = 0x%s\n", + REGISTER_NAME (argreg), paddr (struct_addr)); + regcache_cooked_write_unsigned (regcache, argreg, struct_addr); + argreg++; + } + + for (argnum = 0; argnum < nargs; argnum++) + { + int len; + struct type *arg_type; + struct type *target_type; + enum type_code typecode; + char *val; + + arg_type = check_typedef (VALUE_TYPE (args[argnum])); + len = TYPE_LENGTH (arg_type); + target_type = TYPE_TARGET_TYPE (arg_type); + typecode = TYPE_CODE (arg_type); + val = VALUE_CONTENTS (args[argnum]); + + /* If the argument is a pointer to a function, and it is a + Thumb function, create a LOCAL copy of the value and set + the THUMB bit in it. */ + if (TYPE_CODE_PTR == typecode + && target_type != NULL + && TYPE_CODE_FUNC == TYPE_CODE (target_type)) + { + CORE_ADDR regval = extract_address (val, len); + if (arm_pc_is_thumb (regval)) + { + val = alloca (len); + store_address (val, len, MAKE_THUMB_ADDR (regval)); + } + } + + /* Copy the argument to general registers or the stack in + register-sized pieces. Large arguments are split between + registers and stack. */ + while (len > 0) + { + int partial_len = len < REGISTER_SIZE ? len : REGISTER_SIZE; + + if (argreg <= ARM_LAST_ARG_REGNUM) + { + /* The argument is being passed in a general purpose + register. */ + CORE_ADDR regval = extract_address (val, partial_len); + if (arm_debug) + fprintf_unfiltered (gdb_stdlog, "arg %d in %s = 0x%s\n", + argnum, REGISTER_NAME (argreg), + phex (regval, REGISTER_SIZE)); + regcache_cooked_write_unsigned (regcache, argreg, regval); + argreg++; + } + else + { + /* Push the arguments onto the stack. */ + if (arm_debug) + fprintf_unfiltered (gdb_stdlog, "arg %d @ sp + %d\n", + argnum, nstack); + si = push_stack_item (si, val, REGISTER_SIZE); + nstack += REGISTER_SIZE; + } + + len -= partial_len; + val += partial_len; + } + } + /* If we have an odd number of words to push, then decrement the stack + by one word now, so first stack argument will be dword aligned. */ + if (nstack & 4) + sp -= 4; + + while (si) + { + sp -= si->len; + write_memory (sp, si->data, si->len); + si = pop_stack_item (si); + } + + /* Finally, update teh SP register. */ + regcache_cooked_write_unsigned (regcache, ARM_SP_REGNUM, sp); + + return sp; +} + static void print_fpu_flags (int flags) { @@ -2456,15 +2480,6 @@ arm_store_return_value (struct type *type, struct regcache *regs, } } -/* Store the address of the place in which to copy the structure the - subroutine will return. This is called from call_function. */ - -static void -arm_store_struct_return (CORE_ADDR addr, CORE_ADDR sp) -{ - write_register (ARM_A1_REGNUM, addr); -} - static int arm_get_longjmp_target (CORE_ADDR *pc) { @@ -2928,9 +2943,8 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) set_gdbarch_fix_call_dummy (gdbarch, generic_fix_call_dummy); set_gdbarch_call_dummy_address (gdbarch, entry_point_address); - set_gdbarch_push_return_address (gdbarch, arm_push_return_address); - set_gdbarch_push_arguments (gdbarch, arm_push_arguments); + set_gdbarch_push_dummy_call (gdbarch, arm_push_dummy_call); /* Frame handling. */ set_gdbarch_deprecated_frame_chain_valid (gdbarch, arm_frame_chain_valid); @@ -2994,7 +3008,6 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) /* Returning results. */ set_gdbarch_extract_return_value (gdbarch, arm_extract_return_value); set_gdbarch_store_return_value (gdbarch, arm_store_return_value); - set_gdbarch_deprecated_store_struct_return (gdbarch, arm_store_struct_return); set_gdbarch_use_struct_convention (gdbarch, arm_use_struct_convention); set_gdbarch_extract_struct_value_address (gdbarch, arm_extract_struct_value_address); |