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