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