type.c revision 56abb870555ae16ecffaa12373a0d72328e19514
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
58	case ARGTYPE_ARRAY:
59	case ARGTYPE_ENUM:
60	case ARGTYPE_STRUCT:
61	case ARGTYPE_POINTER:
62		assert(!"Not a simple type!");
63	};
64	abort();
65}
66
67struct enum_entry {
68	char *key;
69	int own_key;
70	int value;
71};
72
73void
74type_init_enum(struct arg_type_info *info)
75{
76	info->type = ARGTYPE_ENUM;
77	VECT_INIT(&info->u.entries, struct enum_entry);
78}
79
80int
81type_enum_add(struct arg_type_info *info,
82	      const char *key, int own_key, int value)
83{
84	assert(info->type == ARGTYPE_ENUM);
85	struct enum_entry entry = { (char *)key, own_key, value };
86	return VECT_PUSHBACK(&info->u.entries, &entry);
87}
88
89size_t
90type_enum_size(struct arg_type_info *info)
91{
92	assert(info->type == ARGTYPE_ENUM);
93	return vect_size(&info->u.entries);
94}
95
96const char *
97type_enum_get(struct arg_type_info *info, int value)
98{
99	assert(info->type == ARGTYPE_ENUM);
100	size_t i;
101	for (i = 0; i < vect_size(&info->u.entries); ++i) {
102		struct enum_entry *entry = VECT_ELEMENT(&info->u.entries,
103							struct enum_entry, i);
104		if (value == entry->value)
105			return entry->key;
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
241void
242type_init_string(struct arg_type_info *info,
243		 struct expr_node *length, int own_length)
244{
245	info->type = ARGTYPE_STRING_N;
246	info->u.string_n_info.length = length;
247	info->u.string_n_info.own_length = own_length;
248}
249
250static void
251type_array_destroy(struct arg_type_info *info)
252{
253	if (info->u.array_info.own_info) {
254		type_destroy(info->u.array_info.elt_type);
255		free(info->u.array_info.elt_type);
256	}
257	if (info->u.array_info.own_length) {
258		expr_destroy(info->u.array_info.length);
259		free(info->u.array_info.length);
260	}
261}
262
263static void
264type_string_n_destroy(struct arg_type_info *info)
265{
266	if (info->u.array_info.own_length) {
267		expr_destroy(info->u.string_n_info.length);
268		free(info->u.string_n_info.length);
269	}
270}
271
272void
273type_init_pointer(struct arg_type_info *info,
274		  struct arg_type_info *pointee_info, int own_info)
275{
276	info->type = ARGTYPE_POINTER;
277	info->u.ptr_info.info = pointee_info;
278	info->u.ptr_info.own_info = own_info;
279}
280
281static void
282type_pointer_destroy(struct arg_type_info *info)
283{
284	if (info->u.ptr_info.own_info) {
285		type_destroy(info->u.ptr_info.info);
286		free(info->u.ptr_info.info);
287	}
288}
289
290void
291type_destroy(struct arg_type_info *info)
292{
293	if (info == NULL)
294		return;
295
296	switch (info->type) {
297	case ARGTYPE_ENUM:
298		return type_enum_destroy(info);
299
300	case ARGTYPE_STRUCT:
301		type_struct_destroy(info);
302		break;
303
304	case ARGTYPE_ARRAY:
305		type_array_destroy(info);
306		break;
307
308	case ARGTYPE_POINTER:
309		type_pointer_destroy(info);
310		break;
311
312	case ARGTYPE_STRING_N:
313		type_string_n_destroy(info);
314
315	case ARGTYPE_UNKNOWN:
316	case ARGTYPE_VOID:
317	case ARGTYPE_INT:
318	case ARGTYPE_UINT:
319	case ARGTYPE_LONG:
320	case ARGTYPE_ULONG:
321	case ARGTYPE_OCTAL:
322	case ARGTYPE_CHAR:
323	case ARGTYPE_SHORT:
324	case ARGTYPE_USHORT:
325	case ARGTYPE_FLOAT:
326	case ARGTYPE_DOUBLE:
327		break;
328
329	case ARGTYPE_FORMAT:
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		return -1;
442	}
443
444	abort();
445}
446
447#undef alignof
448#define alignof(field,st) ((size_t) ((char*) &st.field - (char*) &st))
449
450size_t
451type_alignof(struct Process *proc, struct arg_type_info *type)
452{
453	size_t arch_alignment = arch_type_alignof(proc, type);
454	if (arch_alignment != (size_t)-2)
455		return arch_alignment;
456
457	struct { char c; char C; } cC;
458	struct { char c; short s; } cs;
459	struct { char c; int i; } ci;
460	struct { char c; long l; } cl;
461	struct { char c; void* p; } cp;
462	struct { char c; float f; } cf;
463	struct { char c; double d; } cd;
464
465	static size_t char_alignment = alignof(C, cC);
466	static size_t short_alignment = alignof(s, cs);
467	static size_t int_alignment = alignof(i, ci);
468	static size_t long_alignment = alignof(l, cl);
469	static size_t ptr_alignment = alignof(p, cp);
470	static size_t float_alignment = alignof(f, cf);
471	static size_t double_alignment = alignof(d, cd);
472
473	switch (type->type) {
474		size_t alignment;
475	case ARGTYPE_LONG:
476	case ARGTYPE_ULONG:
477		return long_alignment;
478	case ARGTYPE_CHAR:
479		return char_alignment;
480	case ARGTYPE_SHORT:
481	case ARGTYPE_USHORT:
482		return short_alignment;
483	case ARGTYPE_FLOAT:
484		return float_alignment;
485	case ARGTYPE_DOUBLE:
486		return double_alignment;
487	case ARGTYPE_POINTER:
488		return ptr_alignment;
489
490	case ARGTYPE_ARRAY:
491		return type_alignof(proc, type->u.array_info.elt_type);
492
493	case ARGTYPE_STRUCT:
494		if (layout_struct(proc, type, NULL, &alignment, NULL) < 0)
495			return (size_t)-1;
496		return alignment;
497
498	default:
499		return int_alignment;
500	}
501}
502
503size_t
504type_offsetof(struct Process *proc, struct arg_type_info *type, size_t emt)
505{
506	assert(type->type == ARGTYPE_STRUCT
507	       || type->type == ARGTYPE_ARRAY
508	       /* XXX Temporary, this will be removed.  */
509	       || type->type == ARGTYPE_STRING_N);
510
511	switch (type->type) {
512		size_t alignment;
513		size_t size;
514	case ARGTYPE_ARRAY:
515		alignment = type_alignof(proc, type->u.array_info.elt_type);
516		if (alignment == (size_t)-1)
517			return (size_t)-1;
518
519		size = type_sizeof(proc, type->u.array_info.elt_type);
520		if (size == (size_t)-1)
521			return (size_t)-1;
522
523		return emt * align(size, alignment);
524
525	case ARGTYPE_STRING_N:
526		return emt;
527
528	case ARGTYPE_STRUCT:
529		if (layout_struct(proc, type, NULL, NULL, &emt) < 0)
530			return (size_t)-1;
531		return emt;
532
533	default:
534		abort ();
535	}
536}
537
538struct arg_type_info *
539type_element(struct arg_type_info *info, size_t emt)
540{
541	assert(info->type == ARGTYPE_STRUCT
542	       || info->type == ARGTYPE_ARRAY
543	       /* XXX Temporary, this will be removed.  */
544	       || info->type == ARGTYPE_STRING_N);
545
546	switch (info->type) {
547	case ARGTYPE_ARRAY:
548		return info->u.array_info.elt_type;
549
550	case ARGTYPE_STRUCT:
551		assert(emt < type_struct_size(info));
552		return type_struct_get(info, emt);
553
554	case ARGTYPE_STRING_N:
555		return type_get_simple(ARGTYPE_CHAR);
556
557	default:
558		abort ();
559	}
560}
561