diff options
author | Scott Bambrough <scottb@netwinder.org> | 2000-04-07 22:26:11 +0000 |
---|---|---|
committer | Scott Bambrough <scottb@netwinder.org> | 2000-04-07 22:26:11 +0000 |
commit | a128f2e0f4cebfdce2bf261e0d9006489d09ae74 (patch) | |
tree | 8f95c24bad40ece52a045fb69841010952fe081a /gdb/arm-linux-tdep.c | |
parent | 51b2cea34dded93e80b05ed7081c8e08548495b5 (diff) | |
download | gdb-a128f2e0f4cebfdce2bf261e0d9006489d09ae74.tar.gz |
2000-04-07 Scott Bambrough <scottb@netwinder.org>
* ChangeLog: Correct date on last entry.
* arm-linux-tdep.c (arm_linux_push_arguments): New function.
* config/arm/tm-linux: Redefined PUSH_ARGUMENTS for Linux.
* config/arm/tm-embed: Fix build warning from redefinition of
LOWEST_PC.
* config/arm/tm-arm.h: Remove TARGET_BYTE_ORDER_SELECTABLE.
* config/arm/tm-wince.h: Remove TARGET_BYTE_ORDER_SELECTABLE and
TARGET_BYTE_ORDER. Add TARGET_BYTE_ORDER_SELECTABLE_P to
override default in tm-arm.h. Use default target byte order
from tm-arm.h.
Diffstat (limited to 'gdb/arm-linux-tdep.c')
-rw-r--r-- | gdb/arm-linux-tdep.c | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/gdb/arm-linux-tdep.c b/gdb/arm-linux-tdep.c index bf26300ff03..8a575a4ff49 100644 --- a/gdb/arm-linux-tdep.c +++ b/gdb/arm-linux-tdep.c @@ -22,6 +22,7 @@ #include "target.h" #include "value.h" #include "gdbtypes.h" +#include "floatformat.h" #ifdef GET_LONGJMP_TARGET @@ -76,6 +77,150 @@ arm_linux_extract_return_value (struct type *type, memcpy (valbuf, ®buf[REGISTER_BYTE (regnum)], TYPE_LENGTH (type)); } +/* 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. + + FIXME: This and arm_push_arguments should be merged. However this + function breaks on a little endian host, big endian target + using the COFF file format. ELF is ok. + + ScottB. */ + +/* Addresses for calling Thumb functions have the bit 0 set. + Here are some macros to test, set, or clear bit 0 of addresses. */ +#define IS_THUMB_ADDR(addr) ((addr) & 1) +#define MAKE_THUMB_ADDR(addr) ((addr) | 1) +#define UNMAKE_THUMB_ADDR(addr) ((addr) & ~1) + +CORE_ADDR +arm_linux_push_arguments (int nargs, value_ptr * args, CORE_ADDR sp, + int struct_return, CORE_ADDR struct_addr) +{ + char *fp; + int argnum, argreg, nstack_size; + + /* 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_size = -4 * REGISTER_SIZE; /* Some arguments go into A1-A4. */ + + if (struct_return) /* The struct address goes in A1. */ + nstack_size += REGISTER_SIZE; + + /* Walk through the arguments and add their size to nstack_size. */ + for (argnum = 0; argnum < nargs; argnum++) + { + int len; + struct type *arg_type; + + arg_type = check_typedef (VALUE_TYPE (args[argnum])); + len = TYPE_LENGTH (arg_type); + + /* ANSI C code passes float arguments as integers, K&R code + passes float arguments as doubles. Correct for this here. */ + if (TYPE_CODE_FLT == TYPE_CODE (arg_type) && REGISTER_SIZE == len) + nstack_size += FP_REGISTER_VIRTUAL_SIZE; + else + nstack_size += len; + } + + /* Allocate room on the stack, and initialize our stack frame + pointer. */ + fp = NULL; + if (nstack_size > 0) + { + sp -= nstack_size; + fp = (char *) sp; + } + + /* Initialize the integer argument register pointer. */ + argreg = A1_REGNUM; + + /* The struct_return pointer occupies the first parameter passing + register. */ + if (struct_return) + write_register (argreg++, struct_addr); + + /* Process arguments from left to right. Store as many as allowed + in the parameter passing registers (A1-A4), and save the rest on + the temporary stack. */ + for (argnum = 0; argnum < nargs; argnum++) + { + int len; + char *val; + double dbl_arg; + CORE_ADDR regval; + enum type_code typecode; + struct type *arg_type, *target_type; + + arg_type = check_typedef (VALUE_TYPE (args[argnum])); + target_type = TYPE_TARGET_TYPE (arg_type); + len = TYPE_LENGTH (arg_type); + typecode = TYPE_CODE (arg_type); + val = (char *) VALUE_CONTENTS (args[argnum]); + + /* ANSI C code passes float arguments as integers, K&R code + passes float arguments as doubles. The .stabs record for + for ANSI prototype floating point arguments records the + type as FP_INTEGER, while a K&R style (no prototype) + .stabs records the type as FP_FLOAT. In this latter case + the compiler converts the float arguments to double before + calling the function. */ + if (TYPE_CODE_FLT == typecode && REGISTER_SIZE == len) + { + /* Float argument in buffer is in host format. Read it and + convert to DOUBLEST, and store it in target double. */ + DOUBLEST dblval; + + len = TARGET_DOUBLE_BIT / TARGET_CHAR_BIT; + floatformat_to_doublest (HOST_FLOAT_FORMAT, val, &dblval); + store_floating (&dbl_arg, len, dblval); + val = (char *) &dbl_arg; + } + + /* If the argument is a pointer to a function, and it is a Thumb + function, set the low bit of the pointer. */ + if (TYPE_CODE_PTR == typecode + && NULL != target_type + && TYPE_CODE_FUNC == TYPE_CODE (target_type)) + { + CORE_ADDR regval = extract_address (val, len); + if (arm_pc_is_thumb (regval)) + 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) + { + /* It's an argument being passed in a general register. */ + regval = extract_address (val, partial_len); + write_register (argreg++, regval); + } + else + { + /* Push the arguments onto the stack. */ + write_memory ((CORE_ADDR) fp, val, REGISTER_SIZE); + fp += REGISTER_SIZE; + } + + len -= partial_len; + val += partial_len; + } + } + + /* Return adjusted stack pointer. */ + return sp; +} + void _initialize_arm_linux_tdep (void) { |