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
41enum {
42	/* How many (double) VFP registers the AAPCS uses for
43	 * parameter passing.  */
44	NUM_VFP_REGS = 8,
45};
46
47struct 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
76static int
77fetch_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
94struct fetch_context *
95arch_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
128struct fetch_context *
129arch_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.  */
140static int
141pass_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.  */
183static int
184consider_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
200int
201arch_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
264int
265arch_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
324void
325arch_fetch_arg_done(struct fetch_context *context)
326{
327	free(context);
328}
329
330int
331arch_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
339void
340arch_fetch_param_pack_end(struct fetch_context *context)
341{
342	context->in_varargs = false;
343}
344