/* * This file is part of ltrace. * Copyright (C) 2012,2013 Petr Machata, Red Hat Inc. * Copyright (C) 2008,2009 Juan Cespedes * Copyright (C) 2006 Steve Fink * Copyright (C) 2006 Ian Wienand * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ #include #include #include #include #include #include #include "backend.h" #include "fetch.h" #include "type.h" #include "proc.h" #include "value.h" struct fetch_context { arch_addr_t stack_pointer; struct pt_all_user_regs regs; enum param_pack_flavor ppflavor; /* Return values larger than 256 bits (except HFAs of up to 8 * elements) are returned in a buffer allocated by the * caller. A pointer to the buffer is passed to the called * procedure in r8. This register is not guaranteed to be * preserved by the called procedure. */ unsigned long r8; int slot_n; int flt; }; union cfm_t { struct { unsigned long sof:7; unsigned long sol:7; unsigned long sor:4; unsigned long rrb_gr:7; unsigned long rrb_fr:7; unsigned long rrb_pr:6; } cfm; unsigned long value; }; static int fetch_context_init(struct process *proc, struct fetch_context *context) { context->slot_n = 0; context->flt = 8; if (ptrace(PTRACE_GETREGS, proc->pid, 0, &context->regs) < 0) return -1; context->stack_pointer = (void *)(context->regs.gr[12] + 16); context->ppflavor = PARAM_PACK_ARGS; return 0; } struct fetch_context * arch_fetch_arg_init(enum tof type, struct process *proc, struct arg_type_info *ret_info) { struct fetch_context *context = malloc(sizeof(*context)); if (context == NULL || fetch_context_init(proc, context) < 0) { free(context); return NULL; } context->r8 = context->regs.gr[8]; return context; } struct fetch_context * arch_fetch_arg_clone(struct process *proc, struct fetch_context *context) { struct fetch_context *clone = malloc(sizeof(*context)); if (clone == NULL) return NULL; *clone = *context; return clone; } int allocate_stack_slot(struct fetch_context *ctx, struct process *proc, struct arg_type_info *info, struct value *valuep) { size_t al = type_alignof(proc, info); size_t sz = type_sizeof(proc, info); if (al == (size_t)-1 || sz == (size_t)-1) return -1; errno = 0; long value = ptrace(PTRACE_PEEKDATA, proc->pid, ctx->stack_pointer, 0); if (value == -1 && errno != 0) return -1; ctx->stack_pointer += 8; value_set_word(valuep, value); return 0; } static int allocate_reg(struct fetch_context *ctx, struct process *proc, struct arg_type_info *info, struct value *valuep) { if (ctx->slot_n >= 8) return allocate_stack_slot(ctx, proc, info, valuep); int reg_num = ctx->slot_n++; if (ctx->slot_n == 8) ctx->flt = 16; if (valuep == NULL) return 0; /* This would normally be brought over from asm/ptrace.h, but * when we do, we get namespace conflicts between asm/fpu.h * and libunwind. */ enum { PT_AUR_BSP = 17 }; union cfm_t cfm = { .value = ctx->regs.cfm }; unsigned long *bsp = (unsigned long *)ctx->regs.ar[PT_AUR_BSP]; unsigned long idx = -cfm.cfm.sof + reg_num; unsigned long *ptr = ia64_rse_skip_regs(bsp, idx); errno = 0; long ret = ptrace(PTRACE_PEEKDATA, proc->pid, ptr, 0); if (ret == -1 && errno != 0) return -1; value_set_word(valuep, ret); return 0; } static int copy_aggregate_part(struct fetch_context *ctx, struct process *proc, unsigned char *buf, size_t size) { size_t slots = (size + 7) / 8; struct arg_type_info *long_info = type_get_simple(ARGTYPE_LONG); while (slots-- > 0) { size_t chunk_sz = size > 8 ? 8 : size; size -= 8; struct value tmp; value_init(&tmp, proc, NULL, long_info, 0); int rc = allocate_reg(ctx, proc, long_info, &tmp); if (rc >= 0) { memcpy(buf, value_get_data(&tmp, NULL), chunk_sz); buf += 8; } value_destroy(&tmp); if (rc < 0) return -1; } return 0; } static int allocate_arg(struct fetch_context *ctx, struct process *proc, struct arg_type_info *info, struct value *valuep) { size_t sz = type_sizeof(proc, info); size_t align = type_alignof(proc, info); if (sz == (size_t)-1 || align == (size_t)-1) return -1; unsigned char *buf = value_reserve(valuep, sz); if (buf == NULL) return -1; assert(align == 0 || align == 1 || align == 2 || align == 4 || align == 8 || align == 16); /* For aggregates with an external alignment of 16 bytes, the * Next Even policy is used. 128-bit integers use the Next * Even policy as well. */ if (align == 16 && ctx->slot_n % 2 != 0) allocate_reg(ctx, proc, info, NULL); int rc= copy_aggregate_part(ctx, proc, buf, sz); return rc; } /* Stolen from David Mosberger's utrace tool, which he released under the GPL (http://www.gelato.unsw.edu.au/archives/linux-ia64/0104/1405.html) */ static inline double fpreg_to_double (struct ia64_fpreg *fp) { double result; asm ("ldf.fill %0=%1" : "=f"(result) : "m"(*fp)); return result; } static int allocate_float(struct fetch_context *ctx, struct process *proc, struct arg_type_info *info, struct value *valuep, int take_slot) { /* The actual parameter is passed in the next available * floating-point parameter register, if one is * available. Floating-point parameter registers are allocated * as needed from the range f8-f15, starting with f8. */ /* Any register parameters corresponding to a * variable-argument specification are passed in GRs. */ if (ctx->flt > 15 || ctx->ppflavor == PARAM_PACK_VARARGS) /* If all available floating-point parameter registers * have been used, the actual parameter is passed in * the appropriate general register(s). */ return allocate_reg(ctx, proc, info, valuep); union { double d; float f; char buf[0]; } u = { .d = fpreg_to_double(&ctx->regs.fr[ctx->flt++]) }; if (take_slot) allocate_reg(ctx, proc, info, NULL); if (info->type == ARGTYPE_FLOAT) u.f = u.d; else assert(info->type == ARGTYPE_DOUBLE); if (value_reserve(valuep, sizeof(u)) == NULL) return -1; memmove(value_get_raw_data(valuep), u.buf, sizeof(u)); return 0; } static int allocate_hfa(struct fetch_context *ctx, struct process *proc, struct arg_type_info *info, struct value *valuep, enum arg_type hfa_type, size_t hfa_count) { size_t sz = type_sizeof(proc, info); if (sz == (size_t)-1) return -1; /* If an actual parameter is known to correspond to an HFA * formal parameter, each element is passed in the next * available floating-point argument register, until the eight * argument registers are exhausted. The remaining elements of * the aggregate are passed in output GRs, according to the * normal conventions. * * Because HFAs are mapped to parameter slots as aggregates, * single-precision HFAs will be allocated with two * floating-point values in each parameter slot, but only one * value per register. * * It is possible for the first of two values in a parameter * slot to occupy the last available floating- point parameter * register. In this case, the second value is passed in its * designated GR, but the half of the GR that would have * contained the first value is undefined. */ size_t slot_off = 0; unsigned char *buf = value_reserve(valuep, sz); if (buf == NULL) return -1; struct arg_type_info *hfa_info = type_get_simple(hfa_type); size_t hfa_sz = type_sizeof(proc, hfa_info); /* Pass in register the part that we can. */ while (ctx->flt <= 15 && hfa_count > 0) { struct value tmp; value_init(&tmp, proc, NULL, hfa_info, 0); int rc = allocate_float(ctx, proc, hfa_info, &tmp, 0); if (rc >= 0) { memcpy(buf, value_get_data(&tmp, NULL), hfa_sz); slot_off += hfa_sz; buf += hfa_sz; hfa_count--; /* Scratch each fully used slot. */ while (slot_off >= 8) { if (allocate_reg(ctx, proc, info, NULL) < 0) rc = -1; slot_off -= 8; } } value_destroy(&tmp); if (rc < 0) return -1; } /* If we have half-slot opened (the case where odd * ARGTYPE_FLOAT member fits into the last floating point * register, and the following even member does not), finish * it first. */ struct arg_type_info *long_info = type_get_simple(ARGTYPE_LONG); if (slot_off != 0 && hfa_count > 0) { struct value tmp; value_init(&tmp, proc, NULL, long_info, 0); int rc = allocate_reg(ctx, proc, long_info, &tmp); if (rc >= 0) { unsigned char *data = value_get_data(&tmp, NULL); memcpy(buf, data, 8 - slot_off); buf += 8 - slot_off; hfa_count--; } value_destroy(&tmp); if (rc < 0) { return -1; } } /* The rest is passed in registers and on stack. */ size_t rest = hfa_count * hfa_sz; return copy_aggregate_part(ctx, proc, buf, rest); } static int allocate_ret(struct fetch_context *ctx, struct process *proc, struct arg_type_info *info, struct value *valuep) { size_t sz = type_sizeof(proc, info); if (sz == (size_t)-1) return -1; /* Homogeneous floating-point aggregates [...] are returned in * floating-point registers, provided the array or structure * contains no more than eight individual values. The * elements of the aggregate are placed in successive * floating-point registers, beginning with f8. */ if (info->type == ARGTYPE_STRUCT || info->type == ARGTYPE_ARRAY) { size_t hfa_size; struct arg_type_info *hfa_info = type_get_hfa_type(info, &hfa_size); if (hfa_info != NULL && hfa_size <= 8) return allocate_hfa(ctx, proc, info, valuep, hfa_info->type, hfa_size); } /* Integers and pointers are passed in r8. 128-bit integers * are passed in r8 and r9. Aggregates of up to 256 bits [32 * bytes] are passed in registers r8...r11. */ if (sz <= 32) { unsigned char *buf = value_reserve(valuep, sz); if (buf == NULL) return -1; memcpy(buf, ctx->regs.gr + 8, sz); return 0; } if (value_pass_by_reference(valuep) < 0) return -1; value_set_word(valuep, ctx->r8); return 0; } int arch_fetch_arg_next(struct fetch_context *ctx, enum tof type, struct process *proc, struct arg_type_info *info, struct value *valuep) { switch (info->type) { struct arg_type_info *hfa_info; size_t hfa_size; case ARGTYPE_VOID: value_set_word(valuep, 0); return 0; case ARGTYPE_FLOAT: case ARGTYPE_DOUBLE: return allocate_float(ctx, proc, info, valuep, 1); case ARGTYPE_STRUCT: hfa_info = type_get_hfa_type(info, &hfa_size); if (hfa_info != NULL) return allocate_hfa(ctx, proc, info, valuep, hfa_info->type, hfa_size); /* Fall through. */ case ARGTYPE_CHAR: case ARGTYPE_SHORT: case ARGTYPE_USHORT: case ARGTYPE_INT: case ARGTYPE_UINT: case ARGTYPE_LONG: case ARGTYPE_ULONG: case ARGTYPE_POINTER: return allocate_arg(ctx, proc, info, valuep); case ARGTYPE_ARRAY: /* Arrays decay into pointers. XXX Fortran? */ default: assert(info->type != info->type); abort(); } } int arch_fetch_retval(struct fetch_context *ctx, enum tof type, struct process *proc, struct arg_type_info *info, struct value *valuep) { if (fetch_context_init(proc, ctx) < 0) return -1; switch (info->type) { case ARGTYPE_VOID: case ARGTYPE_FLOAT: case ARGTYPE_DOUBLE: /* The rules for returning those types are the same as * for passing them in arguments. */ return arch_fetch_arg_next(ctx, type, proc, info, valuep); case ARGTYPE_CHAR: case ARGTYPE_SHORT: case ARGTYPE_USHORT: case ARGTYPE_INT: case ARGTYPE_UINT: case ARGTYPE_LONG: case ARGTYPE_ULONG: case ARGTYPE_POINTER: case ARGTYPE_STRUCT: return allocate_ret(ctx, proc, info, valuep); case ARGTYPE_ARRAY: /* Arrays decay into pointers. XXX Fortran? */ assert(info->type != ARGTYPE_ARRAY); abort(); } assert("unhandled type"); abort(); return arch_fetch_arg_next(ctx, type, proc, info, valuep); } void arch_fetch_arg_done(struct fetch_context *context) { free(context); } int arch_fetch_param_pack_start(struct fetch_context *context, enum param_pack_flavor ppflavor) { context->ppflavor = ppflavor; return 0; } void arch_fetch_param_pack_end(struct fetch_context *context) { context->ppflavor = PARAM_PACK_ARGS; }