1/*
2 * This file is part of ltrace.
3 * Copyright (C) 2014 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 <stdlib.h>
24#include <string.h>
25#include <stdint.h>
26
27#include "fetch.h"
28#include "proc.h"
29#include "type.h"
30#include "value.h"
31
32int aarch64_read_gregs(struct process *proc, struct user_pt_regs *regs);
33int aarch64_read_fregs(struct process *proc, struct user_fpsimd_state *regs);
34
35
36struct fetch_context
37{
38	struct user_pt_regs gregs;
39	struct user_fpsimd_state fpregs;
40	arch_addr_t nsaa;
41	unsigned ngrn;
42	unsigned nsrn;
43	arch_addr_t x8;
44};
45
46static int
47context_init(struct fetch_context *context, struct process *proc)
48{
49	if (aarch64_read_gregs(proc, &context->gregs) < 0
50	    || aarch64_read_fregs(proc, &context->fpregs) < 0)
51		return -1;
52
53	context->ngrn = 0;
54	context->nsrn = 0;
55	/* XXX double cast */
56	context->nsaa = (arch_addr_t) (uintptr_t) context->gregs.sp;
57	context->x8 = 0;
58
59	return 0;
60}
61
62struct fetch_context *
63arch_fetch_arg_clone(struct process *proc, struct fetch_context *context)
64{
65	struct fetch_context *ret = malloc(sizeof(*ret));
66	if (ret == NULL)
67		return NULL;
68	return memcpy(ret, context, sizeof(*ret));
69}
70
71static void
72fetch_next_gpr(struct fetch_context *context, unsigned char *buf)
73{
74	uint64_t u = context->gregs.regs[context->ngrn++];
75	memcpy(buf, &u, 8);
76}
77
78static int
79fetch_gpr(struct fetch_context *context, struct value *value, size_t sz)
80{
81	if (sz < 8)
82		sz = 8;
83
84	unsigned char *buf = value_reserve(value, sz);
85	if (buf == NULL)
86		return -1;
87
88	size_t i;
89	for (i = 0; i < sz; i += 8)
90		fetch_next_gpr(context, buf + i);
91
92	return 0;
93}
94
95static void
96fetch_next_sse(struct fetch_context *context, unsigned char *buf, size_t sz)
97{
98	__int128 u = context->fpregs.vregs[context->nsrn++];
99	memcpy(buf, &u, sz);
100}
101
102static int
103fetch_sse(struct fetch_context *context, struct value *value, size_t sz)
104{
105	unsigned char *buf = value_reserve(value, sz);
106	if (buf == NULL)
107		return -1;
108
109	fetch_next_sse(context, buf, sz);
110	return 0;
111}
112
113static int
114fetch_hfa(struct fetch_context *context,
115	  struct value *value, struct arg_type_info *hfa_t, size_t count)
116{
117	size_t sz = type_sizeof(value->inferior, hfa_t);
118	unsigned char *buf = value_reserve(value, sz * count);
119	if (buf == NULL)
120		return -1;
121
122	size_t i;
123	for (i = 0; i < count; ++i) {
124		fetch_next_sse(context, buf, sz);
125		buf += sz;
126	}
127	return 0;
128}
129
130static int
131fetch_stack(struct fetch_context *context, struct value *value,
132	    size_t align, size_t sz)
133{
134	if (align < 8)
135		align = 8;
136	size_t amount = ((sz + align - 1) / align) * align;
137
138	/* XXX double casts */
139	uintptr_t sp = (uintptr_t) context->nsaa;
140	sp = ((sp + align - 1) / align) * align;
141
142	value_in_inferior(value, (arch_addr_t) sp);
143
144	sp += amount;
145	context->nsaa = (arch_addr_t) sp;
146
147	return 0;
148}
149
150enum convert_method {
151	CVT_ERR = -1,
152	CVT_NOP = 0,
153	CVT_BYREF,
154};
155
156enum fetch_method {
157	FETCH_NOP,
158	FETCH_STACK,
159	FETCH_GPR,
160	FETCH_SSE,
161	FETCH_HFA,
162};
163
164struct fetch_script {
165	enum convert_method c;
166	enum fetch_method f;
167	size_t sz;
168	struct arg_type_info *hfa_t;
169	size_t count;
170};
171
172static struct fetch_script
173pass_arg(struct fetch_context const *context,
174	 struct process *proc, struct arg_type_info *info)
175{
176	enum fetch_method cvt = CVT_NOP;
177
178	size_t sz = type_sizeof(proc, info);
179	if (sz == (size_t) -1)
180		return (struct fetch_script) { CVT_ERR, FETCH_NOP, sz };
181
182	switch (info->type) {
183	case ARGTYPE_VOID:
184		return (struct fetch_script) { cvt, FETCH_NOP, sz };
185
186	case ARGTYPE_STRUCT:
187	case ARGTYPE_ARRAY:;
188		size_t count;
189		struct arg_type_info *hfa_t = type_get_hfa_type(info, &count);
190		if (hfa_t != NULL && count <= 4) {
191			if (context->nsrn + count <= 8)
192				return (struct fetch_script)
193					{ cvt, FETCH_HFA, sz, hfa_t, count };
194			return (struct fetch_script)
195				{ cvt, FETCH_STACK, sz, hfa_t, count };
196		}
197
198		if (sz <= 16) {
199			size_t count = sz / 8;
200			if (context->ngrn + count <= 8)
201				return (struct fetch_script)
202					{ cvt, FETCH_GPR, sz };
203		}
204
205		cvt = CVT_BYREF;
206		sz = 8;
207		/* Fall through.  */
208
209	case ARGTYPE_POINTER:
210	case ARGTYPE_INT:
211	case ARGTYPE_UINT:
212	case ARGTYPE_LONG:
213	case ARGTYPE_ULONG:
214	case ARGTYPE_CHAR:
215	case ARGTYPE_SHORT:
216	case ARGTYPE_USHORT:
217		if (context->ngrn < 8 && sz <= 8)
218			return (struct fetch_script) { cvt, FETCH_GPR, sz };
219		/* We don't support types wider than 8 bytes as of
220		 * now.  */
221		assert(sz <= 8);
222
223		return (struct fetch_script) { cvt, FETCH_STACK, sz };
224
225	case ARGTYPE_FLOAT:
226	case ARGTYPE_DOUBLE:
227		if (context->nsrn < 8) {
228			/* ltrace doesn't support float128.  */
229			assert(sz <= 8);
230			return (struct fetch_script) { cvt, FETCH_SSE, sz };
231		}
232
233		return (struct fetch_script) { cvt, FETCH_STACK, sz };
234	}
235
236	assert(! "Failed to allocate argument.");
237	abort();
238}
239
240static int
241convert_arg(struct value *value, struct fetch_script how)
242{
243	switch (how.c) {
244	case CVT_NOP:
245		return 0;
246	case CVT_BYREF:
247		return value_pass_by_reference(value);
248	case CVT_ERR:
249		return -1;
250	}
251
252	assert(! "Don't know how to convert argument.");
253	abort();
254}
255
256static int
257fetch_arg(struct fetch_context *context,
258	  struct process *proc, struct arg_type_info *info,
259	  struct value *value, struct fetch_script how)
260{
261	if (convert_arg(value, how) < 0)
262		return -1;
263
264	switch (how.f) {
265	case FETCH_NOP:
266		return 0;
267
268	case FETCH_STACK:
269		if (how.hfa_t != NULL && how.count != 0 && how.count <= 8)
270			context->nsrn = 8;
271		return fetch_stack(context, value,
272				   type_alignof(proc, info), how.sz);
273
274	case FETCH_GPR:
275		return fetch_gpr(context, value, how.sz);
276
277	case FETCH_SSE:
278		return fetch_sse(context, value, how.sz);
279
280	case FETCH_HFA:
281		return fetch_hfa(context, value, how.hfa_t, how.count);
282	}
283
284	assert(! "Don't know how to fetch argument.");
285	abort();
286}
287
288struct fetch_context *
289arch_fetch_arg_init(enum tof type, struct process *proc,
290		    struct arg_type_info *ret_info)
291{
292	struct fetch_context *context = malloc(sizeof *context);
293	if (context == NULL || context_init(context, proc) < 0) {
294	fail:
295		free(context);
296		return NULL;
297	}
298
299	/* There's a provision in ARMv8 parameter passing convention
300	 * for returning types that, if passed as first argument to a
301	 * function, would be passed on stack.  For those types, x8
302	 * contains an address where the return argument should be
303	 * placed.  The callee doesn't need to preserve the value of
304	 * x8, so we need to fetch it now.
305	 *
306	 * To my knowledge, there are currently no types where this
307	 * holds, but the code is here, utterly untested.  */
308
309	struct fetch_script how = pass_arg(context, proc, ret_info);
310	if (how.c == CVT_ERR)
311		goto fail;
312	if (how.c == CVT_NOP && how.f == FETCH_STACK) {
313		/* XXX double cast.  */
314		context->x8 = (arch_addr_t) (uintptr_t) context->gregs.regs[8];
315		/* See the comment above about the assert.  */
316		assert(! "Unexpected: first argument passed on stack.");
317		abort();
318	}
319
320	return context;
321}
322
323int
324arch_fetch_arg_next(struct fetch_context *context, enum tof type,
325		    struct process *proc, struct arg_type_info *info,
326		    struct value *value)
327{
328	return fetch_arg(context, proc, info, value,
329			 pass_arg(context, proc, info));
330}
331
332int
333arch_fetch_retval(struct fetch_context *context, enum tof type,
334		  struct process *proc, struct arg_type_info *info,
335		  struct value *value)
336{
337	if (context->x8 != 0) {
338		value_in_inferior(value, context->x8);
339		return 0;
340	}
341
342	if (context_init(context, proc) < 0)
343		return -1;
344
345	return fetch_arg(context, proc, info, value,
346			 pass_arg(context, proc, info));
347}
348
349void
350arch_fetch_arg_done(struct fetch_context *context)
351{
352	if (context != NULL)
353		free(context);
354}
355
356size_t
357arch_type_sizeof(struct process *proc, struct arg_type_info *arg)
358{
359	return (size_t) -2;
360}
361
362size_t
363arch_type_alignof(struct process *proc, struct arg_type_info *arg)
364{
365	return (size_t) -2;
366}
367