• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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