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