1f08c3bdfSopenharmony_ci/*
2f08c3bdfSopenharmony_ci * Example trivial client program that uses the sparse library
3f08c3bdfSopenharmony_ci * to tokenize, preprocess and parse a C file, and prints out
4f08c3bdfSopenharmony_ci * the results.
5f08c3bdfSopenharmony_ci *
6f08c3bdfSopenharmony_ci * Copyright (C) 2003 Transmeta Corp.
7f08c3bdfSopenharmony_ci *               2003-2004 Linus Torvalds
8f08c3bdfSopenharmony_ci *
9f08c3bdfSopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a copy
10f08c3bdfSopenharmony_ci * of this software and associated documentation files (the "Software"), to deal
11f08c3bdfSopenharmony_ci * in the Software without restriction, including without limitation the rights
12f08c3bdfSopenharmony_ci * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13f08c3bdfSopenharmony_ci * copies of the Software, and to permit persons to whom the Software is
14f08c3bdfSopenharmony_ci * furnished to do so, subject to the following conditions:
15f08c3bdfSopenharmony_ci *
16f08c3bdfSopenharmony_ci * The above copyright notice and this permission notice shall be included in
17f08c3bdfSopenharmony_ci * all copies or substantial portions of the Software.
18f08c3bdfSopenharmony_ci *
19f08c3bdfSopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20f08c3bdfSopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21f08c3bdfSopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22f08c3bdfSopenharmony_ci * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23f08c3bdfSopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24f08c3bdfSopenharmony_ci * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25f08c3bdfSopenharmony_ci * THE SOFTWARE.
26f08c3bdfSopenharmony_ci */
27f08c3bdfSopenharmony_ci#include <stdarg.h>
28f08c3bdfSopenharmony_ci#include <stdlib.h>
29f08c3bdfSopenharmony_ci#include <stdio.h>
30f08c3bdfSopenharmony_ci#include <string.h>
31f08c3bdfSopenharmony_ci#include <ctype.h>
32f08c3bdfSopenharmony_ci#include <unistd.h>
33f08c3bdfSopenharmony_ci#include <fcntl.h>
34f08c3bdfSopenharmony_ci
35f08c3bdfSopenharmony_ci#include "lib.h"
36f08c3bdfSopenharmony_ci#include "allocate.h"
37f08c3bdfSopenharmony_ci#include "token.h"
38f08c3bdfSopenharmony_ci#include "parse.h"
39f08c3bdfSopenharmony_ci#include "symbol.h"
40f08c3bdfSopenharmony_ci#include "expression.h"
41f08c3bdfSopenharmony_ci#include "linearize.h"
42f08c3bdfSopenharmony_ci
43f08c3bdfSopenharmony_cistatic int context_increase(struct basic_block *bb, int entry)
44f08c3bdfSopenharmony_ci{
45f08c3bdfSopenharmony_ci	int sum = 0;
46f08c3bdfSopenharmony_ci	struct instruction *insn;
47f08c3bdfSopenharmony_ci
48f08c3bdfSopenharmony_ci	FOR_EACH_PTR(bb->insns, insn) {
49f08c3bdfSopenharmony_ci		int val;
50f08c3bdfSopenharmony_ci		if (!insn->bb)
51f08c3bdfSopenharmony_ci			continue;
52f08c3bdfSopenharmony_ci		if (insn->opcode != OP_CONTEXT)
53f08c3bdfSopenharmony_ci			continue;
54f08c3bdfSopenharmony_ci		val = insn->increment;
55f08c3bdfSopenharmony_ci		if (insn->check) {
56f08c3bdfSopenharmony_ci			int current = sum + entry;
57f08c3bdfSopenharmony_ci			if (!val) {
58f08c3bdfSopenharmony_ci				if (!current)
59f08c3bdfSopenharmony_ci					continue;
60f08c3bdfSopenharmony_ci			} else if (current >= val)
61f08c3bdfSopenharmony_ci				continue;
62f08c3bdfSopenharmony_ci			warning(insn->pos, "context check failure");
63f08c3bdfSopenharmony_ci			continue;
64f08c3bdfSopenharmony_ci		}
65f08c3bdfSopenharmony_ci		sum += val;
66f08c3bdfSopenharmony_ci	} END_FOR_EACH_PTR(insn);
67f08c3bdfSopenharmony_ci	return sum;
68f08c3bdfSopenharmony_ci}
69f08c3bdfSopenharmony_ci
70f08c3bdfSopenharmony_cistatic int imbalance(struct entrypoint *ep, struct basic_block *bb, int entry, int exit, const char *why)
71f08c3bdfSopenharmony_ci{
72f08c3bdfSopenharmony_ci	if (Wcontext) {
73f08c3bdfSopenharmony_ci		struct symbol *sym = ep->name;
74f08c3bdfSopenharmony_ci		warning(bb->pos, "context imbalance in '%s' - %s", show_ident(sym->ident), why);
75f08c3bdfSopenharmony_ci	}
76f08c3bdfSopenharmony_ci	return -1;
77f08c3bdfSopenharmony_ci}
78f08c3bdfSopenharmony_ci
79f08c3bdfSopenharmony_cistatic int check_bb_context(struct entrypoint *ep, struct basic_block *bb, int entry, int exit);
80f08c3bdfSopenharmony_ci
81f08c3bdfSopenharmony_cistatic int check_children(struct entrypoint *ep, struct basic_block *bb, int entry, int exit)
82f08c3bdfSopenharmony_ci{
83f08c3bdfSopenharmony_ci	struct instruction *insn;
84f08c3bdfSopenharmony_ci	struct basic_block *child;
85f08c3bdfSopenharmony_ci
86f08c3bdfSopenharmony_ci	insn = last_instruction(bb->insns);
87f08c3bdfSopenharmony_ci	if (!insn)
88f08c3bdfSopenharmony_ci		return 0;
89f08c3bdfSopenharmony_ci	if (insn->opcode == OP_RET)
90f08c3bdfSopenharmony_ci		return entry != exit ? imbalance(ep, bb, entry, exit, "wrong count at exit") : 0;
91f08c3bdfSopenharmony_ci
92f08c3bdfSopenharmony_ci	FOR_EACH_PTR(bb->children, child) {
93f08c3bdfSopenharmony_ci		if (check_bb_context(ep, child, entry, exit))
94f08c3bdfSopenharmony_ci			return -1;
95f08c3bdfSopenharmony_ci	} END_FOR_EACH_PTR(child);
96f08c3bdfSopenharmony_ci	return 0;
97f08c3bdfSopenharmony_ci}
98f08c3bdfSopenharmony_ci
99f08c3bdfSopenharmony_cistatic int check_bb_context(struct entrypoint *ep, struct basic_block *bb, int entry, int exit)
100f08c3bdfSopenharmony_ci{
101f08c3bdfSopenharmony_ci	if (!bb)
102f08c3bdfSopenharmony_ci		return 0;
103f08c3bdfSopenharmony_ci	if (bb->context == entry)
104f08c3bdfSopenharmony_ci		return 0;
105f08c3bdfSopenharmony_ci
106f08c3bdfSopenharmony_ci	/* Now that's not good.. */
107f08c3bdfSopenharmony_ci	if (bb->context >= 0)
108f08c3bdfSopenharmony_ci		return imbalance(ep, bb, entry, bb->context, "different lock contexts for basic block");
109f08c3bdfSopenharmony_ci
110f08c3bdfSopenharmony_ci	bb->context = entry;
111f08c3bdfSopenharmony_ci	entry += context_increase(bb, entry);
112f08c3bdfSopenharmony_ci	if (entry < 0)
113f08c3bdfSopenharmony_ci		return imbalance(ep, bb, entry, exit, "unexpected unlock");
114f08c3bdfSopenharmony_ci
115f08c3bdfSopenharmony_ci	return check_children(ep, bb, entry, exit);
116f08c3bdfSopenharmony_ci}
117f08c3bdfSopenharmony_ci
118f08c3bdfSopenharmony_cistatic void check_cast_instruction(struct instruction *insn)
119f08c3bdfSopenharmony_ci{
120f08c3bdfSopenharmony_ci	struct symbol *orig_type = insn->orig_type;
121f08c3bdfSopenharmony_ci	if (orig_type) {
122f08c3bdfSopenharmony_ci		int old = orig_type->bit_size;
123f08c3bdfSopenharmony_ci		int new = insn->size;
124f08c3bdfSopenharmony_ci		int oldsigned = (orig_type->ctype.modifiers & MOD_SIGNED) != 0;
125f08c3bdfSopenharmony_ci		int newsigned = insn->opcode == OP_SEXT;
126f08c3bdfSopenharmony_ci
127f08c3bdfSopenharmony_ci		if (new > old) {
128f08c3bdfSopenharmony_ci			if (oldsigned == newsigned)
129f08c3bdfSopenharmony_ci				return;
130f08c3bdfSopenharmony_ci			if (newsigned)
131f08c3bdfSopenharmony_ci				return;
132f08c3bdfSopenharmony_ci			warning(insn->pos, "cast loses sign");
133f08c3bdfSopenharmony_ci			return;
134f08c3bdfSopenharmony_ci		}
135f08c3bdfSopenharmony_ci		if (new < old) {
136f08c3bdfSopenharmony_ci			warning(insn->pos, "cast drops bits");
137f08c3bdfSopenharmony_ci			return;
138f08c3bdfSopenharmony_ci		}
139f08c3bdfSopenharmony_ci		if (oldsigned == newsigned) {
140f08c3bdfSopenharmony_ci			warning(insn->pos, "cast wasn't removed");
141f08c3bdfSopenharmony_ci			return;
142f08c3bdfSopenharmony_ci		}
143f08c3bdfSopenharmony_ci		warning(insn->pos, "cast changes sign");
144f08c3bdfSopenharmony_ci	}
145f08c3bdfSopenharmony_ci}
146f08c3bdfSopenharmony_ci
147f08c3bdfSopenharmony_cistatic void check_range_instruction(struct instruction *insn)
148f08c3bdfSopenharmony_ci{
149f08c3bdfSopenharmony_ci	warning(insn->pos, "value out of range");
150f08c3bdfSopenharmony_ci}
151f08c3bdfSopenharmony_ci
152f08c3bdfSopenharmony_cistatic void check_byte_count(struct instruction *insn, pseudo_t count)
153f08c3bdfSopenharmony_ci{
154f08c3bdfSopenharmony_ci	if (!count)
155f08c3bdfSopenharmony_ci		return;
156f08c3bdfSopenharmony_ci	if (count->type == PSEUDO_VAL) {
157f08c3bdfSopenharmony_ci		unsigned long long val = count->value;
158f08c3bdfSopenharmony_ci		if (Wmemcpy_max_count && val > fmemcpy_max_count)
159f08c3bdfSopenharmony_ci			warning(insn->pos, "%s with byte count of %llu",
160f08c3bdfSopenharmony_ci				show_ident(insn->func->sym->ident), val);
161f08c3bdfSopenharmony_ci		return;
162f08c3bdfSopenharmony_ci	}
163f08c3bdfSopenharmony_ci	/* OK, we could try to do the range analysis here */
164f08c3bdfSopenharmony_ci}
165f08c3bdfSopenharmony_ci
166f08c3bdfSopenharmony_cistatic void check_memset(struct instruction *insn)
167f08c3bdfSopenharmony_ci{
168f08c3bdfSopenharmony_ci	check_byte_count(insn, ptr_list_nth(insn->arguments, 3));
169f08c3bdfSopenharmony_ci}
170f08c3bdfSopenharmony_ci
171f08c3bdfSopenharmony_ci#define check_memcpy check_memset
172f08c3bdfSopenharmony_ci#define check_ctu check_memset
173f08c3bdfSopenharmony_ci#define check_cfu check_memset
174f08c3bdfSopenharmony_ci
175f08c3bdfSopenharmony_cistruct checkfn {
176f08c3bdfSopenharmony_ci	struct ident *id;
177f08c3bdfSopenharmony_ci	void (*check)(struct instruction *insn);
178f08c3bdfSopenharmony_ci};
179f08c3bdfSopenharmony_ci
180f08c3bdfSopenharmony_cistatic void check_call_instruction(struct instruction *insn)
181f08c3bdfSopenharmony_ci{
182f08c3bdfSopenharmony_ci	pseudo_t fn = insn->func;
183f08c3bdfSopenharmony_ci	struct ident *ident;
184f08c3bdfSopenharmony_ci	static const struct checkfn check_fn[] = {
185f08c3bdfSopenharmony_ci		{ &memset_ident, check_memset },
186f08c3bdfSopenharmony_ci		{ &memcpy_ident, check_memcpy },
187f08c3bdfSopenharmony_ci		{ &copy_to_user_ident, check_ctu },
188f08c3bdfSopenharmony_ci		{ &copy_from_user_ident, check_cfu },
189f08c3bdfSopenharmony_ci	};
190f08c3bdfSopenharmony_ci	int i;
191f08c3bdfSopenharmony_ci
192f08c3bdfSopenharmony_ci	if (fn->type != PSEUDO_SYM)
193f08c3bdfSopenharmony_ci		return;
194f08c3bdfSopenharmony_ci	ident = fn->sym->ident;
195f08c3bdfSopenharmony_ci	if (!ident)
196f08c3bdfSopenharmony_ci		return;
197f08c3bdfSopenharmony_ci	for (i = 0; i < ARRAY_SIZE(check_fn); i++) {
198f08c3bdfSopenharmony_ci		if (check_fn[i].id != ident)
199f08c3bdfSopenharmony_ci			continue;
200f08c3bdfSopenharmony_ci		check_fn[i].check(insn);
201f08c3bdfSopenharmony_ci		break;
202f08c3bdfSopenharmony_ci	}
203f08c3bdfSopenharmony_ci}
204f08c3bdfSopenharmony_ci
205f08c3bdfSopenharmony_cistatic void check_one_instruction(struct instruction *insn)
206f08c3bdfSopenharmony_ci{
207f08c3bdfSopenharmony_ci	switch (insn->opcode) {
208f08c3bdfSopenharmony_ci	case OP_SEXT: case OP_ZEXT:
209f08c3bdfSopenharmony_ci	case OP_TRUNC:
210f08c3bdfSopenharmony_ci		if (verbose)
211f08c3bdfSopenharmony_ci			check_cast_instruction(insn);
212f08c3bdfSopenharmony_ci		break;
213f08c3bdfSopenharmony_ci	case OP_RANGE:
214f08c3bdfSopenharmony_ci		check_range_instruction(insn);
215f08c3bdfSopenharmony_ci		break;
216f08c3bdfSopenharmony_ci	case OP_CALL:
217f08c3bdfSopenharmony_ci		check_call_instruction(insn);
218f08c3bdfSopenharmony_ci		break;
219f08c3bdfSopenharmony_ci	default:
220f08c3bdfSopenharmony_ci		break;
221f08c3bdfSopenharmony_ci	}
222f08c3bdfSopenharmony_ci}
223f08c3bdfSopenharmony_ci
224f08c3bdfSopenharmony_cistatic void check_bb_instructions(struct basic_block *bb)
225f08c3bdfSopenharmony_ci{
226f08c3bdfSopenharmony_ci	struct instruction *insn;
227f08c3bdfSopenharmony_ci	FOR_EACH_PTR(bb->insns, insn) {
228f08c3bdfSopenharmony_ci		if (!insn->bb)
229f08c3bdfSopenharmony_ci			continue;
230f08c3bdfSopenharmony_ci		check_one_instruction(insn);
231f08c3bdfSopenharmony_ci	} END_FOR_EACH_PTR(insn);
232f08c3bdfSopenharmony_ci}
233f08c3bdfSopenharmony_ci
234f08c3bdfSopenharmony_cistatic void check_instructions(struct entrypoint *ep)
235f08c3bdfSopenharmony_ci{
236f08c3bdfSopenharmony_ci	struct basic_block *bb;
237f08c3bdfSopenharmony_ci	FOR_EACH_PTR(ep->bbs, bb) {
238f08c3bdfSopenharmony_ci		bb->context = -1;
239f08c3bdfSopenharmony_ci		check_bb_instructions(bb);
240f08c3bdfSopenharmony_ci	} END_FOR_EACH_PTR(bb);
241f08c3bdfSopenharmony_ci}
242f08c3bdfSopenharmony_ci
243f08c3bdfSopenharmony_cistatic void check_context(struct entrypoint *ep)
244f08c3bdfSopenharmony_ci{
245f08c3bdfSopenharmony_ci	struct symbol *sym = ep->name;
246f08c3bdfSopenharmony_ci	struct context *context;
247f08c3bdfSopenharmony_ci	unsigned int in_context = 0, out_context = 0;
248f08c3bdfSopenharmony_ci
249f08c3bdfSopenharmony_ci	if (Wuninitialized && verbose && ep->entry->bb->needs) {
250f08c3bdfSopenharmony_ci		pseudo_t pseudo;
251f08c3bdfSopenharmony_ci		FOR_EACH_PTR(ep->entry->bb->needs, pseudo) {
252f08c3bdfSopenharmony_ci			if (pseudo->type != PSEUDO_ARG)
253f08c3bdfSopenharmony_ci				warning(sym->pos, "%s: possible uninitialized variable (%s)",
254f08c3bdfSopenharmony_ci					show_ident(sym->ident), show_pseudo(pseudo));
255f08c3bdfSopenharmony_ci		} END_FOR_EACH_PTR(pseudo);
256f08c3bdfSopenharmony_ci	}
257f08c3bdfSopenharmony_ci
258f08c3bdfSopenharmony_ci	check_instructions(ep);
259f08c3bdfSopenharmony_ci
260f08c3bdfSopenharmony_ci	FOR_EACH_PTR(sym->ctype.contexts, context) {
261f08c3bdfSopenharmony_ci		in_context += context->in;
262f08c3bdfSopenharmony_ci		out_context += context->out;
263f08c3bdfSopenharmony_ci	} END_FOR_EACH_PTR(context);
264f08c3bdfSopenharmony_ci	check_bb_context(ep, ep->entry->bb, in_context, out_context);
265f08c3bdfSopenharmony_ci}
266f08c3bdfSopenharmony_ci
267f08c3bdfSopenharmony_ci/* list_compound_symbol - symbol info for arrays, structures, unions */
268f08c3bdfSopenharmony_cistatic void list_compound_symbol(struct symbol *sym)
269f08c3bdfSopenharmony_ci{
270f08c3bdfSopenharmony_ci	struct symbol *base;
271f08c3bdfSopenharmony_ci
272f08c3bdfSopenharmony_ci	/* Only show symbols that have a positive size */
273f08c3bdfSopenharmony_ci	if (sym->bit_size <= 0)
274f08c3bdfSopenharmony_ci		return;
275f08c3bdfSopenharmony_ci	if (!sym->ctype.base_type)
276f08c3bdfSopenharmony_ci		return;
277f08c3bdfSopenharmony_ci	/* Don't show unnamed types */
278f08c3bdfSopenharmony_ci	if (!sym->ident)
279f08c3bdfSopenharmony_ci		return;
280f08c3bdfSopenharmony_ci
281f08c3bdfSopenharmony_ci	if (sym->type == SYM_NODE)
282f08c3bdfSopenharmony_ci		base = sym->ctype.base_type;
283f08c3bdfSopenharmony_ci	else
284f08c3bdfSopenharmony_ci		base = sym;
285f08c3bdfSopenharmony_ci	switch (base->type) {
286f08c3bdfSopenharmony_ci	case SYM_STRUCT: case SYM_UNION: case SYM_ARRAY:
287f08c3bdfSopenharmony_ci		break;
288f08c3bdfSopenharmony_ci	default:
289f08c3bdfSopenharmony_ci		return;
290f08c3bdfSopenharmony_ci	}
291f08c3bdfSopenharmony_ci
292f08c3bdfSopenharmony_ci	info(sym->pos, "%s: compound size %u, alignment %lu",
293f08c3bdfSopenharmony_ci		show_typename(sym),
294f08c3bdfSopenharmony_ci		bits_to_bytes(sym->bit_size),
295f08c3bdfSopenharmony_ci		sym->ctype.alignment);
296f08c3bdfSopenharmony_ci}
297f08c3bdfSopenharmony_ci
298f08c3bdfSopenharmony_cistatic void check_symbols(struct symbol_list *list)
299f08c3bdfSopenharmony_ci{
300f08c3bdfSopenharmony_ci	struct symbol *sym;
301f08c3bdfSopenharmony_ci
302f08c3bdfSopenharmony_ci	FOR_EACH_PTR(list, sym) {
303f08c3bdfSopenharmony_ci		struct entrypoint *ep;
304f08c3bdfSopenharmony_ci
305f08c3bdfSopenharmony_ci		expand_symbol(sym);
306f08c3bdfSopenharmony_ci		ep = linearize_symbol(sym);
307f08c3bdfSopenharmony_ci		if (ep && ep->entry) {
308f08c3bdfSopenharmony_ci			if (dbg_entry)
309f08c3bdfSopenharmony_ci				show_entry(ep);
310f08c3bdfSopenharmony_ci
311f08c3bdfSopenharmony_ci			check_context(ep);
312f08c3bdfSopenharmony_ci		}
313f08c3bdfSopenharmony_ci		if (dbg_compound)
314f08c3bdfSopenharmony_ci			list_compound_symbol(sym);
315f08c3bdfSopenharmony_ci	} END_FOR_EACH_PTR(sym);
316f08c3bdfSopenharmony_ci
317f08c3bdfSopenharmony_ci	if (Wsparse_error && die_if_error)
318f08c3bdfSopenharmony_ci		exit(1);
319f08c3bdfSopenharmony_ci}
320f08c3bdfSopenharmony_ci
321f08c3bdfSopenharmony_ciint main(int argc, char **argv)
322f08c3bdfSopenharmony_ci{
323f08c3bdfSopenharmony_ci	struct string_list *filelist = NULL;
324f08c3bdfSopenharmony_ci	char *file;
325f08c3bdfSopenharmony_ci
326f08c3bdfSopenharmony_ci	// by default ignore -o <file>
327f08c3bdfSopenharmony_ci	do_output = 0;
328f08c3bdfSopenharmony_ci
329f08c3bdfSopenharmony_ci	// Expand, linearize and show it.
330f08c3bdfSopenharmony_ci	check_symbols(sparse_initialize(argc, argv, &filelist));
331f08c3bdfSopenharmony_ci	FOR_EACH_PTR(filelist, file) {
332f08c3bdfSopenharmony_ci		check_symbols(sparse(file));
333f08c3bdfSopenharmony_ci	} END_FOR_EACH_PTR(file);
334f08c3bdfSopenharmony_ci
335f08c3bdfSopenharmony_ci	report_stats();
336f08c3bdfSopenharmony_ci	return 0;
337f08c3bdfSopenharmony_ci}
338