1 /*
2 * This file is part of ltrace.
3 * Copyright (C) 2013 Petr Machata, Red Hat Inc.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18 * 02110-1301 USA
19 */
20
21 #include <sys/ptrace.h>
22 #include <asm/ptrace.h>
23 #include <assert.h>
24 #include <elf.h>
25 #include <libelf.h>
26 #include <stdint.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <stdbool.h>
31
32 #include "backend.h"
33 #include "fetch.h"
34 #include "library.h"
35 #include "proc.h"
36 #include "ptrace.h"
37 #include "regs.h"
38 #include "type.h"
39 #include "value.h"
40
41 enum {
42 /* How many (double) VFP registers the AAPCS uses for
43 * parameter passing. */
44 NUM_VFP_REGS = 8,
45 };
46
47 struct fetch_context {
48 struct pt_regs regs;
49
50 struct {
51 union {
52 double d[32];
53 float s[64];
54 };
55 uint32_t fpscr;
56 } fpregs;
57
58 /* VFP register allocation. ALLOC.S tracks whether the
59 * corresponding FPREGS.S register is taken, ALLOC.D the same
60 * for FPREGS.D. We only track 8 (16) registers, because
61 * that's what the ABI uses for parameter passing. */
62 union {
63 int16_t d[NUM_VFP_REGS];
64 int8_t s[NUM_VFP_REGS * 2];
65 } alloc;
66
67 unsigned ncrn;
68 arch_addr_t sp;
69 arch_addr_t nsaa;
70 arch_addr_t ret_struct;
71
72 bool hardfp:1;
73 bool in_varargs:1;
74 };
75
76 static int
fetch_register_banks(struct process * proc,struct fetch_context * context)77 fetch_register_banks(struct process *proc, struct fetch_context *context)
78 {
79 if (ptrace(PTRACE_GETREGS, proc->pid, NULL, &context->regs) == -1)
80 return -1;
81
82 if (context->hardfp
83 && ptrace(PTRACE_GETVFPREGS, proc->pid,
84 NULL, &context->fpregs) == -1)
85 return -1;
86
87 context->ncrn = 0;
88 context->nsaa = context->sp = get_stack_pointer(proc);
89 memset(&context->alloc, 0, sizeof(context->alloc));
90
91 return 0;
92 }
93
94 struct fetch_context *
arch_fetch_arg_init(enum tof type,struct process * proc,struct arg_type_info * ret_info)95 arch_fetch_arg_init(enum tof type, struct process *proc,
96 struct arg_type_info *ret_info)
97 {
98 struct fetch_context *context = malloc(sizeof(*context));
99
100 {
101 struct process *mainp = proc;
102 while (mainp->libraries == NULL && mainp->parent != NULL)
103 mainp = mainp->parent;
104 context->hardfp = mainp->libraries->arch.hardfp;
105 }
106
107 if (context == NULL
108 || fetch_register_banks(proc, context) < 0) {
109 free(context);
110 return NULL;
111 }
112
113 if (ret_info->type == ARGTYPE_STRUCT
114 || ret_info->type == ARGTYPE_ARRAY) {
115 size_t sz = type_sizeof(proc, ret_info);
116 assert(sz != (size_t)-1);
117 if (sz > 4) {
118 /* XXX double cast */
119 context->ret_struct
120 = (arch_addr_t)context->regs.uregs[0];
121 context->ncrn++;
122 }
123 }
124
125 return context;
126 }
127
128 struct fetch_context *
arch_fetch_arg_clone(struct process * proc,struct fetch_context * context)129 arch_fetch_arg_clone(struct process *proc,
130 struct fetch_context *context)
131 {
132 struct fetch_context *clone = malloc(sizeof(*context));
133 if (clone == NULL)
134 return NULL;
135 *clone = *context;
136 return clone;
137 }
138
139 /* 0 is success, 1 is failure, negative value is an error. */
140 static int
pass_in_vfp(struct fetch_context * ctx,struct process * proc,enum arg_type type,size_t count,struct value * valuep)141 pass_in_vfp(struct fetch_context *ctx, struct process *proc,
142 enum arg_type type, size_t count, struct value *valuep)
143 {
144 assert(type == ARGTYPE_FLOAT || type == ARGTYPE_DOUBLE);
145 unsigned max = type == ARGTYPE_DOUBLE ? NUM_VFP_REGS : 2 * NUM_VFP_REGS;
146 if (count > max)
147 return 1;
148
149 size_t i;
150 size_t j;
151 for (i = 0; i < max; ++i) {
152 for (j = i; j < i + count; ++j)
153 if ((type == ARGTYPE_DOUBLE && ctx->alloc.d[j] != 0)
154 || (type == ARGTYPE_FLOAT && ctx->alloc.s[j] != 0))
155 goto next;
156
157 /* Found COUNT consecutive unallocated registers at I. */
158 const size_t sz = (type == ARGTYPE_FLOAT ? 4 : 8) * count;
159 unsigned char *data = value_reserve(valuep, sz);
160 if (data == NULL)
161 return -1;
162
163 for (j = i; j < i + count; ++j)
164 if (type == ARGTYPE_DOUBLE)
165 ctx->alloc.d[j] = -1;
166 else
167 ctx->alloc.s[j] = -1;
168
169 if (type == ARGTYPE_DOUBLE)
170 memcpy(data, ctx->fpregs.d + i, sz);
171 else
172 memcpy(data, ctx->fpregs.s + i, sz);
173
174 return 0;
175
176 next:
177 continue;
178 }
179 return 1;
180 }
181
182 /* 0 is success, 1 is failure, negative value is an error. */
183 static int
consider_vfp(struct fetch_context * ctx,struct process * proc,struct arg_type_info * info,struct value * valuep)184 consider_vfp(struct fetch_context *ctx, struct process *proc,
185 struct arg_type_info *info, struct value *valuep)
186 {
187 struct arg_type_info *float_info = NULL;
188 size_t hfa_size = 1;
189 if (info->type == ARGTYPE_FLOAT || info->type == ARGTYPE_DOUBLE)
190 float_info = info;
191 else
192 float_info = type_get_hfa_type(info, &hfa_size);
193
194 if (float_info != NULL && hfa_size <= 4)
195 return pass_in_vfp(ctx, proc, float_info->type,
196 hfa_size, valuep);
197 return 1;
198 }
199
200 int
arch_fetch_arg_next(struct fetch_context * ctx,enum tof type,struct process * proc,struct arg_type_info * info,struct value * valuep)201 arch_fetch_arg_next(struct fetch_context *ctx, enum tof type,
202 struct process *proc,
203 struct arg_type_info *info, struct value *valuep)
204 {
205 const size_t sz = type_sizeof(proc, info);
206 assert(sz != (size_t)-1);
207
208 if (ctx->hardfp && !ctx->in_varargs) {
209 int rc;
210 if ((rc = consider_vfp(ctx, proc, info, valuep)) != 1)
211 return rc;
212 }
213
214 /* IHI0042E_aapcs: If the argument requires double-word
215 * alignment (8-byte), the NCRN is rounded up to the next even
216 * register number. */
217 const size_t al = type_alignof(proc, info);
218 assert(al != (size_t)-1);
219 if (al == 8)
220 ctx->ncrn = ((ctx->ncrn + 1) / 2) * 2;
221
222 /* If the size in words of the argument is not more than r4
223 * minus NCRN, the argument is copied into core registers,
224 * starting at the NCRN. */
225 /* If the NCRN is less than r4 and the NSAA is equal to the
226 * SP, the argument is split between core registers and the
227 * stack. */
228
229 const size_t words = (sz + 3) / 4;
230 if (ctx->ncrn < 4 && ctx->nsaa == ctx->sp) {
231 unsigned char *data = value_reserve(valuep, words * 4);
232 if (data == NULL)
233 return -1;
234 size_t i;
235 for (i = 0; i < words && ctx->ncrn < 4; ++i) {
236 memcpy(data, &ctx->regs.uregs[ctx->ncrn++], 4);
237 data += 4;
238 }
239 const size_t rest = (words - i) * 4;
240 if (rest > 0) {
241 umovebytes(proc, ctx->nsaa, data, rest);
242 ctx->nsaa += rest;
243 }
244 return 0;
245 }
246
247 assert(ctx->ncrn == 4);
248
249 /* If the argument required double-word alignment (8-byte),
250 * then the NSAA is rounded up to the next double-word
251 * address. */
252 if (al == 8)
253 /* XXX double cast. */
254 ctx->nsaa = (arch_addr_t)((((uintptr_t)ctx->nsaa + 7) / 8) * 8);
255 else
256 ctx->nsaa = (arch_addr_t)((((uintptr_t)ctx->nsaa + 3) / 4) * 4);
257
258 value_in_inferior(valuep, ctx->nsaa);
259 ctx->nsaa += sz;
260
261 return 0;
262 }
263
264 int
arch_fetch_retval(struct fetch_context * ctx,enum tof type,struct process * proc,struct arg_type_info * info,struct value * valuep)265 arch_fetch_retval(struct fetch_context *ctx, enum tof type,
266 struct process *proc, struct arg_type_info *info,
267 struct value *valuep)
268 {
269 if (fetch_register_banks(proc, ctx) < 0)
270 return -1;
271
272 if (ctx->hardfp && !ctx->in_varargs) {
273 int rc;
274 if ((rc = consider_vfp(ctx, proc, info, valuep)) != 1)
275 return rc;
276 }
277
278 size_t sz = type_sizeof(proc, info);
279 assert(sz != (size_t)-1);
280
281 switch (info->type) {
282 unsigned char *data;
283
284 case ARGTYPE_VOID:
285 return 0;
286
287 case ARGTYPE_FLOAT:
288 case ARGTYPE_DOUBLE:
289 if (ctx->hardfp && !ctx->in_varargs) {
290 unsigned char *data = value_reserve(valuep, sz);
291 if (data == NULL)
292 return -1;
293 memmove(data, &ctx->fpregs, sz);
294 return 0;
295 }
296 goto pass_in_registers;
297
298 case ARGTYPE_ARRAY:
299 case ARGTYPE_STRUCT:
300 if (sz > 4) {
301 value_in_inferior(valuep, ctx->ret_struct);
302 return 0;
303 }
304 /* Fall through. */
305
306 case ARGTYPE_CHAR:
307 case ARGTYPE_SHORT:
308 case ARGTYPE_USHORT:
309 case ARGTYPE_INT:
310 case ARGTYPE_UINT:
311 case ARGTYPE_LONG:
312 case ARGTYPE_ULONG:
313 case ARGTYPE_POINTER:
314 pass_in_registers:
315 if ((data = value_reserve(valuep, sz)) == NULL)
316 return -1;
317 memmove(data, ctx->regs.uregs, sz);
318 return 0;
319 }
320 assert(info->type != info->type);
321 abort();
322 }
323
324 void
arch_fetch_arg_done(struct fetch_context * context)325 arch_fetch_arg_done(struct fetch_context *context)
326 {
327 free(context);
328 }
329
330 int
arch_fetch_param_pack_start(struct fetch_context * context,enum param_pack_flavor ppflavor)331 arch_fetch_param_pack_start(struct fetch_context *context,
332 enum param_pack_flavor ppflavor)
333 {
334 if (ppflavor == PARAM_PACK_VARARGS)
335 context->in_varargs = true;
336 return 0;
337 }
338
339 void
arch_fetch_param_pack_end(struct fetch_context * context)340 arch_fetch_param_pack_end(struct fetch_context *context)
341 {
342 context->in_varargs = false;
343 }
344