fetch.c revision 5b5c256d0e9076e745908a43acc46353265a5608
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 valuep->where = VAL_LOC_INFERIOR; 138 valuep->u.address = context->stack_pointer; 139 context->stack_pointer += sz; 140 } 141 142 return 0; 143} 144 145int 146arch_fetch_retval(struct fetch_context *context, enum tof type, 147 struct Process *proc, struct arg_type_info *info, 148 struct value *valuep) 149{ 150 if (fetch_register_banks(proc, context, type == LT_TOF_FUNCTIONR) < 0) 151 return -1; 152 153 if (context->retval.type != NULL) { 154 /* Struct return value was extracted when in fetch 155 * init. */ 156 *valuep = context->retval; 157 return 0; 158 } 159 160 size_t sz = type_sizeof(proc, info); 161 if (sz == (size_t)-1) 162 return -1; 163 if (value_reserve(valuep, sz) == NULL) 164 return -1; 165 166 switch (info->type) { 167 case ARGTYPE_VOID: 168 return 0; 169 170 case ARGTYPE_INT: 171 case ARGTYPE_UINT: 172 case ARGTYPE_LONG: 173 case ARGTYPE_ULONG: 174 case ARGTYPE_CHAR: 175 case ARGTYPE_SHORT: 176 case ARGTYPE_USHORT: 177 case ARGTYPE_POINTER: 178 { 179 unsigned char *buf = value_get_raw_data(valuep); 180 int reg = info->type == ARGTYPE_POINTER ? PT_A0 : PT_D0; 181 unsigned char *val 182 = (unsigned char *)&context->regs[reg]; 183 if (sz < 4) val += 4 - sz; 184 memcpy(buf, val, sz); 185 } 186 return 0; 187 188 case ARGTYPE_FLOAT: 189 case ARGTYPE_DOUBLE: 190 { 191 union { 192 long double ld; 193 double d; 194 float f; 195 char buf[0]; 196 } u; 197 198 unsigned long *reg = &context->fpregs.fpregs[0]; 199 memcpy (&u.ld, reg, sizeof (u.ld)); 200 if (valuep->type->type == ARGTYPE_FLOAT) 201 u.f = (float)u.ld; 202 else if (valuep->type->type == ARGTYPE_DOUBLE) 203 u.d = (double)u.ld; 204 else { 205 assert(!"Unexpected floating type!"); 206 abort(); 207 } 208 unsigned char *buf = value_get_raw_data (valuep); 209 memcpy (buf, u.buf, sz); 210 } 211 return 0; 212 213 case ARGTYPE_STRUCT: 214 { 215 unsigned char *buf = value_get_raw_data(valuep); 216 unsigned char *val 217 = (unsigned char *)&context->regs[PT_D0]; 218 219 assert(sz <= 4 || sz == 8); 220 if (sz < 4) val += 4 - sz; 221 memcpy(buf, val, sz <= 4 ? sz : 4); 222 if (sz == 8) 223 memcpy(buf + 4, &context->regs[PT_D1], 4); 224 } 225 return 0; 226 227 default: 228 assert(!"Unexpected m68k retval type!"); 229 abort(); 230 } 231 232 abort(); 233} 234 235void 236arch_fetch_arg_done(struct fetch_context *context) 237{ 238 if (context != NULL) 239 free(context); 240} 241