/*
* This file is part of ltrace.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <sys/types.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <sys/procfs.h>
#include <sys/reg.h>
#include "backend.h"
#include "expr.h"
#include "fetch.h"
#include "proc.h"
#include "ptrace.h"
#include "type.h"
#include "value.h"
struct fetch_context
{
elf_gregset_t regs;
elf_fpregset_t fpregs;
int arg_num;
arch_addr_t stack_pointer;
struct value retval;
};
static int
fetch_register_banks(struct process *proc, struct fetch_context *context,
int floating)
{
if (ptrace(PTRACE_GETREGS, proc->pid, 0, &context->regs) < 0)
return -1;
if (floating
&& ptrace(PTRACE_GETFPREGS, proc->pid, 0, &context->fpregs) < 0)
return -1;
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)
return NULL;
assert(type != LT_TOF_FUNCTIONR && type != LT_TOF_SYSCALLR);
if (fetch_register_banks(proc, context, type == LT_TOF_FUNCTION) < 0) {
fail:
free(context);
return NULL;
}
context->arg_num = 0;
context->stack_pointer = (arch_addr_t)context->regs[PT_USP] + 4;
size_t sz = type_sizeof(proc, ret_info);
if (sz == (size_t)-1)
goto fail;
if (ret_info->type == ARGTYPE_STRUCT && !(sz <= 4 || sz == 8)) {
value_init(&context->retval, proc, NULL, ret_info, 0);
if (value_pass_by_reference(&context->retval) < 0)
goto fail;
value_set_word(&context->retval, context->regs[PT_A1]);
} else {
value_init_detached(&context->retval, NULL, NULL, 0);
}
return context;
}
struct fetch_context *
arch_fetch_arg_clone(struct process *proc, struct fetch_context *context)
{
struct fetch_context *ret = malloc(sizeof(*ret));
if (ret == NULL)
return NULL;
*ret = *context;
return ret;
}
int
arch_fetch_arg_next(struct fetch_context *context, enum tof type,
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;
if (type == LT_TOF_SYSCALL) {
int reg;
switch (context->arg_num++) {
case 0: reg = PT_D1; break;
case 1: reg = PT_D2; break;
case 2: reg = PT_D3; break;
case 3: reg = PT_D4; break;
case 4: reg = PT_D5; break;
case 5: reg = PT_A0; break;
default:
assert(!"More than six syscall arguments???");
abort();
}
value_set_word(valuep, context->regs[reg]);
} else {
size_t a = type_alignof(valuep->inferior, valuep->type);
if (a < 4)
a = 4;
context->stack_pointer = (arch_addr_t)
align((unsigned long)context->stack_pointer, a);
if (sz < 4)
context->stack_pointer += 4 - sz;
value_in_inferior(valuep, context->stack_pointer);
context->stack_pointer += sz;
}
return 0;
}
int
arch_fetch_retval(struct fetch_context *context, enum tof type,
struct process *proc, struct arg_type_info *info,
struct value *valuep)
{
if (fetch_register_banks(proc, context, type == LT_TOF_FUNCTIONR) < 0)
return -1;
if (context->retval.type != NULL) {
/* Struct return value was extracted when in fetch
* init. */
*valuep = context->retval;
return 0;
}
size_t sz = type_sizeof(proc, info);
if (sz == (size_t)-1)
return -1;
if (value_reserve(valuep, sz) == NULL)
return -1;
switch (info->type) {
case ARGTYPE_VOID:
return 0;
case ARGTYPE_INT:
case ARGTYPE_UINT:
case ARGTYPE_LONG:
case ARGTYPE_ULONG:
case ARGTYPE_CHAR:
case ARGTYPE_SHORT:
case ARGTYPE_USHORT:
case ARGTYPE_POINTER:
{
unsigned char *buf = value_get_raw_data(valuep);
int reg = info->type == ARGTYPE_POINTER ? PT_A0 : PT_D0;
unsigned char *val
= (unsigned char *)&context->regs[reg];
if (sz < 4) val += 4 - sz;
memcpy(buf, val, sz);
}
return 0;
case ARGTYPE_FLOAT:
case ARGTYPE_DOUBLE:
{
union {
long double ld;
double d;
float f;
char buf[0];
} u;
unsigned long *reg = &context->fpregs.fpregs[0];
memcpy (&u.ld, reg, sizeof (u.ld));
if (valuep->type->type == ARGTYPE_FLOAT)
u.f = (float)u.ld;
else if (valuep->type->type == ARGTYPE_DOUBLE)
u.d = (double)u.ld;
else {
assert(!"Unexpected floating type!");
abort();
}
unsigned char *buf = value_get_raw_data (valuep);
memcpy (buf, u.buf, sz);
}
return 0;
case ARGTYPE_STRUCT:
{
unsigned char *buf = value_get_raw_data(valuep);
unsigned char *val
= (unsigned char *)&context->regs[PT_D0];
assert(sz <= 4 || sz == 8);
if (sz < 4) val += 4 - sz;
memcpy(buf, val, sz <= 4 ? sz : 4);
if (sz == 8)
memcpy(buf + 4, &context->regs[PT_D1], 4);
}
return 0;
default:
assert(!"Unexpected m68k retval type!");
abort();
}
abort();
}
void
arch_fetch_arg_done(struct fetch_context *context)
{
if (context != NULL)
free(context);
}