read_config_file.c revision 2fb2b8d877ae562374a89ad777ec24534522840e
1/*
2 * This file is part of ltrace.
3 * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
4 * Copyright (C) 1998,1999,2003,2007,2008,2009 Juan Cespedes
5 * Copyright (C) 2006 Ian Wienand
6 * Copyright (C) 2006 Steve Fink
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21 * 02110-1301 USA
22 */
23
24#include "config.h"
25
26#include <string.h>
27#include <stdlib.h>
28#include <ctype.h>
29#include <errno.h>
30#include <error.h>
31#include <assert.h>
32
33#include "common.h"
34#include "output.h"
35#include "expr.h"
36#include "zero.h"
37#include "param.h"
38#include "type.h"
39
40static int line_no;
41static char *filename;
42static int error_count = 0;
43
44static struct arg_type_info *parse_type(char **str);
45
46Function *list_of_functions = NULL;
47
48/* Map of strings to type names. These do not need to be in any
49 * particular order */
50static struct list_of_pt_t {
51	char *name;
52	enum arg_type pt;
53} list_of_pt[] = {
54	{
55	"void", ARGTYPE_VOID}, {
56	"int", ARGTYPE_INT}, {
57	"uint", ARGTYPE_UINT}, {
58	"long", ARGTYPE_LONG}, {
59	"ulong", ARGTYPE_ULONG}, {
60	"octal", ARGTYPE_OCTAL}, {
61	"char", ARGTYPE_CHAR}, {
62	"short", ARGTYPE_SHORT}, {
63	"ushort", ARGTYPE_USHORT}, {
64	"float", ARGTYPE_FLOAT}, {
65	"double", ARGTYPE_DOUBLE}, {
66	"format", ARGTYPE_FORMAT}, {
67	"string", ARGTYPE_STRING}, {
68	"array", ARGTYPE_ARRAY}, {
69	"struct", ARGTYPE_STRUCT}, {
70	"enum", ARGTYPE_ENUM}, {
71	NULL, ARGTYPE_UNKNOWN}	/* Must finish with NULL */
72};
73
74/* Array of prototype objects for each of the types. The order in this
75 * array must exactly match the list of enumerated values in
76 * common.h */
77static struct arg_type_info arg_type_prototypes[] = {
78	{ ARGTYPE_VOID },
79	{ ARGTYPE_INT },
80	{ ARGTYPE_UINT },
81	{ ARGTYPE_LONG },
82	{ ARGTYPE_ULONG },
83	{ ARGTYPE_OCTAL },
84	{ ARGTYPE_CHAR },
85	{ ARGTYPE_SHORT },
86	{ ARGTYPE_USHORT },
87	{ ARGTYPE_FLOAT },
88	{ ARGTYPE_DOUBLE },
89	{ ARGTYPE_FORMAT },
90	{ ARGTYPE_STRING },
91	{ ARGTYPE_STRING_N },
92	{ ARGTYPE_ARRAY },
93	{ ARGTYPE_ENUM },
94	{ ARGTYPE_STRUCT },
95	{ ARGTYPE_POINTER },
96	{ ARGTYPE_UNKNOWN }
97};
98
99struct arg_type_info *
100lookup_prototype(enum arg_type at) {
101	if (at >= 0 && at <= ARGTYPE_COUNT)
102		return &arg_type_prototypes[at];
103	else
104		return &arg_type_prototypes[ARGTYPE_COUNT]; /* UNKNOWN */
105}
106
107static struct arg_type_info *
108str2type(char **str) {
109	struct list_of_pt_t *tmp = &list_of_pt[0];
110
111	while (tmp->name) {
112		if (!strncmp(*str, tmp->name, strlen(tmp->name))
113				&& index(" ,()#*;012345[", *(*str + strlen(tmp->name)))) {
114			*str += strlen(tmp->name);
115			return lookup_prototype(tmp->pt);
116		}
117		tmp++;
118	}
119	return lookup_prototype(ARGTYPE_UNKNOWN);
120}
121
122static void
123eat_spaces(char **str) {
124	while (**str == ' ') {
125		(*str)++;
126	}
127}
128
129static char *
130xstrndup(char *str, size_t len) {
131	char *ret = (char *) malloc(len + 1);
132	if (ret == NULL) {
133		report_global_error("malloc: %s", strerror(errno));
134		return NULL;
135	}
136	strncpy(ret, str, len);
137	ret[len] = 0;
138	return ret;
139}
140
141static char *
142parse_ident(char **str) {
143	char *ident = *str;
144
145	if (!isalpha(**str) && **str != '_') {
146		report_error(filename, line_no, "bad identifier");
147		return NULL;
148	}
149
150	while (**str && (isalnum(**str) || **str == '_')) {
151		++(*str);
152	}
153
154	return xstrndup(ident, *str - ident);
155}
156
157/*
158  Returns position in string at the left parenthesis which starts the
159  function's argument signature. Returns NULL on error.
160*/
161static char *
162start_of_arg_sig(char *str) {
163	char *pos;
164	int stacked = 0;
165
166	if (!strlen(str))
167		return NULL;
168
169	pos = &str[strlen(str)];
170	do {
171		pos--;
172		if (pos < str)
173			return NULL;
174		while ((pos > str) && (*pos != ')') && (*pos != '('))
175			pos--;
176
177		if (*pos == ')')
178			stacked++;
179		else if (*pos == '(')
180			stacked--;
181		else
182			return NULL;
183
184	} while (stacked > 0);
185
186	return (stacked == 0) ? pos : NULL;
187}
188
189static int
190parse_int(char **str, long *ret)
191{
192	char *end;
193	long n = strtol(*str, &end, 0);
194	if (end == *str) {
195		report_error(filename, line_no, "bad number");
196		return -1;
197	}
198
199	*str = end;
200	if (ret != NULL)
201		*ret = n;
202	return 0;
203}
204
205static int
206check_nonnegative(long l)
207{
208	if (l < 0) {
209		report_error(filename, line_no,
210			     "expected non-negative value, got %ld", l);
211		return -1;
212	}
213	return 0;
214}
215
216static int
217check_int(long l)
218{
219	int i = l;
220	if ((long)i != l) {
221		report_error(filename, line_no,
222			     "Number too large: %ld", l);
223		return -1;
224	}
225	return 0;
226}
227
228static int
229parse_char(char **str, char expected)
230{
231	if (**str != expected) {
232		report_error(filename, line_no,
233			     "expected '%c', got '%c'", expected, **str);
234		return -1;
235	}
236
237	++*str;
238	return 0;
239}
240
241static struct expr_node *parse_argnum(char **str, int zero);
242
243static struct expr_node *
244parse_zero(char **str, struct expr_node *ret)
245{
246	eat_spaces(str);
247	if (**str == '(') {
248		++*str;
249		struct expr_node *arg = parse_argnum(str, 0);
250		if (arg == NULL)
251			return NULL;
252		if (parse_char(str, ')') < 0) {
253		fail:
254			expr_destroy(arg);
255			free(arg);
256			return NULL;
257		}
258
259		struct expr_node *ret = build_zero_w_arg(arg, 1);
260		if (ret == NULL)
261			goto fail;
262		return ret;
263
264	} else {
265		return expr_node_zero();
266	}
267}
268
269static int
270wrap_in_zero(struct expr_node **nodep)
271{
272	struct expr_node *n = build_zero_w_arg(*nodep, 1);
273	if (n == NULL)
274		return -1;
275	*nodep = n;
276	return 0;
277}
278
279/*
280 * Input:
281 *  argN   : The value of argument #N, counting from 1
282 *  eltN   : The value of element #N of the containing structure
283 *  retval : The return value
284 *  N      : The numeric value N
285 */
286static struct expr_node *
287parse_argnum(char **str, int zero)
288{
289	struct expr_node *expr = malloc(sizeof(*expr));
290	if (expr == NULL)
291		return NULL;
292
293	if (isdigit(**str)) {
294		long l;
295		if (parse_int(str, &l) < 0
296		    || check_nonnegative(l) < 0
297		    || check_int(l) < 0)
298			goto fail;
299
300		expr_init_const_word(expr, l, type_get_simple(ARGTYPE_LONG), 0);
301
302		if (zero && wrap_in_zero(&expr) < 0)
303			goto fail;
304
305		return expr;
306
307	} else {
308		char *name = parse_ident(str);
309		if (name == NULL)
310			goto fail;
311
312		int is_arg = strncmp(name, "arg", 3) == 0;
313		int is_elt = !is_arg && strncmp(name, "elt", 3) == 0;
314		if (is_arg || is_elt) {
315			long l;
316			name += 3;
317			if (parse_int(&name, &l) < 0
318			    || check_int(l) < 0)
319				goto fail;
320
321			if (is_arg) {
322				expr_init_argno(expr, l - 1);
323			} else {
324				struct expr_node *e_up = malloc(sizeof(*e_up));
325				struct expr_node *e_ix = malloc(sizeof(*e_ix));
326				if (e_up == NULL || e_ix == NULL) {
327					free(e_up);
328					free(e_ix);
329					goto fail;
330				}
331
332				expr_init_up(e_up, expr_self(), 0);
333				struct arg_type_info *ti
334					= type_get_simple(ARGTYPE_LONG);
335				expr_init_const_word(e_ix, l - 1, ti, 0);
336				expr_init_index(expr, e_up, 1, e_ix, 1);
337			}
338
339		} else if (strcmp(name, "retval") == 0) {
340			expr_init_named(expr, "retval", 0);
341
342		} else if (strcmp(name, "zero") == 0) {
343			struct expr_node *ret = parse_zero(str, expr);
344			if (ret == NULL)
345				goto fail;
346			return ret;
347
348		} else {
349			report_error(filename, line_no,
350				     "Unknown length specifier: '%s'", name);
351			goto fail;
352		}
353
354		if (zero && wrap_in_zero(&expr) < 0)
355			goto fail;
356
357		return expr;
358	}
359
360fail:
361	free(expr);
362	return NULL;
363}
364
365struct typedef_node_t {
366	char *name;
367	struct arg_type_info *info;
368	struct typedef_node_t *next;
369} *typedefs = NULL;
370
371static struct arg_type_info *
372lookup_typedef(char **str) {
373	struct typedef_node_t *node;
374	char *end = *str;
375	while (*end && (isalnum(*end) || *end == '_'))
376		++end;
377	if (end == *str)
378		return NULL;
379
380	for (node = typedefs; node != NULL; node = node->next) {
381		if (strncmp(*str, node->name, end - *str) == 0) {
382			(*str) += strlen(node->name);
383			return node->info;
384		}
385	}
386
387	return NULL;
388}
389
390static struct typedef_node_t *
391insert_typedef(char *name, struct arg_type_info *info, int own_type)
392{
393	struct typedef_node_t *binding = malloc(sizeof(*binding));
394	binding->name = name;
395	binding->info = info;
396	binding->next = typedefs;
397	typedefs = binding;
398	return binding;
399}
400
401static void
402parse_typedef(char **str) {
403	char *name;
404	struct arg_type_info *info;
405
406	(*str) += strlen("typedef");
407	eat_spaces(str);
408
409	// Grab out the name of the type
410	name = parse_ident(str);
411
412	// Skip = sign
413	eat_spaces(str);
414	if (parse_char(str, '=') < 0)
415		return;
416	eat_spaces(str);
417
418	// Parse the type
419	info = parse_type(str);
420
421	insert_typedef(name, info, 0);
422}
423
424/* Syntax: struct ( type,type,type,... ) */
425static int
426parse_struct(char **str, struct arg_type_info *info)
427{
428	eat_spaces(str);
429	if (parse_char(str, '(') < 0)
430		return -1;
431
432	eat_spaces(str); // Empty arg list with whitespace inside
433
434	type_init_struct(info);
435
436	while (1) {
437		eat_spaces(str);
438		if (**str == 0 || **str == ')') {
439			parse_char(str, ')');
440			return 0;
441		}
442
443		/* Field delimiter.  */
444		if (type_struct_size(info) > 0)
445			parse_char(str, ',');
446
447		eat_spaces(str);
448		int own = 0;
449		struct arg_type_info *field = parse_type(str);
450		if (field == NULL || type_struct_add(info, field, own)) {
451			type_destroy(info);
452			return -1;
453		}
454	}
455}
456
457/* Syntax: enum ( keyname=value,keyname=value,... ) */
458static int
459parse_enum(char **str, struct arg_type_info *info)
460{
461	struct enum_opt {
462		char *key;
463		int value;
464		struct enum_opt *next;
465	};
466	struct enum_opt *list = NULL;
467	struct enum_opt *p;
468	int entries = 0;
469	int ii;
470
471	eat_spaces(str);
472	(*str)++;		// Get past open paren
473	eat_spaces(str);
474
475	int last_val = 0;
476	while (**str && **str != ')') {
477		p = (struct enum_opt *) malloc(sizeof(*p));
478		eat_spaces(str);
479		char *key = parse_ident(str);
480		if (key == NULL) {
481		err:
482			free(key);
483			return -1;
484		}
485
486		if (**str == '=') {
487			++*str;
488			eat_spaces(str);
489			long l;
490			if (parse_int(str, &l) < 0 || check_int(l) < 0)
491				goto err;
492			last_val = l;
493
494		} else {
495			last_val++;
496		}
497
498		p->key = key;
499		p->value = last_val;
500		p->next = list;
501		list = p;
502		++entries;
503
504		// Skip comma
505		eat_spaces(str);
506		if (**str == ',') {
507			(*str)++;
508			eat_spaces(str);
509		}
510	}
511
512	info->u.enum_info.entries = entries;
513	info->u.enum_info.keys = (char **) malloc(entries * sizeof(char *));
514	info->u.enum_info.values = (int *) malloc(entries * sizeof(int));
515	for (ii = 0, p = NULL; list; ++ii, list = list->next) {
516		if (p != NULL)
517			free(p);
518		info->u.enum_info.keys[ii] = list->key;
519		info->u.enum_info.values[ii] = list->value;
520		p = list;
521	}
522	if (p != NULL)
523		free(p);
524
525	return 0;
526}
527
528static struct arg_type_info *
529parse_nonpointer_type(char **str) {
530	struct arg_type_info *simple;
531	struct arg_type_info *info;
532
533	if (strncmp(*str, "typedef", 7) == 0) {
534		parse_typedef(str);
535		return lookup_prototype(ARGTYPE_UNKNOWN);
536	}
537
538	simple = str2type(str);
539	if (simple->type == ARGTYPE_UNKNOWN) {
540		info = lookup_typedef(str);
541		if (info)
542			return info;
543		else
544			return simple;		// UNKNOWN
545	}
546
547	info = malloc(sizeof(*info));
548	info->type = simple->type;
549
550	/* Code to parse parameterized types will go into the following
551	   switch statement. */
552
553	int (*parser) (char **, struct arg_type_info *) = NULL;
554	switch (info->type) {
555
556	/* Syntax: array ( type, N|argN ) */
557	case ARGTYPE_ARRAY:
558		(*str)++;		// Get past open paren
559		eat_spaces(str);
560		if ((info->u.array_info.elt_type = parse_type(str)) == NULL)
561			return NULL;
562		(*str)++;		// Get past comma
563		eat_spaces(str);
564		info->u.array_info.length = parse_argnum(str, 0);
565		(*str)++;		// Get past close paren
566		return info;
567
568	case ARGTYPE_ENUM:
569		parser = parse_enum;
570		break;
571
572	case ARGTYPE_STRING:
573		if (!isdigit(**str) && **str != '[') {
574			/* Oops, was just a simple string after all */
575			free(info);
576			return simple;
577		}
578
579		info->type = ARGTYPE_STRING_N;
580
581		/* Backwards compatibility for string0, string1, ... */
582		if (isdigit(**str)) {
583			info->u.string_n_info.length = parse_argnum(str, 1);
584			return info;
585		}
586
587		(*str)++;		// Skip past opening [
588		eat_spaces(str);
589		info->u.string_n_info.length = parse_argnum(str, 1);
590		eat_spaces(str);
591		(*str)++;		// Skip past closing ]
592		return info;
593
594	// Syntax: struct ( type,type,type,... )
595	case ARGTYPE_STRUCT:
596		parser = parse_struct;
597		break;
598
599	default:
600		if (info->type == ARGTYPE_UNKNOWN) {
601			output_line(0, "Syntax error in `%s', line %d: "
602					"Unknown type encountered",
603					filename, line_no);
604			free(info);
605			error_count++;
606			return NULL;
607		} else {
608			return info;
609		}
610	}
611
612	assert(parser != NULL);
613	if (parser(str, info) < 0) {
614		free(info);
615		return NULL;
616	}
617
618	return info;
619}
620
621static struct arg_type_info *
622parse_type(char **str) {
623	struct arg_type_info *info = parse_nonpointer_type(str);
624	while (**str == '*') {
625		struct arg_type_info *outer = malloc(sizeof(*info));
626		outer->type = ARGTYPE_POINTER;
627		outer->u.ptr_info.info = info;
628		(*str)++;
629		info = outer;
630	}
631	return info;
632}
633
634static int
635add_param(Function *fun, size_t *allocdp)
636{
637	size_t allocd = *allocdp;
638	/* XXX +1 is for the extra_param handling hack.  */
639	if ((fun->num_params + 1) >= allocd) {
640		allocd = allocd > 0 ? 2 * allocd : 8;
641		void *na = realloc(fun->params, sizeof(*fun->params) * allocd);
642		if (na == NULL)
643			return -1;
644
645		fun->params = na;
646		*allocdp = allocd;
647	}
648	return 0;
649}
650
651static Function *
652process_line(char *buf) {
653	char *str = buf;
654	char *tmp;
655
656	line_no++;
657	debug(3, "Reading line %d of `%s'", line_no, filename);
658	eat_spaces(&str);
659
660	/* A comment or empty line.  */
661	if (*str == ';' || *str == 0)
662		return NULL;
663
664	if (strncmp(str, "typedef", 7) == 0) {
665		parse_typedef(&str);
666		return NULL;
667	}
668
669	Function *fun = calloc(1, sizeof(*fun));
670	if (fun == NULL) {
671		report_error(filename, line_no,
672			     "alloc function: %s", strerror(errno));
673		return NULL;
674	}
675
676	fun->return_info = parse_type(&str);
677	if (fun->return_info == NULL
678	    || fun->return_info->type == ARGTYPE_UNKNOWN) {
679	err:
680		debug(3, " Skipping line %d", line_no);
681		return NULL;
682	}
683	debug(4, " return_type = %d", fun->return_info->type);
684
685	eat_spaces(&str);
686	tmp = start_of_arg_sig(str);
687	if (tmp == NULL) {
688		report_error(filename, line_no, "syntax error");
689		goto err;
690	}
691	*tmp = '\0';
692	fun->name = strdup(str);
693	str = tmp + 1;
694	debug(3, " name = %s", fun->name);
695
696	size_t allocd = 0;
697	fun->num_params = 0;
698	struct param *extra_param = NULL;
699
700	int have_stop = 0;
701
702	while (1) {
703		eat_spaces(&str);
704		if (*str == ')')
705			break;
706
707		if (str[0] == '+') {
708			if (have_stop == 0) {
709				if (add_param(fun, &allocd) < 0)
710					goto add_err;
711				param_init_stop
712					(&fun->params[fun->num_params++]);
713				have_stop = 1;
714			}
715			str++;
716		}
717
718		if (add_param(fun, &allocd) < 0) {
719		add_err:
720			report_error(filename, line_no, "(re)alloc params: %s",
721				     strerror(errno));
722			goto err;
723		}
724
725		struct arg_type_info *type = parse_type(&str);
726		if (type == NULL) {
727			report_error(filename, line_no,
728				     "unknown argument type");
729			goto err;
730		}
731
732		/* XXX We used to allow void parameter as a synonym to
733		 * an argument that shouldn't be displayed.  We may
734		 * wish to re-introduce this when lenses are
735		 * implemented, as a synonym, but backends generally
736		 * need to know the type, so disallow bare void for
737		 * now.  */
738		if (type->type == ARGTYPE_VOID) {
739			report_warning(filename, line_no,
740				       "void parameter assumed to be 'int'");
741			type = type_get_simple(ARGTYPE_INT);
742		}
743
744		param_init_type(&fun->params[fun->num_params++], type, 0);
745
746		eat_spaces(&str);
747		if (*str == ',') {
748			str++;
749			continue;
750		} else if (*str == ')') {
751			continue;
752		} else {
753			if (str[strlen(str) - 1] == '\n')
754				str[strlen(str) - 1] = '\0';
755			report_error(filename, line_no,
756				     "syntax error around \"%s\"", str);
757			goto err;
758		}
759	}
760
761	if (extra_param != NULL) {
762		assert(fun->num_params < allocd);
763		memcpy(&fun->params[fun->num_params++], extra_param,
764		       sizeof(*extra_param));
765        }
766
767	return fun;
768}
769
770void
771init_global_config(void)
772{
773	struct arg_type_info *info = malloc(2 * sizeof(*info));
774	if (info == NULL)
775		error(1, errno, "malloc in init_global_config");
776
777	memset(info, 0, 2 * sizeof(*info));
778	info[0].type = ARGTYPE_POINTER;
779	info[0].u.ptr_info.info = &info[1];
780	info[1].type = ARGTYPE_VOID;
781
782	insert_typedef(strdup("addr"), info, 0);
783	insert_typedef(strdup("file"), info, 1);
784}
785
786void
787read_config_file(char *file) {
788	FILE *stream;
789	char buf[1024];
790
791	filename = file;
792	stream = fopen(filename, "r");
793	if (!stream) {
794		return;
795	}
796
797	debug(1, "Reading config file `%s'...", filename);
798
799	line_no = 0;
800	while (fgets(buf, 1024, stream)) {
801		Function *tmp;
802
803		error_count = 0;
804		tmp = process_line(buf);
805
806		if (tmp) {
807			debug(2, "New function: `%s'", tmp->name);
808			tmp->next = list_of_functions;
809			list_of_functions = tmp;
810		}
811	}
812	fclose(stream);
813}
814