diff options
-rw-r--r-- | gdb/ChangeLog | 16 | ||||
-rw-r--r-- | gdb/ppc-sysv-tdep.c | 194 | ||||
-rw-r--r-- | gdb/ppc-tdep.h | 8 | ||||
-rw-r--r-- | gdb/rs6000-tdep.c | 17 |
4 files changed, 231 insertions, 4 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 54940ea..1ad9643 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,5 +1,21 @@ 2003-10-03 Andrew Cagney <cagney@redhat.com> + * rs6000-tdep.c (rs6000_gdbarch_init): When the 64 bit SysV ABI, + set extract_return_value, store_return_value and + use_struct_convention to ppc64_sysv_abi_extract_return_value, + ppc64_sysv_abi_store_return_value and + ppc64_sysv_abi_use_struct_convention. + * ppc-tdep.h (ppc64_sysv_abi_extract_return_value): Declare. + (ppc64_sysv_abi_store_return_value): Declare. + (ppc64_sysv_abi_use_struct_convention): Declare. + * ppc-sysv-tdep.c (enum return_value_convention): Define. + (ppc64_sysv_abi_extract_return_value): New function. + (ppc64_sysv_abi_store_return_value): New function. + (ppc64_sysv_abi_use_struct_convention): New function. + (ppc64_sysv_abi_return_value): New function. + +2003-10-03 Andrew Cagney <cagney@redhat.com> + * ppc-linux-tdep.c (ppc64_linux_convert_from_func_ptr_addr): Only convert a descriptor to a function when it's in the ".opd" section. diff --git a/gdb/ppc-sysv-tdep.c b/gdb/ppc-sysv-tdep.c index d94f188..905b70d 100644 --- a/gdb/ppc-sysv-tdep.c +++ b/gdb/ppc-sysv-tdep.c @@ -325,3 +325,197 @@ ppc_sysv_abi_use_struct_convention (int gcc_p, struct type *value_type) return (TYPE_LENGTH (value_type) > 8); } + + +/* The 64 bit ABI retun value convention. + + Return non-zero if the return-value is stored in a register, return + 0 if the return-value is instead stored on the stack (a.k.a., + struct return convention). + + For a return-value stored in a register: when INVAL is non-NULL, + copy the buffer to the corresponding register return-value location + location; when OUTVAL is non-NULL, fill the buffer from the + corresponding register return-value location. */ + +/* Potential ways that a function can return a value of a given type. */ +enum return_value_convention +{ + /* Where the return value has been squeezed into one or more + registers. */ + RETURN_VALUE_REGISTER_CONVENTION, + /* Commonly known as the "struct return convention". The caller + passes an additional hidden first parameter to the caller. That + parameter contains the address at which the value being returned + should be stored. While typically, and historically, used for + large structs, this is convention is applied to values of many + different types. */ + RETURN_VALUE_STRUCT_CONVENTION +}; + +static enum return_value_convention +ppc64_sysv_abi_return_value (struct type *valtype, struct regcache *regcache, + const void *inval, void *outval) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); + /* Floats and doubles in F1. */ + if (TYPE_CODE (valtype) == TYPE_CODE_FLT + && TYPE_LENGTH (valtype) <= 8) + { + char regval[MAX_REGISTER_SIZE]; + struct type *regtype = register_type (current_gdbarch, FP0_REGNUM); + if (inval != NULL) + { + convert_typed_floating (inval, valtype, regval, regtype); + regcache_cooked_write (regcache, FP0_REGNUM + 1, regval); + } + if (outval != NULL) + { + regcache_cooked_read (regcache, FP0_REGNUM + 1, regval); + convert_typed_floating (regval, regtype, outval, valtype); + } + return RETURN_VALUE_REGISTER_CONVENTION; + } + if (TYPE_CODE (valtype) == TYPE_CODE_INT + && TYPE_LENGTH (valtype) <= 8) + { + /* Integers in r3. */ + if (inval != NULL) + { + /* Be careful to sign extend the value. */ + regcache_cooked_write_unsigned (regcache, tdep->ppc_gp0_regnum + 3, + unpack_long (valtype, inval)); + } + if (outval != NULL) + { + /* Extract the integer from r3. Since this is truncating the + value, there isn't a sign extension problem. */ + ULONGEST regval; + regcache_cooked_read_unsigned (regcache, tdep->ppc_gp0_regnum + 3, + ®val); + store_unsigned_integer (outval, TYPE_LENGTH (valtype), regval); + } + return RETURN_VALUE_REGISTER_CONVENTION; + } + /* All pointers live in r3. */ + if (TYPE_CODE (valtype) == TYPE_CODE_PTR) + { + /* All pointers live in r3. */ + if (inval != NULL) + regcache_cooked_write (regcache, tdep->ppc_gp0_regnum + 3, inval); + if (outval != NULL) + regcache_cooked_read (regcache, tdep->ppc_gp0_regnum + 3, outval); + return RETURN_VALUE_REGISTER_CONVENTION; + } + if (TYPE_CODE (valtype) == TYPE_CODE_ARRAY + && TYPE_LENGTH (valtype) <= 8 + && TYPE_CODE (TYPE_TARGET_TYPE (valtype)) == TYPE_CODE_INT + && TYPE_LENGTH (TYPE_TARGET_TYPE (valtype)) == 1) + { + /* Small character arrays are returned, right justified, in r3. */ + int offset = (register_size (current_gdbarch, tdep->ppc_gp0_regnum + 3) + - TYPE_LENGTH (valtype)); + if (inval != NULL) + regcache_cooked_write_part (regcache, tdep->ppc_gp0_regnum + 3, + offset, TYPE_LENGTH (valtype), inval); + if (outval != NULL) + regcache_cooked_read_part (regcache, tdep->ppc_gp0_regnum + 3, + offset, TYPE_LENGTH (valtype), outval); + return RETURN_VALUE_REGISTER_CONVENTION; + } + /* Big floating point values get stored in adjacent floating + point registers. */ + if (TYPE_CODE (valtype) == TYPE_CODE_FLT + && (TYPE_LENGTH (valtype) == 16 + || TYPE_LENGTH (valtype) == 32)) + { + if (inval || outval != NULL) + { + int i; + for (i = 0; i < TYPE_LENGTH (valtype) / 8; i++) + { + if (inval != NULL) + regcache_cooked_write (regcache, FP0_REGNUM + 1 + i, + (const bfd_byte *) inval + i * 8); + if (outval != NULL) + regcache_cooked_read (regcache, FP0_REGNUM + 1 + i, + (bfd_byte *) outval + i * 8); + } + } + return RETURN_VALUE_REGISTER_CONVENTION; + } + /* Complex values get returned in f1:f2, need to convert. */ + if (TYPE_CODE (valtype) == TYPE_CODE_COMPLEX + && (TYPE_LENGTH (valtype) == 8 || TYPE_LENGTH (valtype) == 16)) + { + if (regcache != NULL) + { + int i; + for (i = 0; i < 2; i++) + { + char regval[MAX_REGISTER_SIZE]; + struct type *regtype = register_type (current_gdbarch, FP0_REGNUM); + if (inval != NULL) + { + convert_typed_floating ((const bfd_byte *) inval + i * (TYPE_LENGTH (valtype) / 2), + valtype, regval, regtype); + regcache_cooked_write (regcache, FP0_REGNUM + 1 + i, regval); + } + if (outval != NULL) + { + regcache_cooked_read (regcache, FP0_REGNUM + 1 + i, regval); + convert_typed_floating (regval, regtype, + (bfd_byte *) outval + i * (TYPE_LENGTH (valtype) / 2), + valtype); + } + } + } + return RETURN_VALUE_REGISTER_CONVENTION; + } + /* Big complex values get stored in f1:f4. */ + if (TYPE_CODE (valtype) == TYPE_CODE_COMPLEX + && TYPE_LENGTH (valtype) == 32) + { + if (regcache != NULL) + { + int i; + for (i = 0; i < 4; i++) + { + if (inval != NULL) + regcache_cooked_write (regcache, FP0_REGNUM + 1 + i, + (const bfd_byte *) inval + i * 8); + if (outval != NULL) + regcache_cooked_read (regcache, FP0_REGNUM + 1 + i, + (bfd_byte *) outval + i * 8); + } + } + return RETURN_VALUE_REGISTER_CONVENTION; + } + return RETURN_VALUE_STRUCT_CONVENTION; +} + +int +ppc64_sysv_abi_use_struct_convention (int gcc_p, struct type *value_type) +{ + return (ppc64_sysv_abi_return_value (value_type, NULL, NULL, NULL) + == RETURN_VALUE_STRUCT_CONVENTION); +} + +void +ppc64_sysv_abi_extract_return_value (struct type *valtype, + struct regcache *regbuf, + void *valbuf) +{ + if (ppc64_sysv_abi_return_value (valtype, regbuf, NULL, valbuf) + != RETURN_VALUE_REGISTER_CONVENTION) + error ("Function return value unknown"); +} + +void +ppc64_sysv_abi_store_return_value (struct type *valtype, + struct regcache *regbuf, + const void *valbuf) +{ + if (!ppc64_sysv_abi_return_value (valtype, regbuf, valbuf, NULL)) + error ("Function return value location unknown"); +} diff --git a/gdb/ppc-tdep.h b/gdb/ppc-tdep.h index dd84fd1..fd150a0 100644 --- a/gdb/ppc-tdep.h +++ b/gdb/ppc-tdep.h @@ -47,6 +47,14 @@ struct link_map_offsets *ppc_linux_svr4_fetch_link_map_offsets (void); void ppc_linux_supply_gregset (char *buf); void ppc_linux_supply_fpregset (char *buf); +int ppc64_sysv_abi_use_struct_convention (int gcc_p, struct type *value_type); +void ppc64_sysv_abi_extract_return_value (struct type *valtype, + struct regcache *regbuf, + void *valbuf); +void ppc64_sysv_abi_store_return_value (struct type *valtype, + struct regcache *regbuf, + const void *valbuf); + /* From rs6000-tdep.c... */ CORE_ADDR rs6000_frame_saved_pc (struct frame_info *fi); diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c index deb5555..fe51340 100644 --- a/gdb/rs6000-tdep.c +++ b/gdb/rs6000-tdep.c @@ -2830,9 +2830,16 @@ rs6000_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) set_gdbarch_pc_regnum (gdbarch, 64); set_gdbarch_sp_regnum (gdbarch, 1); set_gdbarch_deprecated_fp_regnum (gdbarch, 1); - set_gdbarch_deprecated_extract_return_value (gdbarch, - rs6000_extract_return_value); - set_gdbarch_deprecated_store_return_value (gdbarch, rs6000_store_return_value); + if (sysv_abi && wordsize == 8) + { + set_gdbarch_extract_return_value (gdbarch, ppc64_sysv_abi_extract_return_value); + set_gdbarch_store_return_value (gdbarch, ppc64_sysv_abi_store_return_value); + } + else + { + set_gdbarch_deprecated_extract_return_value (gdbarch, rs6000_extract_return_value); + set_gdbarch_deprecated_store_return_value (gdbarch, rs6000_store_return_value); + } if (v->arch == bfd_arch_powerpc) switch (v->mach) @@ -2967,9 +2974,11 @@ rs6000_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) /* Not sure on this. FIXMEmgo */ set_gdbarch_frame_args_skip (gdbarch, 8); - if (sysv_abi) + if (sysv_abi && wordsize == 4) set_gdbarch_use_struct_convention (gdbarch, ppc_sysv_abi_use_struct_convention); + else if (sysv_abi && wordsize == 8) + set_gdbarch_use_struct_convention (gdbarch, ppc64_sysv_abi_use_struct_convention); else set_gdbarch_use_struct_convention (gdbarch, rs6000_use_struct_convention); |