fetch.c revision 929bd57ca202fd2f2e8485ebf65d683e664f67b5
1/* 2 * This file is part of ltrace. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License as 6 * published by the Free Software Foundation; either version 2 of the 7 * License, or (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, but 10 * WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, see <http://www.gnu.org/licenses/>. 16 */ 17 18#include "config.h" 19 20#include <sys/types.h> 21#include <assert.h> 22#include <stdlib.h> 23#include <string.h> 24#include <sys/procfs.h> 25#include <sys/reg.h> 26 27#include "backend.h" 28#include "expr.h" 29#include "fetch.h" 30#include "proc.h" 31#include "ptrace.h" 32#include "type.h" 33#include "value.h" 34 35struct fetch_context 36{ 37 elf_gregset_t regs; 38 elf_fpregset_t fpregs; 39 40 int arg_num; 41 arch_addr_t stack_pointer; 42 struct value retval; 43}; 44 45static int 46fetch_register_banks(struct process *proc, struct fetch_context *context, 47 int floating) 48{ 49 if (ptrace(PTRACE_GETREGS, proc->pid, 0, &context->regs) < 0) 50 return -1; 51 52 if (floating 53 && ptrace(PTRACE_GETFPREGS, proc->pid, 0, &context->fpregs) < 0) 54 return -1; 55 56 return 0; 57} 58 59struct fetch_context * 60arch_fetch_arg_init(enum tof type, struct process *proc, 61 struct arg_type_info *ret_info) 62{ 63 struct fetch_context *context = malloc(sizeof(*context)); 64 if (context == NULL) 65 return NULL; 66 67 assert(type != LT_TOF_FUNCTIONR && type != LT_TOF_SYSCALLR); 68 if (fetch_register_banks(proc, context, type == LT_TOF_FUNCTION) < 0) { 69 fail: 70 free(context); 71 return NULL; 72 } 73 74 context->arg_num = 0; 75 context->stack_pointer = (arch_addr_t)context->regs[PT_USP] + 4; 76 77 size_t sz = type_sizeof(proc, ret_info); 78 if (sz == (size_t)-1) 79 goto fail; 80 81 if (ret_info->type == ARGTYPE_STRUCT && !(sz <= 4 || sz == 8)) { 82 value_init(&context->retval, proc, NULL, ret_info, 0); 83 84 if (value_pass_by_reference(&context->retval) < 0) 85 goto fail; 86 value_set_word(&context->retval, context->regs[PT_A1]); 87 } else { 88 value_init_detached(&context->retval, NULL, NULL, 0); 89 } 90 91 return context; 92} 93 94struct fetch_context * 95arch_fetch_arg_clone(struct process *proc, struct fetch_context *context) 96{ 97 struct fetch_context *ret = malloc(sizeof(*ret)); 98 if (ret == NULL) 99 return NULL; 100 *ret = *context; 101 return ret; 102} 103 104int 105arch_fetch_arg_next(struct fetch_context *context, enum tof type, 106 struct process *proc, struct arg_type_info *info, 107 struct value *valuep) 108{ 109 size_t sz = type_sizeof(proc, info); 110 if (sz == (size_t)-1) 111 return -1; 112 113 if (type == LT_TOF_SYSCALL) { 114 int reg; 115 116 switch (context->arg_num++) { 117 case 0: reg = PT_D1; break; 118 case 1: reg = PT_D2; break; 119 case 2: reg = PT_D3; break; 120 case 3: reg = PT_D4; break; 121 case 4: reg = PT_D5; break; 122 case 5: reg = PT_A0; break; 123 default: 124 assert(!"More than six syscall arguments???"); 125 abort(); 126 } 127 value_set_word(valuep, context->regs[reg]); 128 } else { 129 size_t a = type_alignof(valuep->inferior, valuep->type); 130 if (a < 4) 131 a = 4; 132 context->stack_pointer = (arch_addr_t) 133 align((unsigned long)context->stack_pointer, a); 134 if (sz < 4) 135 context->stack_pointer += 4 - sz; 136 137 value_in_inferior(valuep, context->stack_pointer); 138 context->stack_pointer += sz; 139 } 140 141 return 0; 142} 143 144int 145arch_fetch_retval(struct fetch_context *context, enum tof type, 146 struct process *proc, struct arg_type_info *info, 147 struct value *valuep) 148{ 149 if (fetch_register_banks(proc, context, type == LT_TOF_FUNCTIONR) < 0) 150 return -1; 151 152 if (context->retval.type != NULL) { 153 /* Struct return value was extracted when in fetch 154 * init. */ 155 *valuep = context->retval; 156 return 0; 157 } 158 159 size_t sz = type_sizeof(proc, info); 160 if (sz == (size_t)-1) 161 return -1; 162 if (value_reserve(valuep, sz) == NULL) 163 return -1; 164 165 switch (info->type) { 166 case ARGTYPE_VOID: 167 return 0; 168 169 case ARGTYPE_INT: 170 case ARGTYPE_UINT: 171 case ARGTYPE_LONG: 172 case ARGTYPE_ULONG: 173 case ARGTYPE_CHAR: 174 case ARGTYPE_SHORT: 175 case ARGTYPE_USHORT: 176 case ARGTYPE_POINTER: 177 { 178 unsigned char *buf = value_get_raw_data(valuep); 179 int reg = info->type == ARGTYPE_POINTER ? PT_A0 : PT_D0; 180 unsigned char *val 181 = (unsigned char *)&context->regs[reg]; 182 if (sz < 4) val += 4 - sz; 183 memcpy(buf, val, sz); 184 } 185 return 0; 186 187 case ARGTYPE_FLOAT: 188 case ARGTYPE_DOUBLE: 189 { 190 union { 191 long double ld; 192 double d; 193 float f; 194 char buf[0]; 195 } u; 196 197 unsigned long *reg = &context->fpregs.fpregs[0]; 198 memcpy (&u.ld, reg, sizeof (u.ld)); 199 if (valuep->type->type == ARGTYPE_FLOAT) 200 u.f = (float)u.ld; 201 else if (valuep->type->type == ARGTYPE_DOUBLE) 202 u.d = (double)u.ld; 203 else { 204 assert(!"Unexpected floating type!"); 205 abort(); 206 } 207 unsigned char *buf = value_get_raw_data (valuep); 208 memcpy (buf, u.buf, sz); 209 } 210 return 0; 211 212 case ARGTYPE_STRUCT: 213 { 214 unsigned char *buf = value_get_raw_data(valuep); 215 unsigned char *val 216 = (unsigned char *)&context->regs[PT_D0]; 217 218 assert(sz <= 4 || sz == 8); 219 if (sz < 4) val += 4 - sz; 220 memcpy(buf, val, sz <= 4 ? sz : 4); 221 if (sz == 8) 222 memcpy(buf + 4, &context->regs[PT_D1], 4); 223 } 224 return 0; 225 226 default: 227 assert(!"Unexpected m68k retval type!"); 228 abort(); 229 } 230 231 abort(); 232} 233 234void 235arch_fetch_arg_done(struct fetch_context *context) 236{ 237 if (context != NULL) 238 free(context); 239} 240