/*
* 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 <stdlib.h>
#include <assert.h>
#include <sys/rse.h>
#include <ptrace.h>
#include <string.h>
#include <errno.h>
#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;
}