• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
32 int aarch64_read_gregs(struct process *proc, struct user_pt_regs *regs);
33 int aarch64_read_fregs(struct process *proc, struct user_fpsimd_state *regs);
34 
35 
36 struct 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 
46 static int
context_init(struct fetch_context * context,struct process * proc)47 context_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 
62 struct fetch_context *
arch_fetch_arg_clone(struct process * proc,struct fetch_context * context)63 arch_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 
71 static void
fetch_next_gpr(struct fetch_context * context,unsigned char * buf)72 fetch_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 
78 static int
fetch_gpr(struct fetch_context * context,struct value * value,size_t sz)79 fetch_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 
95 static void
fetch_next_sse(struct fetch_context * context,unsigned char * buf,size_t sz)96 fetch_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 
102 static int
fetch_sse(struct fetch_context * context,struct value * value,size_t sz)103 fetch_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 
113 static int
fetch_hfa(struct fetch_context * context,struct value * value,struct arg_type_info * hfa_t,size_t count)114 fetch_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 
130 static int
fetch_stack(struct fetch_context * context,struct value * value,size_t align,size_t sz)131 fetch_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 
150 enum convert_method {
151 	CVT_ERR = -1,
152 	CVT_NOP = 0,
153 	CVT_BYREF,
154 };
155 
156 enum fetch_method {
157 	FETCH_NOP,
158 	FETCH_STACK,
159 	FETCH_GPR,
160 	FETCH_SSE,
161 	FETCH_HFA,
162 };
163 
164 struct 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 
172 static struct fetch_script
pass_arg(struct fetch_context const * context,struct process * proc,struct arg_type_info * info)173 pass_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 
240 static int
convert_arg(struct value * value,struct fetch_script how)241 convert_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 
256 static int
fetch_arg(struct fetch_context * context,struct process * proc,struct arg_type_info * info,struct value * value,struct fetch_script how)257 fetch_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 
288 struct fetch_context *
arch_fetch_arg_init(enum tof type,struct process * proc,struct arg_type_info * ret_info)289 arch_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 
323 int
arch_fetch_arg_next(struct fetch_context * context,enum tof type,struct process * proc,struct arg_type_info * info,struct value * value)324 arch_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 
332 int
arch_fetch_retval(struct fetch_context * context,enum tof type,struct process * proc,struct arg_type_info * info,struct value * value)333 arch_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 
349 void
arch_fetch_arg_done(struct fetch_context * context)350 arch_fetch_arg_done(struct fetch_context *context)
351 {
352 	if (context != NULL)
353 		free(context);
354 }
355 
356 size_t
arch_type_sizeof(struct process * proc,struct arg_type_info * arg)357 arch_type_sizeof(struct process *proc, struct arg_type_info *arg)
358 {
359 	return (size_t) -2;
360 }
361 
362 size_t
arch_type_alignof(struct process * proc,struct arg_type_info * arg)363 arch_type_alignof(struct process *proc, struct arg_type_info *arg)
364 {
365 	return (size_t) -2;
366 }
367