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