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