Home | History | Annotate | Download | only in arm
      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
     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 *
     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 *
    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
    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
    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
    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
    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
    325 arch_fetch_arg_done(struct fetch_context *context)
    326 {
    327 	free(context);
    328 }
    329 
    330 int
    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
    340 arch_fetch_param_pack_end(struct fetch_context *context)
    341 {
    342 	context->in_varargs = false;
    343 }
    344