2 * Example trivial client program that uses the sparse library
3 * to tokenize, preprocess and parse a C file, and prints out
6 * Copyright (C) 2003 Transmeta Corp.
7 * 2003-2004 Linus Torvalds
9 * Licensed under the Open Software License version 1.1
24 #include "expression.h"
25 #include "linearize.h"
27 struct context_check
{
32 DECLARE_ALLOCATOR(context_check
);
33 DECLARE_PTR_LIST(context_check_list
, struct context_check
);
34 DECLARE_PTR_LIST(context_list_list
, struct context_check_list
);
35 ALLOCATOR(context_check
, "context check list");
37 static const char *unnamed_context
= "<unnamed>";
39 static const char *context_name(struct context
*context
)
41 if (context
->context
&& context
->context
->symbol_name
)
42 return show_ident(context
->context
->symbol_name
);
43 return unnamed_context
;
46 static void context_add(struct context_check_list
**ccl
, const char *name
,
47 int offs
, int offs_false
)
49 struct context_check
*check
, *found
= NULL
;
51 FOR_EACH_PTR(*ccl
, check
) {
52 if (strcmp(name
, check
->name
))
56 } END_FOR_EACH_PTR(check
);
59 found
= __alloc_context_check(0);
60 strncpy(found
->name
, name
, sizeof(found
->name
));
61 found
->name
[sizeof(found
->name
) - 1] = '\0';
62 add_ptr_list(ccl
, found
);
65 found
->val_false
+= offs_false
;
68 static int context_list_has(struct context_check_list
*ccl
,
69 struct context_check
*c
)
71 struct context_check
*check
;
73 FOR_EACH_PTR(ccl
, check
) {
74 if (strcmp(c
->name
, check
->name
))
76 return check
->val
== c
->val
&&
77 check
->val_false
== c
->val_false
;
78 } END_FOR_EACH_PTR(check
);
80 /* not found is equal to 0 */
81 return c
->val
== 0 && c
->val_false
== 0;
84 static int context_lists_equal(struct context_check_list
*ccl1
,
85 struct context_check_list
*ccl2
)
87 struct context_check
*check
;
89 /* can be optimised... */
91 FOR_EACH_PTR(ccl1
, check
) {
92 if (!context_list_has(ccl2
, check
))
94 } END_FOR_EACH_PTR(check
);
96 FOR_EACH_PTR(ccl2
, check
) {
97 if (!context_list_has(ccl1
, check
))
99 } END_FOR_EACH_PTR(check
);
104 static struct context_check_list
*checked_copy(struct context_check_list
*ccl
)
106 struct context_check_list
*result
= NULL
;
107 struct context_check
*c
;
109 FOR_EACH_PTR(ccl
, c
) {
110 context_add(&result
, c
->name
, c
->val_false
, c
->val_false
);
111 } END_FOR_EACH_PTR(c
);
116 #define IMBALANCE_IN "context imbalance in '%s': "
117 #define DEFAULT_CONTEXT_DESCR " default context: "
119 static void get_context_string(char **buf
, const char **name
)
121 if (strcmp(*name
, unnamed_context
)) {
122 *buf
= malloc(strlen(*name
) + 16);
123 sprintf(*buf
, " context '%s': ", *name
);
126 *name
= DEFAULT_CONTEXT_DESCR
;
131 static int context_list_check(struct entrypoint
*ep
, struct position pos
,
132 struct context_check_list
*ccl_cur
,
133 struct context_check_list
*ccl_target
)
135 struct context_check
*c1
, *c2
;
140 /* make sure the loop below checks all */
141 FOR_EACH_PTR(ccl_target
, c1
) {
142 context_add(&ccl_cur
, c1
->name
, 0, 0);
143 } END_FOR_EACH_PTR(c1
);
145 FOR_EACH_PTR(ccl_cur
, c1
) {
149 FOR_EACH_PTR(ccl_target
, c2
) {
150 if (strcmp(c2
->name
, c1
->name
))
154 } END_FOR_EACH_PTR(c2
);
156 if (cur
== tgt
|| !Wcontext
)
160 warning(pos
, IMBALANCE_IN
"wrong count at exit",
161 show_ident(ep
->name
->ident
));
163 warning(pos
, IMBALANCE_IN
"unexpected unlock",
164 show_ident(ep
->name
->ident
));
167 get_context_string(&buf
, &name
);
169 info(pos
, "%swanted %d, got %d",
175 } END_FOR_EACH_PTR(c1
);
180 static int handle_call(struct entrypoint
*ep
, struct basic_block
*bb
,
181 struct instruction
*insn
,
182 struct context_check_list
*combined
)
185 struct context_check
*c
;
186 const char *name
, *call
, *cmp
;
190 if (!insn
->func
|| !insn
->func
->sym
||
191 insn
->func
->type
!= PSEUDO_SYM
)
195 * Check all contexts the function wants.
197 FOR_EACH_PTR(insn
->func
->sym
->ctype
.contexts
, ctx
) {
198 name
= context_name(ctx
);
201 FOR_EACH_PTR(combined
, c
) {
202 if (strcmp(c
->name
, name
) == 0) {
206 } END_FOR_EACH_PTR(c
);
216 if (!ok
&& Wcontext
) {
217 get_context_string(&buf
, &name
);
218 call
= strdup(show_ident(insn
->func
->ident
));
220 warning(insn
->pos
, "context problem in '%s': "
221 "'%s' expected different context",
222 show_ident(ep
->name
->ident
), call
);
224 info(insn
->pos
, "%swanted %s%d, got %d",
225 name
, cmp
, ctx
->in
, val
);
232 } END_FOR_EACH_PTR (ctx
);
237 static int handle_context(struct entrypoint
*ep
, struct basic_block
*bb
,
238 struct instruction
*insn
,
239 struct context_check_list
**combined
)
241 struct context_check
*c
;
248 name
= unnamed_context
;
249 if (insn
->context_expr
)
250 name
= show_ident(insn
->context_expr
->symbol_name
);
252 FOR_EACH_PTR(*combined
, c
) {
253 if (strcmp(c
->name
, name
) == 0) {
257 } END_FOR_EACH_PTR(c
);
259 ok
= insn
->required
<= val
;
261 if (!ok
&& Wcontext
) {
262 get_context_string(&buf
, &name
);
266 "__context__ statement expected different context",
267 show_ident(ep
->name
->ident
));
269 info(insn
->pos
, "%swanted >= %d, got %d",
270 name
, insn
->required
, val
);
276 context_add(combined
, name
, insn
->increment
, insn
->inc_false
);
281 static int check_bb_context(struct entrypoint
*ep
, struct basic_block
*bb
,
282 struct context_check_list
*ccl_in
,
283 struct context_check_list
*ccl_target
,
286 struct context_check_list
*combined
= NULL
, *done
;
287 struct context_check
*c
;
288 struct instruction
*insn
;
293 * Recurse in once to catch bad loops.
295 if (bb
->context_check_recursion
> 1)
297 bb
->context_check_recursion
++;
300 * Abort if we have already checked this block out of the same context.
302 FOR_EACH_PTR(bb
->checked_contexts
, done
) {
303 if (context_lists_equal(done
, ccl_in
))
305 } END_FOR_EACH_PTR(done
);
308 * We're starting with a completely new local list of contexts, so
309 * initialise it according to what we got from the parent block.
310 * That may use either the 'false' or the 'true' part of the context
311 * for the conditional_context() attribute.
313 FOR_EACH_PTR(ccl_in
, c
) {
315 context_add(&combined
, c
->name
, c
->val_false
, c
->val_false
);
317 context_add(&combined
, c
->name
, c
->val
, c
->val
);
318 } END_FOR_EACH_PTR(c
);
320 /* Add the new context to the list of already-checked contexts */
321 done
= checked_copy(combined
);
322 add_ptr_list(&bb
->checked_contexts
, done
);
325 * Now walk the instructions for this block, recursing into any
326 * instructions that have children. We need to have the right
327 * order so we cannot iterate bb->children instead.
329 FOR_EACH_PTR(bb
->insns
, insn
) {
330 switch (insn
->opcode
) {
331 case OP_INLINED_CALL
:
333 if (handle_call(ep
, bb
, insn
, combined
))
337 if (handle_context(ep
, bb
, insn
, &combined
))
342 if (check_bb_context(ep
, insn
->bb_true
,
343 combined
, ccl_target
, 0))
346 if (check_bb_context(ep
, insn
->bb_false
,
347 combined
, ccl_target
, 1))
351 case OP_COMPUTEDGOTO
:
352 FOR_EACH_PTR(insn
->multijmp_list
, mj
) {
353 if (check_bb_context(ep
, mj
->target
,
354 combined
, ccl_target
, 0))
356 } END_FOR_EACH_PTR(mj
);
359 } END_FOR_EACH_PTR(insn
);
361 insn
= last_instruction(bb
->insns
);
365 if (insn
->opcode
== OP_RET
) {
366 err
= context_list_check(ep
, insn
->pos
, combined
, ccl_target
);
373 /* contents will be freed once we return out of recursion */
374 free_ptr_list(&combined
);
375 bb
->context_check_recursion
--;
379 static void free_bb_context_lists(struct basic_block
*bb
)
381 struct context_check_list
*done
;
382 struct instruction
*insn
;
385 if (!bb
->checked_contexts
)
388 FOR_EACH_PTR(bb
->checked_contexts
, done
) {
389 free_ptr_list(&done
);
390 } END_FOR_EACH_PTR(done
);
392 free_ptr_list(&bb
->checked_contexts
);
394 FOR_EACH_PTR(bb
->insns
, insn
) {
395 switch (insn
->opcode
) {
398 free_bb_context_lists(insn
->bb_true
);
400 free_bb_context_lists(insn
->bb_false
);
403 case OP_COMPUTEDGOTO
:
404 FOR_EACH_PTR(insn
->multijmp_list
, mj
) {
405 free_bb_context_lists(mj
->target
);
406 } END_FOR_EACH_PTR(mj
);
409 } END_FOR_EACH_PTR(insn
);
412 static void check_cast_instruction(struct instruction
*insn
)
414 struct symbol
*orig_type
= insn
->orig_type
;
416 int old
= orig_type
->bit_size
;
417 int new = insn
->size
;
418 int oldsigned
= (orig_type
->ctype
.modifiers
& MOD_SIGNED
) != 0;
419 int newsigned
= insn
->opcode
== OP_SCAST
;
422 if (oldsigned
== newsigned
)
426 warning(insn
->pos
, "cast loses sign");
430 warning(insn
->pos
, "cast drops bits");
433 if (oldsigned
== newsigned
) {
434 warning(insn
->pos
, "cast wasn't removed");
437 warning(insn
->pos
, "cast changes sign");
441 static void check_range_instruction(struct instruction
*insn
)
443 warning(insn
->pos
, "value out of range");
446 static void check_byte_count(struct instruction
*insn
, pseudo_t count
)
450 if (count
->type
== PSEUDO_VAL
) {
451 long long val
= count
->value
;
452 if (val
<= 0 || val
> 100000)
453 warning(insn
->pos
, "%s with byte count of %lld",
454 show_ident(insn
->func
->sym
->ident
), val
);
457 /* OK, we could try to do the range analysis here */
460 static pseudo_t
argument(struct instruction
*call
, unsigned int argno
)
463 struct ptr_list
*arg_list
= (struct ptr_list
*) call
->arguments
;
466 if (linearize_ptr_list(arg_list
, (void *)args
, 8) > argno
)
471 static void check_memset(struct instruction
*insn
)
473 check_byte_count(insn
, argument(insn
, 3));
476 #define check_memcpy check_memset
477 #define check_ctu check_memset
478 #define check_cfu check_memset
482 void (*check
)(struct instruction
*insn
);
485 static void check_call_instruction(struct instruction
*insn
)
487 pseudo_t fn
= insn
->func
;
489 static const struct checkfn check_fn
[] = {
490 { &memset_ident
, check_memset
},
491 { &memcpy_ident
, check_memcpy
},
492 { ©_to_user_ident
, check_ctu
},
493 { ©_from_user_ident
, check_cfu
},
497 if (fn
->type
!= PSEUDO_SYM
)
499 ident
= fn
->sym
->ident
;
502 for (i
= 0; i
< sizeof(check_fn
)/sizeof(struct checkfn
) ; i
++) {
503 if (check_fn
[i
].id
!= ident
)
505 check_fn
[i
].check(insn
);
510 static void check_one_instruction(struct instruction
*insn
)
512 switch (insn
->opcode
) {
513 case OP_CAST
: case OP_SCAST
:
515 check_cast_instruction(insn
);
518 check_range_instruction(insn
);
521 check_call_instruction(insn
);
528 static void check_bb_instructions(struct basic_block
*bb
)
530 struct instruction
*insn
;
531 FOR_EACH_PTR(bb
->insns
, insn
) {
534 check_one_instruction(insn
);
535 } END_FOR_EACH_PTR(insn
);
538 static void check_instructions(struct entrypoint
*ep
)
540 struct basic_block
*bb
;
541 FOR_EACH_PTR(ep
->bbs
, bb
) {
542 check_bb_instructions(bb
);
543 } END_FOR_EACH_PTR(bb
);
546 static void check_context(struct entrypoint
*ep
)
548 struct symbol
*sym
= ep
->name
;
549 struct context
*context
;
550 struct context_check_list
*ccl_in
= NULL
, *ccl_target
= NULL
;
552 if (Wuninitialized
&& verbose
&& ep
->entry
->bb
->needs
) {
554 FOR_EACH_PTR(ep
->entry
->bb
->needs
, pseudo
) {
555 if (pseudo
->type
!= PSEUDO_ARG
)
556 warning(sym
->pos
, "%s: possible uninitialized variable (%s)",
557 show_ident(sym
->ident
), show_pseudo(pseudo
));
558 } END_FOR_EACH_PTR(pseudo
);
561 check_instructions(ep
);
563 FOR_EACH_PTR(sym
->ctype
.contexts
, context
) {
564 const char *name
= context_name(context
);
566 context_add(&ccl_in
, name
, context
->in
, context
->in
);
567 context_add(&ccl_target
, name
, context
->out
, context
->out_false
);
568 /* we don't currently check the body of trylock functions */
569 if (context
->out
!= context
->out_false
)
571 } END_FOR_EACH_PTR(context
);
573 check_bb_context(ep
, ep
->entry
->bb
, ccl_in
, ccl_target
, 0);
574 free_ptr_list(&ccl_in
);
575 free_ptr_list(&ccl_target
);
576 free_bb_context_lists(ep
->entry
->bb
);
577 clear_context_check_alloc();
580 static void check_symbols(struct symbol_list
*list
)
584 FOR_EACH_PTR(list
, sym
) {
585 struct entrypoint
*ep
;
588 ep
= linearize_symbol(sym
);
595 } END_FOR_EACH_PTR(sym
);
598 int main(int argc
, char **argv
)
600 struct string_list
*filelist
= NULL
;
603 // Expand, linearize and show it.
604 check_symbols(sparse_initialize(argc
, argv
, &filelist
));
605 FOR_EACH_PTR_NOTAG(filelist
, file
) {
606 check_symbols(sparse(file
));
607 } END_FOR_EACH_PTR_NOTAG(file
);