• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
prettify(const char ** fnamep)51 static 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 
show_include_chain(int stream,const char * base)65 static 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 
show_stream_name(struct position pos)112 static 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 
do_warn(const char * type,struct position pos,const char * fmt,va_list args)129 static 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 
145 static int show_info = 1;
146 
info(struct position pos,const char * fmt,...)147 void 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 
do_error(struct position pos,const char * fmt,va_list args)158 static 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 
warning(struct position pos,const char * fmt,...)181 void 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 
sparse_error(struct position pos,const char * fmt,...)207 void 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 
expression_error(struct expression * expr,const char * fmt,...)215 void 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 
224 NORETURN_ATTR
error_die(struct position pos,const char * fmt,...)225 void 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 
234 NORETURN_ATTR
die(const char * fmt,...)235 void 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 
250 static struct token *pre_buffer_begin = NULL;
251 static struct token **pre_buffer_next = &pre_buffer_begin;
252 
add_pre_buffer(const char * fmt,...)253 void 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 
create_builtin_stream(void)268 static 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 
sparse_tokenstream(struct token * token)297 static 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 
sparse_file(const char * filename)338 static 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  */
sparse_initial(void)368 static 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 
sparse_initialize(int argc,char ** argv,struct string_list ** filelist)380 struct 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 
sparse_keep_tokens(char * filename)453 struct 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 
__sparse(char * filename)468 struct 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 
sparse(char * filename)481 struct 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