310 lines
9.4 KiB
Diff
310 lines
9.4 KiB
Diff
commit cc0e89c519912e0e4e75a2fc0d836f715cdc6806
|
||
Author: Ulrich Weigand <ulrich.weigand@de.ibm.com>
|
||
Date: Tue Feb 4 18:42:35 2014 +0100
|
||
|
||
PowerPC64 ELFv2 ABI: structure passing / return
|
||
|
||
Another significant difference in the ELFv2 ABI is that "homogeneous"
|
||
floating-point and vector aggregates, i.e. aggregates the consist
|
||
(recursively) only of members of the same floating-point or vector type,
|
||
are passed in a series of floating-point / vector registers, as if they
|
||
were seperate parameters. (This is similar to the ARM ABI.) This
|
||
applies to both calls and returns.
|
||
|
||
In addition when returning any aggregate of up to 16 bytes, ELFv2 now
|
||
used general-purpose registers.
|
||
|
||
This patch adds support for these aspects of the ABI, which is relatively
|
||
straightforward after the refactoring patch to ppc-sysv-tdep.c.
|
||
|
||
gdb/ChangeLog:
|
||
|
||
* ppc-sysv-tdep.c (ppc64_aggregate_candidate): New routine.
|
||
(ppc64_elfv2_abi_homogeneous_aggregate): Likewise.
|
||
(ppc64_sysv_abi_push_param): Handle ELFv2 homogeneous structs.
|
||
(ppc64_sysv_abi_return_value): Likewise. Also, handle small
|
||
structures returned in GPRs.
|
||
|
||
### a/gdb/ChangeLog
|
||
### b/gdb/ChangeLog
|
||
## -1,5 +1,13 @@
|
||
2014-02-04 Ulrich Weigand <uweigand@de.ibm.com>
|
||
|
||
+ * ppc-sysv-tdep.c (ppc64_aggregate_candidate): New routine.
|
||
+ (ppc64_elfv2_abi_homogeneous_aggregate): Likewise.
|
||
+ (ppc64_sysv_abi_push_param): Handle ELFv2 homogeneous structs.
|
||
+ (ppc64_sysv_abi_return_value): Likewise. Also, handle small
|
||
+ structures returned in GPRs.
|
||
+
|
||
+2014-02-04 Ulrich Weigand <uweigand@de.ibm.com>
|
||
+
|
||
* ppc-sysv-tdep.c (ppc64_sysv_abi_push_dummy_call): Use correct
|
||
offset to the stack parameter list for the ELFv2 ABI.
|
||
|
||
--- a/gdb/ppc-sysv-tdep.c
|
||
+++ b/gdb/ppc-sysv-tdep.c
|
||
@@ -1101,6 +1101,160 @@ convert_code_addr_to_desc_addr (CORE_ADDR code_addr, CORE_ADDR *desc_addr)
|
||
return 1;
|
||
}
|
||
|
||
+/* Walk down the type tree of TYPE counting consecutive base elements.
|
||
+ If *FIELD_TYPE is NULL, then set it to the first valid floating point
|
||
+ or vector type. If a non-floating point or vector type is found, or
|
||
+ if a floating point or vector type that doesn't match a non-NULL
|
||
+ *FIELD_TYPE is found, then return -1, otherwise return the count in the
|
||
+ sub-tree. */
|
||
+
|
||
+static LONGEST
|
||
+ppc64_aggregate_candidate (struct type *type,
|
||
+ struct type **field_type)
|
||
+{
|
||
+ type = check_typedef (type);
|
||
+
|
||
+ switch (TYPE_CODE (type))
|
||
+ {
|
||
+ case TYPE_CODE_FLT:
|
||
+ case TYPE_CODE_DECFLOAT:
|
||
+ if (!*field_type)
|
||
+ *field_type = type;
|
||
+ if (TYPE_CODE (*field_type) == TYPE_CODE (type)
|
||
+ && TYPE_LENGTH (*field_type) == TYPE_LENGTH (type))
|
||
+ return 1;
|
||
+ break;
|
||
+
|
||
+ case TYPE_CODE_COMPLEX:
|
||
+ type = TYPE_TARGET_TYPE (type);
|
||
+ if (TYPE_CODE (type) == TYPE_CODE_FLT
|
||
+ || TYPE_CODE (type) == TYPE_CODE_DECFLOAT)
|
||
+ {
|
||
+ if (!*field_type)
|
||
+ *field_type = type;
|
||
+ if (TYPE_CODE (*field_type) == TYPE_CODE (type)
|
||
+ && TYPE_LENGTH (*field_type) == TYPE_LENGTH (type))
|
||
+ return 2;
|
||
+ }
|
||
+ break;
|
||
+
|
||
+ case TYPE_CODE_ARRAY:
|
||
+ if (TYPE_VECTOR (type))
|
||
+ {
|
||
+ if (!*field_type)
|
||
+ *field_type = type;
|
||
+ if (TYPE_CODE (*field_type) == TYPE_CODE (type)
|
||
+ && TYPE_LENGTH (*field_type) == TYPE_LENGTH (type))
|
||
+ return 1;
|
||
+ }
|
||
+ else
|
||
+ {
|
||
+ LONGEST count, low_bound, high_bound;
|
||
+
|
||
+ count = ppc64_aggregate_candidate
|
||
+ (TYPE_TARGET_TYPE (type), field_type);
|
||
+ if (count == -1)
|
||
+ return -1;
|
||
+
|
||
+ if (!get_array_bounds (type, &low_bound, &high_bound))
|
||
+ return -1;
|
||
+ count *= high_bound - low_bound;
|
||
+
|
||
+ /* There must be no padding. */
|
||
+ if (count == 0)
|
||
+ return TYPE_LENGTH (type) == 0 ? 0 : -1;
|
||
+ else if (TYPE_LENGTH (type) != count * TYPE_LENGTH (*field_type))
|
||
+ return -1;
|
||
+
|
||
+ return count;
|
||
+ }
|
||
+ break;
|
||
+
|
||
+ case TYPE_CODE_STRUCT:
|
||
+ case TYPE_CODE_UNION:
|
||
+ {
|
||
+ LONGEST count = 0;
|
||
+ int i;
|
||
+
|
||
+ for (i = 0; i < TYPE_NFIELDS (type); i++)
|
||
+ {
|
||
+ LONGEST sub_count;
|
||
+
|
||
+ if (field_is_static (&TYPE_FIELD (type, i)))
|
||
+ continue;
|
||
+
|
||
+ sub_count = ppc64_aggregate_candidate
|
||
+ (TYPE_FIELD_TYPE (type, i), field_type);
|
||
+ if (sub_count == -1)
|
||
+ return -1;
|
||
+
|
||
+ if (TYPE_CODE (type) == TYPE_CODE_STRUCT)
|
||
+ count += sub_count;
|
||
+ else
|
||
+ count = max (count, sub_count);
|
||
+ }
|
||
+
|
||
+ /* There must be no padding. */
|
||
+ if (count == 0)
|
||
+ return TYPE_LENGTH (type) == 0 ? 0 : -1;
|
||
+ else if (TYPE_LENGTH (type) != count * TYPE_LENGTH (*field_type))
|
||
+ return -1;
|
||
+
|
||
+ return count;
|
||
+ }
|
||
+ break;
|
||
+
|
||
+ default:
|
||
+ break;
|
||
+ }
|
||
+
|
||
+ return -1;
|
||
+}
|
||
+
|
||
+/* If an argument of type TYPE is a homogeneous float or vector aggregate
|
||
+ that shall be passed in FP/vector registers according to the ELFv2 ABI,
|
||
+ return the homogeneous element type in *ELT_TYPE and the number of
|
||
+ elements in *N_ELTS, and return non-zero. Otherwise, return zero. */
|
||
+
|
||
+static int
|
||
+ppc64_elfv2_abi_homogeneous_aggregate (struct type *type,
|
||
+ struct type **elt_type, int *n_elts)
|
||
+{
|
||
+ /* Complex types at the top level are treated separately. However,
|
||
+ complex types can be elements of homogeneous aggregates. */
|
||
+ if (TYPE_CODE (type) == TYPE_CODE_STRUCT
|
||
+ || TYPE_CODE (type) == TYPE_CODE_UNION
|
||
+ || (TYPE_CODE (type) == TYPE_CODE_ARRAY && !TYPE_VECTOR (type)))
|
||
+ {
|
||
+ struct type *field_type = NULL;
|
||
+ LONGEST field_count = ppc64_aggregate_candidate (type, &field_type);
|
||
+
|
||
+ if (field_count > 0)
|
||
+ {
|
||
+ int n_regs = ((TYPE_CODE (field_type) == TYPE_CODE_FLT
|
||
+ || TYPE_CODE (field_type) == TYPE_CODE_DECFLOAT)?
|
||
+ (TYPE_LENGTH (field_type) + 7) >> 3 : 1);
|
||
+
|
||
+ /* The ELFv2 ABI allows homogeneous aggregates to occupy
|
||
+ up to 8 registers. */
|
||
+ if (field_count * n_regs <= 8)
|
||
+ {
|
||
+ if (elt_type)
|
||
+ *elt_type = field_type;
|
||
+ if (n_elts)
|
||
+ *n_elts = (int) field_count;
|
||
+ /* Note that field_count is LONGEST since it may hold the size
|
||
+ of an array, while *n_elts is int since its value is bounded
|
||
+ by the number of registers used for argument passing. The
|
||
+ cast cannot overflow due to the bounds checking above. */
|
||
+ return 1;
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+
|
||
/* Structure holding the next argument position. */
|
||
struct ppc64_sysv_argpos
|
||
{
|
||
@@ -1389,6 +1543,29 @@ ppc64_sysv_abi_push_param (struct gdbarch *gdbarch,
|
||
if (TYPE_CODE (type) == TYPE_CODE_FLT)
|
||
ppc64_sysv_abi_push_freg (gdbarch, type, val, argpos);
|
||
}
|
||
+
|
||
+ /* In the ELFv2 ABI, homogeneous floating-point or vector
|
||
+ aggregates are passed in a series of registers. */
|
||
+ if (tdep->elf_abi == POWERPC_ELF_V2)
|
||
+ {
|
||
+ struct type *eltype;
|
||
+ int i, nelt;
|
||
+
|
||
+ if (ppc64_elfv2_abi_homogeneous_aggregate (type, &eltype, &nelt))
|
||
+ for (i = 0; i < nelt; i++)
|
||
+ {
|
||
+ const gdb_byte *elval = val + i * TYPE_LENGTH (eltype);
|
||
+
|
||
+ if (TYPE_CODE (eltype) == TYPE_CODE_FLT
|
||
+ || TYPE_CODE (eltype) == TYPE_CODE_DECFLOAT)
|
||
+ ppc64_sysv_abi_push_freg (gdbarch, eltype, elval, argpos);
|
||
+ else if (TYPE_CODE (eltype) == TYPE_CODE_ARRAY
|
||
+ && TYPE_VECTOR (eltype)
|
||
+ && tdep->vector_abi == POWERPC_VEC_ALTIVEC
|
||
+ && TYPE_LENGTH (eltype) == 16)
|
||
+ ppc64_sysv_abi_push_vreg (gdbarch, elval, argpos);
|
||
+ }
|
||
+ }
|
||
}
|
||
}
|
||
|
||
@@ -1834,6 +2011,72 @@ ppc64_sysv_abi_return_value (struct gdbarch *gdbarch, struct value *function,
|
||
return RETURN_VALUE_REGISTER_CONVENTION;
|
||
}
|
||
|
||
+ /* In the ELFv2 ABI, homogeneous floating-point or vector
|
||
+ aggregates are returned in registers. */
|
||
+ if (tdep->elf_abi == POWERPC_ELF_V2
|
||
+ && ppc64_elfv2_abi_homogeneous_aggregate (valtype, &eltype, &nelt))
|
||
+ {
|
||
+ for (i = 0; i < nelt; i++)
|
||
+ {
|
||
+ ok = ppc64_sysv_abi_return_value_base (gdbarch, eltype, regcache,
|
||
+ readbuf, writebuf, i);
|
||
+ gdb_assert (ok);
|
||
+
|
||
+ if (readbuf)
|
||
+ readbuf += TYPE_LENGTH (eltype);
|
||
+ if (writebuf)
|
||
+ writebuf += TYPE_LENGTH (eltype);
|
||
+ }
|
||
+
|
||
+ return RETURN_VALUE_REGISTER_CONVENTION;
|
||
+ }
|
||
+
|
||
+ /* In the ELFv2 ABI, aggregate types of up to 16 bytes are
|
||
+ returned in registers r3:r4. */
|
||
+ if (tdep->elf_abi == POWERPC_ELF_V2
|
||
+ && TYPE_LENGTH (valtype) <= 16
|
||
+ && (TYPE_CODE (valtype) == TYPE_CODE_STRUCT
|
||
+ || TYPE_CODE (valtype) == TYPE_CODE_UNION
|
||
+ || (TYPE_CODE (valtype) == TYPE_CODE_ARRAY
|
||
+ && !TYPE_VECTOR (valtype))))
|
||
+ {
|
||
+ int n_regs = ((TYPE_LENGTH (valtype) + tdep->wordsize - 1)
|
||
+ / tdep->wordsize);
|
||
+ int i;
|
||
+
|
||
+ for (i = 0; i < n_regs; i++)
|
||
+ {
|
||
+ gdb_byte regval[MAX_REGISTER_SIZE];
|
||
+ int regnum = tdep->ppc_gp0_regnum + 3 + i;
|
||
+ int offset = i * tdep->wordsize;
|
||
+ int len = TYPE_LENGTH (valtype) - offset;
|
||
+
|
||
+ if (len > tdep->wordsize)
|
||
+ len = tdep->wordsize;
|
||
+
|
||
+ if (writebuf != NULL)
|
||
+ {
|
||
+ memset (regval, 0, sizeof regval);
|
||
+ if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG
|
||
+ && offset == 0)
|
||
+ memcpy (regval + tdep->wordsize - len, writebuf, len);
|
||
+ else
|
||
+ memcpy (regval, writebuf + offset, len);
|
||
+ regcache_cooked_write (regcache, regnum, regval);
|
||
+ }
|
||
+ if (readbuf != NULL)
|
||
+ {
|
||
+ regcache_cooked_read (regcache, regnum, regval);
|
||
+ if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG
|
||
+ && offset == 0)
|
||
+ memcpy (readbuf, regval + tdep->wordsize - len, len);
|
||
+ else
|
||
+ memcpy (readbuf + offset, regval, len);
|
||
+ }
|
||
+ }
|
||
+ return RETURN_VALUE_REGISTER_CONVENTION;
|
||
+ }
|
||
+
|
||
/* Handle plain base types. */
|
||
if (ppc64_sysv_abi_return_value_base (gdbarch, valtype, regcache,
|
||
readbuf, writebuf, 0))
|