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 
35 struct 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 
45 static int
fetch_register_banks(struct process * proc,struct fetch_context * context,int floating)46 fetch_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 
59 struct fetch_context *
arch_fetch_arg_init(enum tof type,struct process * proc,struct arg_type_info * ret_info)60 arch_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 
94 struct fetch_context *
arch_fetch_arg_clone(struct process * proc,struct fetch_context * context)95 arch_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 
104 int
arch_fetch_arg_next(struct fetch_context * context,enum tof type,struct process * proc,struct arg_type_info * info,struct value * valuep)105 arch_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 
144 int
arch_fetch_retval(struct fetch_context * context,enum tof type,struct process * proc,struct arg_type_info * info,struct value * valuep)145 arch_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 
234 void
arch_fetch_arg_done(struct fetch_context * context)235 arch_fetch_arg_done(struct fetch_context *context)
236 {
237 	if (context != NULL)
238 		free(context);
239 }
240