type.c revision 21e56081e73310b3409c6d4af1af118fb700c3f8
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
29struct arg_type_info *
30type_get_simple(enum arg_type type)
31{
32#define HANDLE(T) {					\
33		static struct arg_type_info t = { T };	\
34	case T:						\
35		return &t;				\
36	}
37
38	switch (type) {
39	HANDLE(ARGTYPE_UNKNOWN)
40	HANDLE(ARGTYPE_VOID)
41	HANDLE(ARGTYPE_INT)
42	HANDLE(ARGTYPE_UINT)
43	HANDLE(ARGTYPE_LONG)
44	HANDLE(ARGTYPE_ULONG)
45	HANDLE(ARGTYPE_OCTAL)
46	HANDLE(ARGTYPE_CHAR)
47	HANDLE(ARGTYPE_SHORT)
48	HANDLE(ARGTYPE_USHORT)
49	HANDLE(ARGTYPE_FLOAT)
50	HANDLE(ARGTYPE_DOUBLE)
51
52	  HANDLE(ARGTYPE_FORMAT)
53
54#undef HANDLE
55
56	case ARGTYPE_STRING_N:
57	case ARGTYPE_COUNT:
58
59	case ARGTYPE_ARRAY:
60	case ARGTYPE_ENUM:
61	case ARGTYPE_STRUCT:
62	case ARGTYPE_POINTER:
63		assert(!"Not a simple type!");
64	};
65	abort();
66}
67
68struct enum_entry {
69	char *key;
70	int own_key;
71	int value;
72};
73
74void
75type_init_enum(struct arg_type_info *info)
76{
77	info->type = ARGTYPE_ENUM;
78	VECT_INIT(&info->u.entries, struct enum_entry);
79}
80
81int
82type_enum_add(struct arg_type_info *info,
83	      const char *key, int own_key, int value)
84{
85	assert(info->type == ARGTYPE_ENUM);
86	struct enum_entry entry = { (char *)key, own_key, value };
87	return VECT_PUSHBACK(&info->u.entries, &entry);
88}
89
90size_t
91type_enum_size(struct arg_type_info *info)
92{
93	assert(info->type == ARGTYPE_ENUM);
94	return vect_size(&info->u.entries);
95}
96
97const char *
98type_enum_get(struct arg_type_info *info, int value)
99{
100	assert(info->type == ARGTYPE_ENUM);
101	size_t i;
102	for (i = 0; i < vect_size(&info->u.entries); ++i) {
103		struct enum_entry *entry = VECT_ELEMENT(&info->u.entries,
104							struct enum_entry, i);
105		if (value == entry->value)
106			return entry->key;
107	}
108	return NULL;
109}
110
111static void
112enum_entry_dtor(struct enum_entry *entry, void *data)
113{
114	if (entry->own_key)
115		free(entry->key);
116}
117
118static void
119type_enum_destroy(struct arg_type_info *info)
120{
121	VECT_DESTROY(&info->u.entries, struct enum_entry,
122		     enum_entry_dtor, NULL);
123}
124
125struct struct_field {
126	struct arg_type_info *info;
127	int own_info;
128};
129
130void
131type_init_struct(struct arg_type_info *info)
132{
133	info->type = ARGTYPE_STRUCT;
134	VECT_INIT(&info->u.entries, struct struct_field);
135}
136
137int
138type_struct_add(struct arg_type_info *info,
139		struct arg_type_info *field_info, int own)
140{
141	assert(info->type == ARGTYPE_STRUCT);
142	struct struct_field field = { field_info, own };
143	return VECT_PUSHBACK(&info->u.entries, &field);
144}
145
146struct arg_type_info *
147type_struct_get(struct arg_type_info *info, size_t idx)
148{
149	assert(info->type == ARGTYPE_STRUCT);
150	struct struct_field *field = VECT_ELEMENT(&info->u.entries,
151						  struct struct_field, idx);
152	if (field == NULL)
153		return NULL;
154	return field->info;
155}
156
157size_t
158type_struct_size(struct arg_type_info *info)
159{
160	assert(info->type == ARGTYPE_STRUCT);
161	return vect_size(&info->u.entries);
162}
163
164static void
165struct_field_dtor(struct struct_field *field, void *data)
166{
167	if (field->own_info) {
168		type_destroy(field->info);
169		free(field->info);
170	}
171}
172
173static void
174type_struct_destroy(struct arg_type_info *info)
175{
176	VECT_DESTROY(&info->u.entries, struct struct_field,
177		     struct_field_dtor, NULL);
178}
179
180static int
181layout_struct(struct Process *proc, struct arg_type_info *info,
182	      size_t *sizep, size_t *alignmentp, size_t *offsetofp)
183{
184	size_t sz = 0;
185	size_t max_alignment = 0;
186	size_t i;
187	size_t offsetof_field = (size_t)-1;
188	if (offsetofp != NULL)
189		offsetof_field = *offsetofp;
190
191	assert(info->type == ARGTYPE_STRUCT);
192	for (i = 0; i < vect_size(&info->u.entries); ++i) {
193		struct struct_field *field
194			= VECT_ELEMENT(&info->u.entries,
195				       struct struct_field, i);
196
197		size_t alignment = type_alignof(proc, field->info);
198		if (alignment == (size_t)-1)
199			return -1;
200
201		/* Add padding to SZ to align the next element.  */
202		sz = align(sz, alignment);
203		if (i == offsetof_field) {
204			*offsetofp = sz;
205			if (sizep == NULL && alignmentp == NULL)
206				return 0;
207		}
208
209		size_t size = type_sizeof(proc, field->info);
210		if (size == (size_t)-1)
211			return -1;
212		sz += size;
213
214		if (alignment > max_alignment)
215			max_alignment = alignment;
216	}
217
218	if (max_alignment > 0)
219		sz = align(sz, max_alignment);
220
221	if (sizep != NULL)
222		*sizep = sz;
223
224	if (alignmentp != NULL)
225		*alignmentp = max_alignment;
226
227	return 0;
228}
229
230void
231type_init_array(struct arg_type_info *info,
232		struct arg_type_info *element_info, int own_info,
233		struct expr_node *length, int own_length)
234{
235	info->type = ARGTYPE_ARRAY;
236	info->u.array_info.elt_type = element_info;
237	info->u.array_info.own_info = own_info;
238	info->u.array_info.length = length;
239	info->u.array_info.own_length = own_length;
240}
241
242void
243type_init_string(struct arg_type_info *info,
244		 struct expr_node *length, int own_length)
245{
246	info->type = ARGTYPE_STRING_N;
247	info->u.string_n_info.length = length;
248	info->u.string_n_info.own_length = own_length;
249}
250
251static void
252type_array_destroy(struct arg_type_info *info)
253{
254	if (info->u.array_info.own_info) {
255		type_destroy(info->u.array_info.elt_type);
256		free(info->u.array_info.elt_type);
257	}
258	if (info->u.array_info.own_length) {
259		expr_destroy(info->u.array_info.length);
260		free(info->u.array_info.length);
261	}
262}
263
264static void
265type_string_n_destroy(struct arg_type_info *info)
266{
267	if (info->u.array_info.own_length) {
268		expr_destroy(info->u.string_n_info.length);
269		free(info->u.string_n_info.length);
270	}
271}
272
273void
274type_init_pointer(struct arg_type_info *info,
275		  struct arg_type_info *pointee_info, int own_info)
276{
277	info->type = ARGTYPE_POINTER;
278	info->u.ptr_info.info = pointee_info;
279	info->u.ptr_info.own_info = own_info;
280}
281
282static void
283type_pointer_destroy(struct arg_type_info *info)
284{
285	if (info->u.ptr_info.own_info) {
286		type_destroy(info->u.ptr_info.info);
287		free(info->u.ptr_info.info);
288	}
289}
290
291void
292type_destroy(struct arg_type_info *info)
293{
294	if (info == NULL)
295		return;
296
297	switch (info->type) {
298	case ARGTYPE_ENUM:
299		return type_enum_destroy(info);
300
301	case ARGTYPE_STRUCT:
302		type_struct_destroy(info);
303		break;
304
305	case ARGTYPE_ARRAY:
306		type_array_destroy(info);
307		break;
308
309	case ARGTYPE_POINTER:
310		type_pointer_destroy(info);
311		break;
312
313	case ARGTYPE_STRING_N:
314		type_string_n_destroy(info);
315
316	case ARGTYPE_UNKNOWN:
317	case ARGTYPE_VOID:
318	case ARGTYPE_INT:
319	case ARGTYPE_UINT:
320	case ARGTYPE_LONG:
321	case ARGTYPE_ULONG:
322	case ARGTYPE_OCTAL:
323	case ARGTYPE_CHAR:
324	case ARGTYPE_SHORT:
325	case ARGTYPE_USHORT:
326	case ARGTYPE_FLOAT:
327	case ARGTYPE_DOUBLE:
328		break;
329
330	case ARGTYPE_FORMAT:
331	case ARGTYPE_COUNT:
332		break;
333	}
334}
335
336#ifdef ARCH_HAVE_SIZEOF
337size_t arch_type_sizeof(struct Process *proc, struct arg_type_info * arg);
338#else
339size_t
340arch_type_sizeof(struct Process *proc, struct arg_type_info * arg)
341{
342	/* Use default value.  */
343	return (size_t)-2;
344}
345#endif
346
347#ifdef ARCH_HAVE_ALIGNOF
348size_t arch_type_alignof(struct Process *proc, struct arg_type_info * arg);
349#else
350size_t
351arch_type_alignof(struct Process *proc, struct arg_type_info * arg)
352{
353	/* Use default value.  */
354	return (size_t)-2;
355}
356#endif
357
358/* We need to support alignments that are not power of two.  E.g. long
359 * double on x86 has alignment of 12.  */
360size_t
361align(size_t sz, size_t alignment)
362{
363	assert(alignment != 0);
364
365	if ((sz % alignment) != 0)
366		sz = ((sz / alignment) + 1) * alignment;
367
368	return sz;
369}
370
371size_t
372type_sizeof(struct Process *proc, struct arg_type_info *type)
373{
374	size_t arch_size = arch_type_sizeof(proc, type);
375	if (arch_size != (size_t)-2)
376		return arch_size;
377
378	switch (type->type) {
379		size_t size;
380	case ARGTYPE_CHAR:
381		return sizeof(char);
382
383	case ARGTYPE_SHORT:
384	case ARGTYPE_USHORT:
385		return sizeof(short);
386
387	case ARGTYPE_INT:
388	case ARGTYPE_UINT:
389	case ARGTYPE_ENUM:
390		return sizeof(int);
391
392	case ARGTYPE_LONG:
393	case ARGTYPE_ULONG:
394		return sizeof(long);
395
396	case ARGTYPE_FLOAT:
397		return sizeof(float);
398
399	case ARGTYPE_DOUBLE:
400		return sizeof(double);
401
402	case ARGTYPE_STRUCT:
403		if (layout_struct(proc, type, &size, NULL, NULL) < 0)
404			return (size_t)-1;
405		return size;
406
407	case ARGTYPE_POINTER:
408		return sizeof(void *);
409
410	case ARGTYPE_ARRAY:
411		if (expr_is_compile_constant(type->u.array_info.length)) {
412			long l;
413			if (expr_eval_constant(type->u.array_info.length,
414					       &l) < 0)
415				return -1;
416
417			struct arg_type_info *elt_ti
418				= type->u.array_info.elt_type;
419
420			size_t elt_size = type_sizeof(proc, elt_ti);
421			if (elt_size == (size_t)-1)
422				return (size_t)-1;
423
424			return ((size_t)l) * elt_size;
425
426		} else {
427			/* Flexible arrays don't count into the
428			 * sizeof.  */
429			return 0;
430		}
431
432	case ARGTYPE_VOID:
433		return 0;
434
435	/* XXX these are in fact formatting conventions, not
436	 * data types.  They should be handled differently.  */
437	case ARGTYPE_OCTAL:
438	case ARGTYPE_UNKNOWN:
439		return sizeof(long);
440
441	case ARGTYPE_FORMAT:
442	case ARGTYPE_STRING_N:
443	case ARGTYPE_COUNT:
444		return -1;
445	}
446
447	abort();
448}
449
450#undef alignof
451#define alignof(field,st) ((size_t) ((char*) &st.field - (char*) &st))
452
453size_t
454type_alignof(struct Process *proc, struct arg_type_info *type)
455{
456	size_t arch_alignment = arch_type_alignof(proc, type);
457	if (arch_alignment != (size_t)-2)
458		return arch_alignment;
459
460	struct { char c; char C; } cC;
461	struct { char c; short s; } cs;
462	struct { char c; int i; } ci;
463	struct { char c; long l; } cl;
464	struct { char c; void* p; } cp;
465	struct { char c; float f; } cf;
466	struct { char c; double d; } cd;
467
468	static size_t char_alignment = alignof(C, cC);
469	static size_t short_alignment = alignof(s, cs);
470	static size_t int_alignment = alignof(i, ci);
471	static size_t long_alignment = alignof(l, cl);
472	static size_t ptr_alignment = alignof(p, cp);
473	static size_t float_alignment = alignof(f, cf);
474	static size_t double_alignment = alignof(d, cd);
475
476	switch (type->type) {
477		size_t alignment;
478	case ARGTYPE_LONG:
479	case ARGTYPE_ULONG:
480		return long_alignment;
481	case ARGTYPE_CHAR:
482		return char_alignment;
483	case ARGTYPE_SHORT:
484	case ARGTYPE_USHORT:
485		return short_alignment;
486	case ARGTYPE_FLOAT:
487		return float_alignment;
488	case ARGTYPE_DOUBLE:
489		return double_alignment;
490	case ARGTYPE_POINTER:
491		return ptr_alignment;
492
493	case ARGTYPE_ARRAY:
494		return type_alignof(proc, type->u.array_info.elt_type);
495
496	case ARGTYPE_STRUCT:
497		if (layout_struct(proc, type, NULL, &alignment, NULL) < 0)
498			return (size_t)-1;
499		return alignment;
500
501	default:
502		return int_alignment;
503	}
504}
505
506size_t
507type_offsetof(struct Process *proc, struct arg_type_info *type, size_t emt)
508{
509	assert(type->type == ARGTYPE_STRUCT
510	       || type->type == ARGTYPE_ARRAY
511	       /* XXX Temporary, this will be removed.  */
512	       || type->type == ARGTYPE_STRING_N);
513
514	switch (type->type) {
515		size_t alignment;
516		size_t size;
517	case ARGTYPE_ARRAY:
518		alignment = type_alignof(proc, type->u.array_info.elt_type);
519		if (alignment == (size_t)-1)
520			return (size_t)-1;
521
522		size = type_sizeof(proc, type->u.array_info.elt_type);
523		if (size == (size_t)-1)
524			return (size_t)-1;
525
526		return emt * align(size, alignment);
527
528	case ARGTYPE_STRING_N:
529		return emt;
530
531	case ARGTYPE_STRUCT:
532		if (layout_struct(proc, type, NULL, NULL, &emt) < 0)
533			return (size_t)-1;
534		return emt;
535
536	default:
537		abort ();
538	}
539}
540
541struct arg_type_info *
542type_element(struct arg_type_info *info, size_t emt)
543{
544	assert(info->type == ARGTYPE_STRUCT
545	       || info->type == ARGTYPE_ARRAY
546	       /* XXX Temporary, this will be removed.  */
547	       || info->type == ARGTYPE_STRING_N);
548
549	switch (info->type) {
550	case ARGTYPE_ARRAY:
551		return info->u.array_info.elt_type;
552
553	case ARGTYPE_STRUCT:
554		assert(emt < type_struct_size(info));
555		return type_struct_get(info, emt);
556
557	case ARGTYPE_STRING_N:
558		return type_get_simple(ARGTYPE_CHAR);
559
560	default:
561		abort ();
562	}
563}
564