xref: /third_party/ltp/tools/sparse/sparse-src/lib.c (revision f08c3bdf)
1/*
2 * 'sparse' library helper routines.
3 *
4 * Copyright (C) 2003 Transmeta Corp.
5 *               2003-2004 Linus Torvalds
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 */
25#include <ctype.h>
26#include <errno.h>
27#include <fcntl.h>
28#include <stdarg.h>
29#include <stddef.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <unistd.h>
34#include <assert.h>
35
36#include <sys/types.h>
37
38#include "lib.h"
39#include "allocate.h"
40#include "token.h"
41#include "parse.h"
42#include "symbol.h"
43#include "expression.h"
44#include "evaluate.h"
45#include "scope.h"
46#include "linearize.h"
47#include "target.h"
48#include "machine.h"
49#include "bits.h"
50
51static int prettify(const char **fnamep)
52{
53	const char *name = *fnamep;
54	int len = strlen(name);
55
56	if (len > 2 && !memcmp(name, "./", 2)) {
57		name += 2;
58		len -= 2;
59	}
60
61	*fnamep = name;
62	return len;
63}
64
65static const char *show_include_chain(int stream, const char *base)
66{
67	static char buffer[200];
68	int len = 0;
69
70	while ((stream = stream_prev(stream)) >= 0) {
71		const char *p = stream_name(stream);
72		int pretty_len;
73
74		if (p == base)
75			break;
76
77		pretty_len = prettify(&p);
78		if (pretty_len <= 0)
79			break;
80
81		/*
82		 * At worst, we'll need " (through %s, ...)" in addition to the
83		 * new filename
84		 */
85		if (pretty_len + len + 20 > sizeof(buffer)) {
86			if (!len)
87				return "";
88			memcpy(buffer+len, ", ...", 5);
89			len += 5;
90			break;
91		}
92
93		if (!len) {
94			memcpy(buffer, " (through ", 10);
95			len = 10;
96		} else {
97			buffer[len++] = ',';
98			buffer[len++] = ' ';
99		}
100
101		memcpy(buffer+len, p, pretty_len);
102		len += pretty_len;
103	}
104	if (!len)
105		return "";
106
107	buffer[len] = ')';
108	buffer[len+1] = 0;
109	return buffer;
110}
111
112static const char *show_stream_name(struct position pos)
113{
114	const char *name = stream_name(pos.stream);
115	static const char *last;
116
117	if (name == base_filename)
118		return name;
119	if (name == last)
120		return name;
121	last = name;
122
123	fprintf(stderr, "%s: note: in included file%s:\n",
124		base_filename,
125		show_include_chain(pos.stream, base_filename));
126	return name;
127}
128
129static void do_warn(const char *type, struct position pos, const char * fmt, va_list args)
130{
131	static char buffer[512];
132
133	/* Shut up warnings if position is bad_token.pos */
134	if (pos.type == TOKEN_BAD)
135		return;
136
137	vsprintf(buffer, fmt, args);
138
139	fflush(stdout);
140	fprintf(stderr, "%s:%d:%d: %s%s%s\n",
141		show_stream_name(pos), pos.line, pos.pos,
142		diag_prefix, type, buffer);
143}
144
145static int show_info = 1;
146
147void info(struct position pos, const char * fmt, ...)
148{
149	va_list args;
150
151	if (!show_info)
152		return;
153	va_start(args, fmt);
154	do_warn("", pos, fmt, args);
155	va_end(args);
156}
157
158static void do_error(struct position pos, const char * fmt, va_list args)
159{
160	static int errors = 0;
161        die_if_error = 1;
162	show_info = 1;
163	/* Shut up warnings if position is bad_token.pos */
164	if (pos.type == TOKEN_BAD)
165		return;
166	/* Shut up warnings after an error */
167	has_error |= ERROR_CURR_PHASE;
168	if (errors > fmax_errors) {
169		static int once = 0;
170		show_info = 0;
171		if (once)
172			return;
173		fmt = "too many errors";
174		once = 1;
175	}
176
177	do_warn("error: ", pos, fmt, args);
178	errors++;
179}
180
181void warning(struct position pos, const char * fmt, ...)
182{
183	va_list args;
184
185	if (Wsparse_error) {
186		va_start(args, fmt);
187		do_error(pos, fmt, args);
188		va_end(args);
189		return;
190	}
191
192	if (!fmax_warnings || has_error) {
193		show_info = 0;
194		return;
195	}
196
197	if (!--fmax_warnings) {
198		show_info = 0;
199		fmt = "too many warnings";
200	}
201
202	va_start(args, fmt);
203	do_warn("warning: ", pos, fmt, args);
204	va_end(args);
205}
206
207void sparse_error(struct position pos, const char * fmt, ...)
208{
209	va_list args;
210	va_start(args, fmt);
211	do_error(pos, fmt, args);
212	va_end(args);
213}
214
215void expression_error(struct expression *expr, const char *fmt, ...)
216{
217	va_list args;
218	va_start(args, fmt);
219	do_error(expr->pos, fmt, args);
220	va_end(args);
221	expr->ctype = &bad_ctype;
222}
223
224NORETURN_ATTR
225void error_die(struct position pos, const char * fmt, ...)
226{
227	va_list args;
228	va_start(args, fmt);
229	do_warn("error: ", pos, fmt, args);
230	va_end(args);
231	exit(1);
232}
233
234NORETURN_ATTR
235void die(const char *fmt, ...)
236{
237	va_list args;
238	static char buffer[512];
239
240	va_start(args, fmt);
241	vsnprintf(buffer, sizeof(buffer), fmt, args);
242	va_end(args);
243
244	fprintf(stderr, "%s%s\n", diag_prefix, buffer);
245	exit(1);
246}
247
248////////////////////////////////////////////////////////////////////////////////
249
250static struct token *pre_buffer_begin = NULL;
251static struct token **pre_buffer_next = &pre_buffer_begin;
252
253void add_pre_buffer(const char *fmt, ...)
254{
255	va_list args;
256	unsigned int size;
257	struct token *begin, *end;
258	char buffer[4096];
259
260	va_start(args, fmt);
261	size = vsnprintf(buffer, sizeof(buffer), fmt, args);
262	va_end(args);
263	begin = tokenize_buffer(buffer, size, &end);
264	*pre_buffer_next = begin;
265	pre_buffer_next = &end->next;
266}
267
268static void create_builtin_stream(void)
269{
270	// Temporary hack
271	add_pre_buffer("#define _Pragma(x)\n");
272
273	/* add the multiarch include directories, if any */
274	if (multiarch_dir && *multiarch_dir) {
275		add_pre_buffer("#add_system \"/usr/include/%s\"\n", multiarch_dir);
276		add_pre_buffer("#add_system \"/usr/local/include/%s\"\n", multiarch_dir);
277	}
278
279	/* We add compiler headers path here because we have to parse
280	 * the arguments to get it, falling back to default. */
281	add_pre_buffer("#add_system \"%s/include\"\n", gcc_base_dir);
282	add_pre_buffer("#add_system \"%s/include-fixed\"\n", gcc_base_dir);
283
284	add_pre_buffer("#define __builtin_stdarg_start(a,b) ((a) = (__builtin_va_list)(&(b)))\n");
285	add_pre_buffer("#define __builtin_va_start(a,b) ((a) = (__builtin_va_list)(&(b)))\n");
286	add_pre_buffer("#define __builtin_ms_va_start(a,b) ((a) = (__builtin_ms_va_list)(&(b)))\n");
287	add_pre_buffer("#define __builtin_va_arg(arg,type)  ({ type __va_arg_ret = *(type *)(arg); arg += sizeof(type); __va_arg_ret; })\n");
288	add_pre_buffer("#define __builtin_va_alist (*(void *)0)\n");
289	add_pre_buffer("#define __builtin_va_arg_incr(x) ((x) + 1)\n");
290	add_pre_buffer("#define __builtin_va_copy(dest, src) ({ dest = src; (void)0; })\n");
291	add_pre_buffer("#define __builtin_ms_va_copy(dest, src) ({ dest = src; (void)0; })\n");
292	add_pre_buffer("#define __builtin_va_end(arg)\n");
293	add_pre_buffer("#define __builtin_ms_va_end(arg)\n");
294	add_pre_buffer("#define __builtin_va_arg_pack()\n");
295}
296
297static struct symbol_list *sparse_tokenstream(struct token *token)
298{
299	int builtin = token && !token->pos.stream;
300
301	// Preprocess the stream
302	token = preprocess(token);
303
304	if (dump_macro_defs || dump_macros_only) {
305		if (!builtin)
306			dump_macro_definitions();
307		if (dump_macros_only)
308			return NULL;
309	}
310
311	if (preprocess_only) {
312		while (!eof_token(token)) {
313			int prec = 1;
314			struct token *next = token->next;
315			const char *separator = "";
316			if (next->pos.whitespace)
317				separator = " ";
318			if (next->pos.newline) {
319				separator = "\n\t\t\t\t\t";
320				prec = next->pos.pos;
321				if (prec > 4)
322					prec = 4;
323			}
324			printf("%s%.*s", show_token(token), prec, separator);
325			token = next;
326		}
327		putchar('\n');
328
329		return NULL;
330	}
331
332	// Parse the resulting C code
333	while (!eof_token(token))
334		token = external_declaration(token, &translation_unit_used_list, NULL);
335	return translation_unit_used_list;
336}
337
338static struct symbol_list *sparse_file(const char *filename)
339{
340	int fd;
341	struct token *token;
342
343	if (strcmp(filename, "-") == 0) {
344		fd = 0;
345	} else {
346		fd = open(filename, O_RDONLY);
347		if (fd < 0)
348			die("No such file: %s", filename);
349	}
350	base_filename = filename;
351
352	// Tokenize the input stream
353	token = tokenize(NULL, filename, fd, NULL, includepath);
354	close(fd);
355
356	return sparse_tokenstream(token);
357}
358
359/*
360 * This handles the "-include" directive etc: we're in global
361 * scope, and all types/macros etc will affect all the following
362 * files.
363 *
364 * NOTE NOTE NOTE! "#undef" of anything in this stage will
365 * affect all subsequent files too, i.e. we can have non-local
366 * behaviour between files!
367 */
368static struct symbol_list *sparse_initial(void)
369{
370	int i;
371
372	// Prepend any "include" file to the stream.
373	// We're in global scope, it will affect all files!
374	for (i = 0; i < cmdline_include_nr; i++)
375		add_pre_buffer("#argv_include \"%s\"\n", cmdline_include[i]);
376
377	return sparse_tokenstream(pre_buffer_begin);
378}
379
380struct symbol_list *sparse_initialize(int argc, char **argv, struct string_list **filelist)
381{
382	char **args;
383	struct symbol_list *list;
384
385	base_filename = "command-line";
386
387	// Initialize symbol stream first, so that we can add defines etc
388	init_symbols();
389
390	// initialize the default target to the native 'machine'
391	target_config(MACH_NATIVE);
392
393	args = argv;
394	for (;;) {
395		char *arg = *++args;
396		if (!arg)
397			break;
398
399		if (arg[0] == '-' && arg[1]) {
400			args = handle_switch(arg+1, args);
401			continue;
402		}
403		add_ptr_list(filelist, arg);
404	}
405	handle_switch_finalize();
406
407	// Redirect stdout if needed
408	if (dump_macro_defs || preprocess_only)
409		do_output = 1;
410	if (do_output && outfile && strcmp(outfile, "-")) {
411		if (!freopen(outfile, "w", stdout))
412			die("error: cannot open %s: %s", outfile, strerror(errno));
413	}
414
415	if (fdump_ir == 0)
416		fdump_ir = PASS_FINAL;
417
418	list = NULL;
419	if (filelist) {
420		// Initialize type system
421		target_init();
422		init_ctype();
423
424		predefined_macros();
425		create_builtin_stream();
426		init_builtins(0);
427
428		list = sparse_initial();
429
430		/*
431		 * Protect the initial token allocations, since
432		 * they need to survive all the others
433		 */
434		protect_token_alloc();
435	}
436	/*
437	 * Evaluate the complete symbol list
438	 * Note: This is not needed for normal cases.
439	 *	 These symbols should only be predefined defines and
440	 *	 declaratons which will be evaluated later, when needed.
441	 *	 This is also the case when a file is directly included via
442	 *	 '-include <file>' on the command line *AND* the file only
443	 *	 contains defines, declarations and inline definitions.
444	 *	 However, in the rare cases where the given file should
445	 *	 contain some definitions, these will never be evaluated
446	 *	 and thus won't be able to be linearized correctly.
447	 *	 Hence the evaluate_symbol_list() here under.
448	 */
449	evaluate_symbol_list(list);
450	return list;
451}
452
453struct symbol_list * sparse_keep_tokens(char *filename)
454{
455	struct symbol_list *res;
456
457	/* Clear previous symbol list */
458	translation_unit_used_list = NULL;
459
460	new_file_scope();
461	res = sparse_file(filename);
462
463	/* And return it */
464	return res;
465}
466
467
468struct symbol_list * __sparse(char *filename)
469{
470	struct symbol_list *res;
471
472	res = sparse_keep_tokens(filename);
473
474	/* Drop the tokens for this file after parsing */
475	clear_token_alloc();
476
477	/* And return it */
478	return res;
479}
480
481struct symbol_list * sparse(char *filename)
482{
483	struct symbol_list *res = __sparse(filename);
484
485	if (has_error & ERROR_CURR_PHASE)
486		has_error = ERROR_PREV_PHASE;
487	/* Evaluate the complete symbol list */
488	evaluate_symbol_list(res);
489
490	return res;
491}
492