Home | History | Annotate | Download | only in aarch64
      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
     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 *
     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
     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
     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
     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
    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
    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
    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
    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
    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
    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 *
    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
    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
    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
    350 arch_fetch_arg_done(struct fetch_context *context)
    351 {
    352 	if (context != NULL)
    353 		free(context);
    354 }
    355 
    356 size_t
    357 arch_type_sizeof(struct process *proc, struct arg_type_info *arg)
    358 {
    359 	return (size_t) -2;
    360 }
    361 
    362 size_t
    363 arch_type_alignof(struct process *proc, struct arg_type_info *arg)
    364 {
    365 	return (size_t) -2;
    366 }
    367