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