1/* Function return value location for Linux/x86-64 ABI.
2   Copyright (C) 2005-2010, 2014 Red Hat, Inc.
3   This file is part of elfutils.
4
5   This file is free software; you can redistribute it and/or modify
6   it under the terms of either
7
8     * the GNU Lesser General Public License as published by the Free
9       Software Foundation; either version 3 of the License, or (at
10       your option) any later version
11
12   or
13
14     * the GNU General Public License as published by the Free
15       Software Foundation; either version 2 of the License, or (at
16       your option) any later version
17
18   or both in parallel, as here.
19
20   elfutils is distributed in the hope that it will be useful, but
21   WITHOUT ANY WARRANTY; without even the implied warranty of
22   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23   General Public License for more details.
24
25   You should have received copies of the GNU General Public License and
26   the GNU Lesser General Public License along with this program.  If
27   not, see <http://www.gnu.org/licenses/>.  */
28
29#ifdef HAVE_CONFIG_H
30# include <config.h>
31#endif
32
33#include <assert.h>
34#include <dwarf.h>
35
36#define BACKEND x86_64_
37#include "libebl_CPU.h"
38
39
40/* %rax, or pair %rax, %rdx.  */
41static const Dwarf_Op loc_intreg[] =
42  {
43    { .atom = DW_OP_reg0 }, { .atom = DW_OP_piece, .number = 8 },
44    { .atom = DW_OP_reg1 }, { .atom = DW_OP_piece, .number = 8 },
45  };
46#define nloc_intreg	1
47#define nloc_intregpair	4
48
49/* %st(0), or pair %st(0), %st(1).  */
50static const Dwarf_Op loc_x87reg[] =
51  {
52    { .atom = DW_OP_regx, .number = 33 },
53    { .atom = DW_OP_piece, .number = 10 },
54    { .atom = DW_OP_regx, .number = 34 },
55    { .atom = DW_OP_piece, .number = 10 },
56  };
57#define nloc_x87reg	1
58#define nloc_x87regpair	4
59
60/* %xmm0, or pair %xmm0, %xmm1.  */
61static const Dwarf_Op loc_ssereg[] =
62  {
63    { .atom = DW_OP_reg17 }, { .atom = DW_OP_piece, .number = 16 },
64    { .atom = DW_OP_reg18 }, { .atom = DW_OP_piece, .number = 16 },
65  };
66#define nloc_ssereg	1
67#define nloc_sseregpair	4
68
69/* The return value is a structure and is actually stored in stack space
70   passed in a hidden argument by the caller.  But, the compiler
71   helpfully returns the address of that space in %rax.  */
72static const Dwarf_Op loc_aggregate[] =
73  {
74    { .atom = DW_OP_breg0, .number = 0 }
75  };
76#define nloc_aggregate 1
77
78
79int
80x86_64_return_value_location (Dwarf_Die *functypedie, const Dwarf_Op **locp)
81{
82  /* Start with the function's type, and get the DW_AT_type attribute,
83     which is the type of the return value.  */
84  Dwarf_Die die_mem, *typedie = &die_mem;
85  int tag = dwarf_peeled_die_type (functypedie, typedie);
86  if (tag <= 0)
87    return tag;
88
89  Dwarf_Word size;
90  switch (tag)
91    {
92    case -1:
93      return -1;
94
95    case DW_TAG_subrange_type:
96      if (! dwarf_hasattr_integrate (typedie, DW_AT_byte_size))
97	{
98	  Dwarf_Attribute attr_mem, *attr;
99	  attr = dwarf_attr_integrate (typedie, DW_AT_type, &attr_mem);
100	  typedie = dwarf_formref_die (attr, &die_mem);
101	  tag = DWARF_TAG_OR_RETURN (typedie);
102	}
103      /* Fall through.  */
104
105    case DW_TAG_base_type:
106    case DW_TAG_enumeration_type:
107    case DW_TAG_pointer_type:
108    case DW_TAG_ptr_to_member_type:
109      {
110	Dwarf_Attribute attr_mem;
111	if (dwarf_formudata (dwarf_attr_integrate (typedie, DW_AT_byte_size,
112						   &attr_mem), &size) != 0)
113	  {
114	    if (tag == DW_TAG_pointer_type || tag == DW_TAG_ptr_to_member_type)
115	      size = 8;
116	    else
117	      return -1;
118	  }
119      }
120
121      if (tag == DW_TAG_base_type)
122	{
123	  Dwarf_Attribute attr_mem;
124	  Dwarf_Word encoding;
125	  if (dwarf_formudata (dwarf_attr_integrate (typedie, DW_AT_encoding,
126						     &attr_mem),
127			       &encoding) != 0)
128	    return -1;
129
130	  switch (encoding)
131	    {
132	    case DW_ATE_complex_float:
133	      switch (size)
134		{
135		case 4 * 2:	/* complex float */
136		case 8 * 2:	/* complex double */
137		  *locp = loc_ssereg;
138		  return nloc_sseregpair;
139		case 16 * 2:	/* complex long double */
140		  *locp = loc_x87reg;
141		  return nloc_x87regpair;
142		}
143	      return -2;
144
145	    case DW_ATE_float:
146	      switch (size)
147		{
148		case 4:	/* float */
149		case 8:	/* double */
150		  *locp = loc_ssereg;
151		  return nloc_ssereg;
152		case 16:	/* long double */
153		  /* XXX distinguish __float128, which is sseregpair?? */
154		  *locp = loc_x87reg;
155		  return nloc_x87reg;
156		}
157	      return -2;
158	    }
159	}
160
161    intreg:
162      *locp = loc_intreg;
163      if (size <= 8)
164	return nloc_intreg;
165      if (size <= 16)
166	return nloc_intregpair;
167
168    large:
169      *locp = loc_aggregate;
170      return nloc_aggregate;
171
172    case DW_TAG_structure_type:
173    case DW_TAG_class_type:
174    case DW_TAG_union_type:
175    case DW_TAG_array_type:
176      if (dwarf_aggregate_size (typedie, &size) != 0)
177	goto large;
178      if (size > 16)
179	goto large;
180
181      /* XXX
182	 Must examine the fields in picayune ways to determine the
183	 actual answer.  This will be right for small C structs
184	 containing integer types and similarly simple cases.
185      */
186
187      goto intreg;
188    }
189
190  /* XXX We don't have a good way to return specific errors from ebl calls.
191     This value means we do not understand the type, but it is well-formed
192     DWARF and might be valid.  */
193  return -2;
194}
195