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