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