/* * 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); }