type.c revision e3f4a984db115979e09414b7281da98399dd8949
1/*
2 * This file is part of ltrace.
3 * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
4 * Copyright (C) 2007,2008 Juan Cespedes
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
19 * 02110-1301 USA
20 */
21
22#include <assert.h>
23#include <stdlib.h>
24
25#include "type.h"
26#include "sysdep.h"
27#include "expr.h"
28#include "lens.h"
29
30struct arg_type_info *
31type_get_simple(enum arg_type type)
32{
33#define HANDLE(T) {					\
34		static struct arg_type_info t = { T };	\
35	case T:						\
36		return &t;				\
37	}
38
39	switch (type) {
40	HANDLE(ARGTYPE_VOID)
41	HANDLE(ARGTYPE_INT)
42	HANDLE(ARGTYPE_UINT)
43	HANDLE(ARGTYPE_LONG)
44	HANDLE(ARGTYPE_ULONG)
45	HANDLE(ARGTYPE_CHAR)
46	HANDLE(ARGTYPE_SHORT)
47	HANDLE(ARGTYPE_USHORT)
48	HANDLE(ARGTYPE_FLOAT)
49	HANDLE(ARGTYPE_DOUBLE)
50
51#undef HANDLE
52
53	case ARGTYPE_ARRAY:
54	case ARGTYPE_ENUM:
55	case ARGTYPE_STRUCT:
56	case ARGTYPE_POINTER:
57		assert(!"Not a simple type!");
58	};
59	abort();
60}
61
62static void
63type_init_common(struct arg_type_info *info, enum arg_type type)
64{
65	info->type = type;
66	info->lens = NULL;
67	info->own_lens = 0;
68}
69
70struct enum_entry {
71	char *key;
72	int own_key;
73	int value;
74};
75
76void
77type_init_enum(struct arg_type_info *info)
78{
79	type_init_common(info, ARGTYPE_ENUM);
80	VECT_INIT(&info->u.entries, struct enum_entry);
81}
82
83int
84type_enum_add(struct arg_type_info *info,
85	      const char *key, int own_key, int value)
86{
87	assert(info->type == ARGTYPE_ENUM);
88	struct enum_entry entry = { (char *)key, own_key, value };
89	return VECT_PUSHBACK(&info->u.entries, &entry);
90}
91
92size_t
93type_enum_size(struct arg_type_info *info)
94{
95	assert(info->type == ARGTYPE_ENUM);
96	return vect_size(&info->u.entries);
97}
98
99const char *
100type_enum_get(struct arg_type_info *info, int value)
101{
102	assert(info->type == ARGTYPE_ENUM);
103	size_t i;
104	for (i = 0; i < vect_size(&info->u.entries); ++i) {
105		struct enum_entry *entry = VECT_ELEMENT(&info->u.entries,
106							struct enum_entry, i);
107		if (value == entry->value)
108			return entry->key;
109	}
110	return NULL;
111}
112
113static void
114enum_entry_dtor(struct enum_entry *entry, void *data)
115{
116	if (entry->own_key)
117		free(entry->key);
118}
119
120static void
121type_enum_destroy(struct arg_type_info *info)
122{
123	VECT_DESTROY(&info->u.entries, struct enum_entry,
124		     enum_entry_dtor, NULL);
125}
126
127struct struct_field {
128	struct arg_type_info *info;
129	int own_info;
130};
131
132void
133type_init_struct(struct arg_type_info *info)
134{
135	type_init_common(info, ARGTYPE_STRUCT);
136	VECT_INIT(&info->u.entries, struct struct_field);
137}
138
139int
140type_struct_add(struct arg_type_info *info,
141		struct arg_type_info *field_info, int own)
142{
143	assert(info->type == ARGTYPE_STRUCT);
144	struct struct_field field = { field_info, own };
145	return VECT_PUSHBACK(&info->u.entries, &field);
146}
147
148struct arg_type_info *
149type_struct_get(struct arg_type_info *info, size_t idx)
150{
151	assert(info->type == ARGTYPE_STRUCT);
152	struct struct_field *field = VECT_ELEMENT(&info->u.entries,
153						  struct struct_field, idx);
154	if (field == NULL)
155		return NULL;
156	return field->info;
157}
158
159size_t
160type_struct_size(struct arg_type_info *info)
161{
162	assert(info->type == ARGTYPE_STRUCT);
163	return vect_size(&info->u.entries);
164}
165
166static void
167struct_field_dtor(struct struct_field *field, void *data)
168{
169	if (field->own_info) {
170		type_destroy(field->info);
171		free(field->info);
172	}
173}
174
175static void
176type_struct_destroy(struct arg_type_info *info)
177{
178	VECT_DESTROY(&info->u.entries, struct struct_field,
179		     struct_field_dtor, NULL);
180}
181
182static int
183layout_struct(struct Process *proc, struct arg_type_info *info,
184	      size_t *sizep, size_t *alignmentp, size_t *offsetofp)
185{
186	size_t sz = 0;
187	size_t max_alignment = 0;
188	size_t i;
189	size_t offsetof_field = (size_t)-1;
190	if (offsetofp != NULL)
191		offsetof_field = *offsetofp;
192
193	assert(info->type == ARGTYPE_STRUCT);
194	for (i = 0; i < vect_size(&info->u.entries); ++i) {
195		struct struct_field *field
196			= VECT_ELEMENT(&info->u.entries,
197				       struct struct_field, i);
198
199		size_t alignment = type_alignof(proc, field->info);
200		if (alignment == (size_t)-1)
201			return -1;
202
203		/* Add padding to SZ to align the next element.  */
204		sz = align(sz, alignment);
205		if (i == offsetof_field) {
206			*offsetofp = sz;
207			if (sizep == NULL && alignmentp == NULL)
208				return 0;
209		}
210
211		size_t size = type_sizeof(proc, field->info);
212		if (size == (size_t)-1)
213			return -1;
214		sz += size;
215
216		if (alignment > max_alignment)
217			max_alignment = alignment;
218	}
219
220	if (max_alignment > 0)
221		sz = align(sz, max_alignment);
222
223	if (sizep != NULL)
224		*sizep = sz;
225
226	if (alignmentp != NULL)
227		*alignmentp = max_alignment;
228
229	return 0;
230}
231
232void
233type_init_array(struct arg_type_info *info,
234		struct arg_type_info *element_info, int own_info,
235		struct expr_node *length_expr, int own_length)
236{
237	type_init_common(info, ARGTYPE_ARRAY);
238	info->u.array_info.elt_type = element_info;
239	info->u.array_info.own_info = own_info;
240	info->u.array_info.length = length_expr;
241	info->u.array_info.own_length = own_length;
242}
243
244static void
245type_array_destroy(struct arg_type_info *info)
246{
247	if (info->u.array_info.own_info) {
248		type_destroy(info->u.array_info.elt_type);
249		free(info->u.array_info.elt_type);
250	}
251	if (info->u.array_info.own_length) {
252		expr_destroy(info->u.array_info.length);
253		free(info->u.array_info.length);
254	}
255}
256
257void
258type_init_pointer(struct arg_type_info *info,
259		  struct arg_type_info *pointee_info, int own_info)
260{
261	type_init_common(info, ARGTYPE_POINTER);
262	info->u.ptr_info.info = pointee_info;
263	info->u.ptr_info.own_info = own_info;
264}
265
266static void
267type_pointer_destroy(struct arg_type_info *info)
268{
269	if (info->u.ptr_info.own_info) {
270		type_destroy(info->u.ptr_info.info);
271		free(info->u.ptr_info.info);
272	}
273}
274
275void
276type_destroy(struct arg_type_info *info)
277{
278	if (info == NULL)
279		return;
280
281	switch (info->type) {
282	case ARGTYPE_ENUM:
283		type_enum_destroy(info);
284		break;
285
286	case ARGTYPE_STRUCT:
287		type_struct_destroy(info);
288		break;
289
290	case ARGTYPE_ARRAY:
291		type_array_destroy(info);
292		break;
293
294	case ARGTYPE_POINTER:
295		type_pointer_destroy(info);
296		break;
297
298	case ARGTYPE_VOID:
299	case ARGTYPE_INT:
300	case ARGTYPE_UINT:
301	case ARGTYPE_LONG:
302	case ARGTYPE_ULONG:
303	case ARGTYPE_CHAR:
304	case ARGTYPE_SHORT:
305	case ARGTYPE_USHORT:
306	case ARGTYPE_FLOAT:
307	case ARGTYPE_DOUBLE:
308		break;
309	}
310
311	if (info->own_lens) {
312		lens_destroy(info->lens);
313		free(info->lens);
314	}
315}
316
317#ifdef ARCH_HAVE_SIZEOF
318size_t arch_type_sizeof(struct Process *proc, struct arg_type_info * arg);
319#else
320size_t
321arch_type_sizeof(struct Process *proc, struct arg_type_info * arg)
322{
323	/* Use default value.  */
324	return (size_t)-2;
325}
326#endif
327
328#ifdef ARCH_HAVE_ALIGNOF
329size_t arch_type_alignof(struct Process *proc, struct arg_type_info * arg);
330#else
331size_t
332arch_type_alignof(struct Process *proc, struct arg_type_info * arg)
333{
334	/* Use default value.  */
335	return (size_t)-2;
336}
337#endif
338
339/* We need to support alignments that are not power of two.  E.g. long
340 * double on x86 has alignment of 12.  */
341size_t
342align(size_t sz, size_t alignment)
343{
344	assert(alignment != 0);
345
346	if ((sz % alignment) != 0)
347		sz = ((sz / alignment) + 1) * alignment;
348
349	return sz;
350}
351
352size_t
353type_sizeof(struct Process *proc, struct arg_type_info *type)
354{
355	size_t arch_size = arch_type_sizeof(proc, type);
356	if (arch_size != (size_t)-2)
357		return arch_size;
358
359	switch (type->type) {
360		size_t size;
361	case ARGTYPE_CHAR:
362		return sizeof(char);
363
364	case ARGTYPE_SHORT:
365	case ARGTYPE_USHORT:
366		return sizeof(short);
367
368	case ARGTYPE_INT:
369	case ARGTYPE_UINT:
370	case ARGTYPE_ENUM:
371		return sizeof(int);
372
373	case ARGTYPE_LONG:
374	case ARGTYPE_ULONG:
375		return sizeof(long);
376
377	case ARGTYPE_FLOAT:
378		return sizeof(float);
379
380	case ARGTYPE_DOUBLE:
381		return sizeof(double);
382
383	case ARGTYPE_STRUCT:
384		if (layout_struct(proc, type, &size, NULL, NULL) < 0)
385			return (size_t)-1;
386		return size;
387
388	case ARGTYPE_POINTER:
389		return sizeof(void *);
390
391	case ARGTYPE_ARRAY:
392		if (expr_is_compile_constant(type->u.array_info.length)) {
393			long l;
394			if (expr_eval_constant(type->u.array_info.length,
395					       &l) < 0)
396				return -1;
397
398			struct arg_type_info *elt_ti
399				= type->u.array_info.elt_type;
400
401			size_t elt_size = type_sizeof(proc, elt_ti);
402			if (elt_size == (size_t)-1)
403				return (size_t)-1;
404
405			return ((size_t)l) * elt_size;
406
407		} else {
408			/* Flexible arrays don't count into the
409			 * sizeof.  */
410			return 0;
411		}
412
413	case ARGTYPE_VOID:
414		return 0;
415	}
416
417	abort();
418}
419
420#undef alignof
421#define alignof(field,st) ((size_t) ((char*) &st.field - (char*) &st))
422
423size_t
424type_alignof(struct Process *proc, struct arg_type_info *type)
425{
426	size_t arch_alignment = arch_type_alignof(proc, type);
427	if (arch_alignment != (size_t)-2)
428		return arch_alignment;
429
430	struct { char c; char C; } cC;
431	struct { char c; short s; } cs;
432	struct { char c; int i; } ci;
433	struct { char c; long l; } cl;
434	struct { char c; void* p; } cp;
435	struct { char c; float f; } cf;
436	struct { char c; double d; } cd;
437
438	static size_t char_alignment = alignof(C, cC);
439	static size_t short_alignment = alignof(s, cs);
440	static size_t int_alignment = alignof(i, ci);
441	static size_t long_alignment = alignof(l, cl);
442	static size_t ptr_alignment = alignof(p, cp);
443	static size_t float_alignment = alignof(f, cf);
444	static size_t double_alignment = alignof(d, cd);
445
446	switch (type->type) {
447		size_t alignment;
448	case ARGTYPE_LONG:
449	case ARGTYPE_ULONG:
450		return long_alignment;
451	case ARGTYPE_CHAR:
452		return char_alignment;
453	case ARGTYPE_SHORT:
454	case ARGTYPE_USHORT:
455		return short_alignment;
456	case ARGTYPE_FLOAT:
457		return float_alignment;
458	case ARGTYPE_DOUBLE:
459		return double_alignment;
460	case ARGTYPE_POINTER:
461		return ptr_alignment;
462
463	case ARGTYPE_ARRAY:
464		return type_alignof(proc, type->u.array_info.elt_type);
465
466	case ARGTYPE_STRUCT:
467		if (layout_struct(proc, type, NULL, &alignment, NULL) < 0)
468			return (size_t)-1;
469		return alignment;
470
471	default:
472		return int_alignment;
473	}
474}
475
476size_t
477type_offsetof(struct Process *proc, struct arg_type_info *type, size_t emt)
478{
479	assert(type->type == ARGTYPE_STRUCT
480	       || type->type == ARGTYPE_ARRAY);
481
482	switch (type->type) {
483		size_t alignment;
484		size_t size;
485	case ARGTYPE_ARRAY:
486		alignment = type_alignof(proc, type->u.array_info.elt_type);
487		if (alignment == (size_t)-1)
488			return (size_t)-1;
489
490		size = type_sizeof(proc, type->u.array_info.elt_type);
491		if (size == (size_t)-1)
492			return (size_t)-1;
493
494		return emt * align(size, alignment);
495
496	case ARGTYPE_STRUCT:
497		if (layout_struct(proc, type, NULL, NULL, &emt) < 0)
498			return (size_t)-1;
499		return emt;
500
501	default:
502		abort();
503	}
504}
505
506struct arg_type_info *
507type_element(struct arg_type_info *info, size_t emt)
508{
509	assert(info->type == ARGTYPE_STRUCT
510	       || info->type == ARGTYPE_ARRAY);
511
512	switch (info->type) {
513	case ARGTYPE_ARRAY:
514		return info->u.array_info.elt_type;
515
516	case ARGTYPE_STRUCT:
517		assert(emt < type_struct_size(info));
518		return type_struct_get(info, emt);
519
520	default:
521		abort();
522	}
523}
524