fetch.c revision 54737daf1c51ec826fcabc8189a7ff891f29d59b
1/*
2 * This file is part of ltrace.
3 * Copyright (C) 2012 Petr Machata, Red Hat Inc.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18 * 02110-1301 USA
19 */
20
21#include <assert.h>
22#include <ptrace.h>
23#include <stdint.h>
24#include <stdlib.h>
25#include <string.h>
26
27#include <stdio.h>
28
29#include "backend.h"
30#include "fetch.h"
31#include "type.h"
32#include "proc.h"
33#include "value.h"
34
35static int allocate_gpr(struct fetch_context *ctx, struct Process *proc,
36			struct arg_type_info *info, struct value *valuep);
37
38/* Floating point registers have the same width on 32-bit as well as
39 * 64-bit PPC, but <ucontext.h> presents a different API depending on
40 * whether ltrace is PPC32 or PPC64.
41 *
42 * This is PPC64 definition.  The PPC32 is simply an array of 33
43 * doubles, and doesn't contain the terminating pad.  Both seem
44 * compatible enough.  */
45struct fpregs_t
46{
47	double fpregs[32];
48	double fpscr;
49	unsigned int _pad[2];
50};
51
52typedef uint32_t gregs32_t[48];
53typedef uint64_t gregs64_t[48];
54
55struct fetch_context {
56	target_address_t stack_pointer;
57	int greg;
58	int freg;
59	int ret_struct;
60
61	union {
62		gregs32_t r32;
63		gregs64_t r64;
64	} regs;
65	struct fpregs_t fpregs;
66
67};
68
69static int
70fetch_context_init(struct Process *proc, struct fetch_context *context)
71{
72	context->greg = 3;
73	context->freg = 1;
74
75	if (proc->e_machine == EM_PPC64)
76		context->stack_pointer = proc->stack_pointer + 8;
77	else
78		context->stack_pointer = proc->stack_pointer + 112;
79
80	/* When ltrace is 64-bit, we might use PTRACE_GETREGS to
81	 * obtain 64-bit as well as 32-bit registers.  But if we do it
82	 * this way, 32-bit ltrace can obtain 64-bit registers.
83	 *
84	 * XXX this direction is not supported as of this writing, but
85	 * should be eventually.  */
86	if (proc->e_machine == EM_PPC64) {
87		if (ptrace(PTRACE_GETREGS64, proc->pid, 0,
88			   &context->regs.r64) < 0)
89			return -1;
90	} else if (ptrace(PTRACE_GETREGS, proc->pid, 0,
91			  &context->regs.r32) < 0) {
92		return -1;
93	}
94
95	if (ptrace(PTRACE_GETFPREGS, proc->pid, 0, &context->fpregs) < 0)
96		return -1;
97
98	return 0;
99}
100
101struct fetch_context *
102arch_fetch_arg_init(enum tof type, struct Process *proc,
103		    struct arg_type_info *ret_info)
104{
105	struct fetch_context *context = malloc(sizeof(*context));
106	if (context == NULL
107	    || fetch_context_init(proc, context) < 0) {
108		free(context);
109		return NULL;
110	}
111
112	/* Aggregates or unions of any length, and character strings
113	 * of length longer than 8 bytes, will be returned in a
114	 * storage buffer allocated by the caller. The caller will
115	 * pass the address of this buffer as a hidden first argument
116	 * in r3, causing the first explicit argument to be passed in
117	 * r4.  */
118	context->ret_struct = ret_info->type == ARGTYPE_STRUCT;
119	if (context->ret_struct)
120		context->greg++;
121
122	return context;
123}
124
125struct fetch_context *
126arch_fetch_arg_clone(struct Process *proc,
127		     struct fetch_context *context)
128{
129	struct fetch_context *clone = malloc(sizeof(*context));
130	if (clone == NULL)
131		return NULL;
132	*clone = *context;
133	return clone;
134}
135
136static int
137allocate_stack_slot(struct fetch_context *ctx, struct Process *proc,
138		    struct arg_type_info *info, struct value *valuep)
139{
140	assert(!"allocate_stack_slot not implemented");
141	abort();
142}
143
144static int
145allocate_float(struct fetch_context *ctx, struct Process *proc,
146	       struct arg_type_info *info, struct value *valuep)
147{
148	int pool = proc->e_machine == EM_PPC ? 8 : 13;
149	if (ctx->freg <= pool) {
150		union {
151			double d;
152			float f;
153			char buf[0];
154		} u = { .d = ctx->fpregs.fpregs[ctx->freg] };
155
156		ctx->freg++;
157		ctx->greg++;
158
159		size_t sz = sizeof(double);
160		if (info->type == ARGTYPE_FLOAT) {
161			sz = sizeof(float);
162			u.f = (float)u.d;
163		}
164
165		if (value_reserve(valuep, sz) == NULL)
166			return -1;
167
168		memcpy(value_get_raw_data(valuep), u.buf, sz);
169		return 0;
170	}
171	return allocate_stack_slot(ctx, proc, info, valuep);
172}
173
174static uint64_t
175read_gpr(struct fetch_context *ctx, struct Process *proc, int reg_num)
176{
177	if (proc->e_machine == EM_PPC)
178		return ctx->regs.r32[reg_num];
179	else
180		return ctx->regs.r64[reg_num];
181}
182
183static int
184allocate_gpr(struct fetch_context *ctx, struct Process *proc,
185	     struct arg_type_info *info, struct value *valuep)
186{
187	if (ctx->greg > 10)
188		return allocate_stack_slot(ctx, proc, info, valuep);
189
190	int reg_num = ctx->greg++;
191	if (valuep == NULL)
192		return 0;
193
194	size_t sz = type_sizeof(proc, info);
195	if (sz == (size_t)-1)
196		return -1;
197	assert(sz == 1 || sz == 2 || sz == 4 || sz == 8);
198	if (value_reserve(valuep, sz) == NULL)
199		return -1;
200
201	union {
202		uint64_t i64;
203		uint32_t i32;
204		uint16_t i16;
205		uint8_t i8;
206		char buf[0];
207	} u;
208
209	u.i64 = read_gpr(ctx, proc, reg_num);
210
211	/* The support for little endian PowerPC is in upstream Linux
212	 * and BFD, and Unix-like Solaris, which we might well support
213	 * at some point, runs PowerPC in little endian as well.  So
214	 * let's do it the hard way.  */
215	switch (sz) {
216	case 1:
217		u.i8 = u.i64;
218		break;
219	case 2:
220		u.i16 = u.i64;
221		break;
222	case 4:
223		u.i32 = u.i64;
224	case 8:
225		break;
226	}
227
228	memcpy(value_get_raw_data(valuep), u.buf, sz);
229	return 0;
230}
231
232static int
233allocate_argument(struct fetch_context *ctx, struct Process *proc,
234		  struct arg_type_info *info, struct value *valuep)
235{
236	switch (info->type) {
237	case ARGTYPE_VOID:
238		value_set_word(valuep, 0);
239		return 0;
240
241	case ARGTYPE_INT:
242	case ARGTYPE_UINT:
243	case ARGTYPE_LONG:
244	case ARGTYPE_ULONG:
245	case ARGTYPE_CHAR:
246	case ARGTYPE_SHORT:
247	case ARGTYPE_USHORT:
248	case ARGTYPE_POINTER:
249		return allocate_gpr(ctx, proc, info, valuep);
250
251	case ARGTYPE_FLOAT:
252	case ARGTYPE_DOUBLE:
253		return allocate_float(ctx, proc, info, valuep);
254
255	case ARGTYPE_STRUCT:
256		/* Fixed size aggregates and unions passed by value
257		 * are mapped to as many doublewords of the parameter
258		 * save area as the value uses in memory.  [...] The
259		 * first eight doublewords mapped to the parameter
260		 * save area correspond to the registers r3 through
261		 * r10.  */
262		assert(!"arch_fetch_arg_next structures not implemented");
263		abort();
264
265	case ARGTYPE_ARRAY:
266		/* Arrays decay into pointers.  */
267		assert(info->type != ARGTYPE_ARRAY);
268		abort();
269	}
270
271	assert(info->type != info->type);
272	abort();
273}
274
275int
276arch_fetch_arg_next(struct fetch_context *ctx, enum tof type,
277		    struct Process *proc,
278		    struct arg_type_info *info, struct value *valuep)
279{
280	return allocate_argument(ctx, proc, info, valuep);
281}
282
283int
284arch_fetch_retval(struct fetch_context *ctx, enum tof type,
285		  struct Process *proc, struct arg_type_info *info,
286		  struct value *valuep)
287{
288	if (ctx->ret_struct) {
289		assert(info->type == ARGTYPE_STRUCT);
290
291		uint64_t addr = read_gpr(ctx, proc, 3);
292		value_init(valuep, proc, NULL, info, 0);
293
294		if (value_pass_by_reference(valuep) < 0) {
295			value_destroy(valuep);
296			return -1;
297		}
298
299		valuep->where = VAL_LOC_INFERIOR;
300		valuep->u.address = (target_address_t)addr;
301		return 0;
302	}
303
304	if (fetch_context_init(proc, ctx) < 0)
305		return -1;
306	return allocate_argument(ctx, proc, info, valuep);
307}
308
309void
310arch_fetch_arg_done(struct fetch_context *context)
311{
312	free(context);
313}
314