296 lines
6.8 KiB
C
296 lines
6.8 KiB
C
/*
|
|
* This file is part of ltrace.
|
|
* Copyright (C) 2012 Petr Machata, Red Hat Inc.
|
|
*
|
|
* 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 <asm/ptrace.h>
|
|
#include <sys/ptrace.h>
|
|
#include <sys/ucontext.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "backend.h"
|
|
#include "fetch.h"
|
|
#include "type.h"
|
|
#include "proc.h"
|
|
#include "value.h"
|
|
|
|
struct fetch_context {
|
|
struct user_regs_struct regs;
|
|
arch_addr_t stack_pointer;
|
|
int greg;
|
|
int freg;
|
|
};
|
|
|
|
static int
|
|
s390x(struct fetch_context *ctx)
|
|
{
|
|
/* +--------+--------+--------+
|
|
* | PSW.31 | PSW.32 | mode |
|
|
* +--------+--------+--------+
|
|
* | 0 | 0 | 24-bit | Not supported in Linux
|
|
* | 0 | 1 | 31-bit | s390 compatible mode
|
|
* | 1 | 1 | 64-bit | z/Architecture, "s390x"
|
|
* +--------+--------+--------+
|
|
* (Note: The leftmost bit is PSW.0, rightmost PSW.63.)
|
|
*/
|
|
|
|
#ifdef __s390x__
|
|
if ((ctx->regs.psw.mask & 0x180000000UL) == 0x180000000UL)
|
|
return 1;
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
fetch_register_banks(struct process *proc, struct fetch_context *ctx,
|
|
bool syscall_enter)
|
|
{
|
|
ptrace_area parea;
|
|
parea.len = sizeof(ctx->regs);
|
|
parea.process_addr = (uintptr_t)&ctx->regs;
|
|
parea.kernel_addr = 0;
|
|
if (ptrace(PTRACE_PEEKUSR_AREA, proc->pid, &parea, NULL) < 0) {
|
|
fprintf(stderr, "fetch_register_banks GPR: %s\n",
|
|
strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
if (syscall_enter)
|
|
ctx->regs.gprs[2] = ctx->regs.orig_gpr2;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
fetch_context_init(struct process *proc, struct fetch_context *context,
|
|
bool syscall_enter)
|
|
{
|
|
context->greg = 2;
|
|
context->freg = 0;
|
|
return fetch_register_banks(proc, context, syscall_enter);
|
|
}
|
|
|
|
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, type == LT_TOF_SYSCALL) < 0) {
|
|
fprintf(stderr, "arch_fetch_arg_init: %s\n",
|
|
strerror(errno));
|
|
free(context);
|
|
return NULL;
|
|
}
|
|
|
|
context->stack_pointer = get_stack_pointer(proc)
|
|
+ (s390x(context) ? 160 : 96);
|
|
if (ret_info->type == ARGTYPE_STRUCT)
|
|
++context->greg;
|
|
|
|
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;
|
|
}
|
|
|
|
static int
|
|
allocate_stack_slot(struct fetch_context *ctx, struct process *proc,
|
|
struct arg_type_info *info, struct value *valuep,
|
|
size_t sz)
|
|
{
|
|
/* Note: here we shouldn't see large composite types, those
|
|
* are passed by reference, which is handled below. Here we
|
|
* only deal with integers, floats, small structs, etc. */
|
|
|
|
size_t a;
|
|
if (s390x(ctx)) {
|
|
assert(sz <= 8);
|
|
a = 8;
|
|
} else {
|
|
/* Note: double is 8 bytes. */
|
|
assert(sz <= 8);
|
|
a = 4;
|
|
}
|
|
|
|
size_t off = sz < a ? a - sz : 0;
|
|
value_in_inferior(valuep, ctx->stack_pointer + off);
|
|
|
|
ctx->stack_pointer += sz > a ? sz : a;
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
copy_gpr(struct fetch_context *ctx, struct value *valuep, int regno)
|
|
{
|
|
value_set_word(valuep, ctx->regs.gprs[regno]);
|
|
}
|
|
|
|
static int
|
|
allocate_gpr(struct fetch_context *ctx, struct process *proc,
|
|
struct arg_type_info *info, struct value *valuep,
|
|
size_t sz)
|
|
{
|
|
if (ctx->greg > 6)
|
|
return allocate_stack_slot(ctx, proc, info, valuep, sz);
|
|
|
|
copy_gpr(ctx, valuep, ctx->greg++);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
allocate_gpr_pair(struct fetch_context *ctx, struct process *proc,
|
|
struct arg_type_info *info, struct value *valuep,
|
|
size_t sz)
|
|
{
|
|
assert(!s390x(ctx));
|
|
assert(sz <= 8);
|
|
|
|
if (ctx->greg > 5) {
|
|
ctx->greg = 7;
|
|
return allocate_stack_slot(ctx, proc, info, valuep, sz);
|
|
}
|
|
|
|
if (value_reserve(valuep, sz) == NULL)
|
|
return -1;
|
|
|
|
unsigned char *ptr = value_get_raw_data(valuep);
|
|
union {
|
|
struct {
|
|
uint32_t a;
|
|
uint32_t b;
|
|
};
|
|
unsigned char buf[8];
|
|
} u;
|
|
u.a = ctx->regs.gprs[ctx->greg++];
|
|
u.b = ctx->regs.gprs[ctx->greg++];
|
|
memcpy(ptr, u.buf, sz);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
allocate_fpr(struct fetch_context *ctx, struct process *proc,
|
|
struct arg_type_info *info, struct value *valuep,
|
|
size_t sz)
|
|
{
|
|
int pool = s390x(ctx) ? 6 : 2;
|
|
|
|
if (ctx->freg > pool)
|
|
return allocate_stack_slot(ctx, proc, info, valuep, sz);
|
|
|
|
if (value_reserve(valuep, sz) == NULL)
|
|
return -1;
|
|
|
|
memcpy(value_get_raw_data(valuep),
|
|
&ctx->regs.fp_regs.fprs[ctx->freg], sz);
|
|
ctx->freg += 2;
|
|
|
|
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)
|
|
{
|
|
size_t sz = type_sizeof(proc, info);
|
|
if (sz == (size_t)-1)
|
|
return -1;
|
|
|
|
switch (info->type) {
|
|
case ARGTYPE_VOID:
|
|
value_set_word(valuep, 0);
|
|
return 0;
|
|
|
|
case ARGTYPE_STRUCT:
|
|
if (type_get_fp_equivalent(info) != NULL)
|
|
/* fall through */
|
|
case ARGTYPE_FLOAT:
|
|
case ARGTYPE_DOUBLE:
|
|
return allocate_fpr(ctx, proc, info, valuep, sz);
|
|
|
|
/* Structures<4 bytes on s390 and structures<8 bytes
|
|
* on s390x are passed in register. On s390, long
|
|
* long and structures<8 bytes are passed in two
|
|
* consecutive registers (if two are available). */
|
|
|
|
if (sz <= (s390x(ctx) ? 8 : 4))
|
|
return allocate_gpr(ctx, proc, info, valuep, sz);
|
|
else if (sz <= 8)
|
|
return allocate_gpr_pair(ctx, proc, info, valuep, sz);
|
|
|
|
/* fall through */
|
|
|
|
case ARGTYPE_ARRAY:
|
|
if (value_pass_by_reference(valuep) < 0)
|
|
return -1;
|
|
/* fall through */
|
|
|
|
case ARGTYPE_INT:
|
|
case ARGTYPE_UINT:
|
|
case ARGTYPE_LONG:
|
|
case ARGTYPE_ULONG:
|
|
case ARGTYPE_CHAR:
|
|
case ARGTYPE_SHORT:
|
|
case ARGTYPE_USHORT:
|
|
case ARGTYPE_POINTER:
|
|
return allocate_gpr(ctx, proc, info, valuep, sz);
|
|
|
|
default:
|
|
assert(info->type != info->type);
|
|
abort();
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
arch_fetch_retval(struct fetch_context *ctx, enum tof type,
|
|
struct process *proc, struct arg_type_info *info,
|
|
struct value *valuep)
|
|
{
|
|
if (info->type == ARGTYPE_STRUCT) {
|
|
if (value_pass_by_reference(valuep) < 0)
|
|
return -1;
|
|
copy_gpr(ctx, valuep, 2);
|
|
return 0;
|
|
}
|
|
|
|
if (fetch_context_init(proc, ctx, false) < 0)
|
|
return -1;
|
|
return arch_fetch_arg_next(ctx, type, proc, info, valuep);
|
|
}
|
|
|
|
void
|
|
arch_fetch_arg_done(struct fetch_context *context)
|
|
{
|
|
free(context);
|
|
}
|