1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28#include <string.h>
29#include <subcmd/parse-options.h>
30
31#include "builtin.h"
32#include "elf.h"
33#include "special.h"
34#include "arch.h"
35#include "warn.h"
36
37#include <linux/hashtable.h>
38
39#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
40
41#define STATE_FP_SAVED 0x1
42#define STATE_FP_SETUP 0x2
43#define STATE_FENTRY 0x4
44
45struct instruction {
46 struct list_head list;
47 struct hlist_node hash;
48 struct section *sec;
49 unsigned long offset;
50 unsigned int len, state;
51 unsigned char type;
52 unsigned long immediate;
53 bool alt_group, visited;
54 struct symbol *call_dest;
55 struct instruction *jump_dest;
56 struct list_head alts;
57 struct symbol *func;
58};
59
60struct alternative {
61 struct list_head list;
62 struct instruction *insn;
63};
64
65struct objtool_file {
66 struct elf *elf;
67 struct list_head insn_list;
68 DECLARE_HASHTABLE(insn_hash, 16);
69 struct section *rodata, *whitelist;
70 bool ignore_unreachables, c_file;
71};
72
73const char *objname;
74static bool nofp;
75
76static struct instruction *find_insn(struct objtool_file *file,
77 struct section *sec, unsigned long offset)
78{
79 struct instruction *insn;
80
81 hash_for_each_possible(file->insn_hash, insn, hash, offset)
82 if (insn->sec == sec && insn->offset == offset)
83 return insn;
84
85 return NULL;
86}
87
88static struct instruction *next_insn_same_sec(struct objtool_file *file,
89 struct instruction *insn)
90{
91 struct instruction *next = list_next_entry(insn, list);
92
93 if (&next->list == &file->insn_list || next->sec != insn->sec)
94 return NULL;
95
96 return next;
97}
98
99#define for_each_insn(file, insn) \
100 list_for_each_entry(insn, &file->insn_list, list)
101
102#define func_for_each_insn(file, func, insn) \
103 for (insn = find_insn(file, func->sec, func->offset); \
104 insn && &insn->list != &file->insn_list && \
105 insn->sec == func->sec && \
106 insn->offset < func->offset + func->len; \
107 insn = list_next_entry(insn, list))
108
109#define sec_for_each_insn_from(file, insn) \
110 for (; insn; insn = next_insn_same_sec(file, insn))
111
112
113
114
115
116
117
118static bool ignore_func(struct objtool_file *file, struct symbol *func)
119{
120 struct rela *rela;
121 struct instruction *insn;
122
123
124 if (file->whitelist && file->whitelist->rela)
125 list_for_each_entry(rela, &file->whitelist->rela->rela_list, list)
126 if (rela->sym->sec == func->sec &&
127 rela->addend == func->offset)
128 return true;
129
130
131 func_for_each_insn(file, func, insn)
132 if (insn->type == INSN_CONTEXT_SWITCH)
133 return true;
134
135 return false;
136}
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152static int __dead_end_function(struct objtool_file *file, struct symbol *func,
153 int recursion)
154{
155 int i;
156 struct instruction *insn;
157 bool empty = true;
158
159
160
161
162
163 static const char * const global_noreturns[] = {
164 "__stack_chk_fail",
165 "panic",
166 "do_exit",
167 "__module_put_and_exit",
168 "complete_and_exit",
169 "kvm_spurious_fault",
170 "__reiserfs_panic",
171 "lbug_with_loc"
172 };
173
174 if (func->bind == STB_WEAK)
175 return 0;
176
177 if (func->bind == STB_GLOBAL)
178 for (i = 0; i < ARRAY_SIZE(global_noreturns); i++)
179 if (!strcmp(func->name, global_noreturns[i]))
180 return 1;
181
182 if (!func->sec)
183 return 0;
184
185 func_for_each_insn(file, func, insn) {
186 empty = false;
187
188 if (insn->type == INSN_RETURN)
189 return 0;
190 }
191
192 if (empty)
193 return 0;
194
195
196
197
198
199
200 func_for_each_insn(file, func, insn) {
201 if (insn->sec != func->sec ||
202 insn->offset >= func->offset + func->len)
203 break;
204
205 if (insn->type == INSN_JUMP_UNCONDITIONAL) {
206 struct instruction *dest = insn->jump_dest;
207 struct symbol *dest_func;
208
209 if (!dest)
210
211 return 0;
212
213 if (dest->sec != func->sec ||
214 dest->offset < func->offset ||
215 dest->offset >= func->offset + func->len) {
216
217 dest_func = find_symbol_by_offset(dest->sec,
218 dest->offset);
219 if (!dest_func)
220 continue;
221
222 if (recursion == 5) {
223 WARN_FUNC("infinite recursion (objtool bug!)",
224 dest->sec, dest->offset);
225 return -1;
226 }
227
228 return __dead_end_function(file, dest_func,
229 recursion + 1);
230 }
231 }
232
233 if (insn->type == INSN_JUMP_DYNAMIC && list_empty(&insn->alts))
234
235 return 0;
236 }
237
238 return 1;
239}
240
241static int dead_end_function(struct objtool_file *file, struct symbol *func)
242{
243 return __dead_end_function(file, func, 0);
244}
245
246
247
248
249
250static int decode_instructions(struct objtool_file *file)
251{
252 struct section *sec;
253 struct symbol *func;
254 unsigned long offset;
255 struct instruction *insn;
256 int ret;
257
258 list_for_each_entry(sec, &file->elf->sections, list) {
259
260 if (!(sec->sh.sh_flags & SHF_EXECINSTR))
261 continue;
262
263 for (offset = 0; offset < sec->len; offset += insn->len) {
264 insn = malloc(sizeof(*insn));
265 memset(insn, 0, sizeof(*insn));
266
267 INIT_LIST_HEAD(&insn->alts);
268 insn->sec = sec;
269 insn->offset = offset;
270
271 ret = arch_decode_instruction(file->elf, sec, offset,
272 sec->len - offset,
273 &insn->len, &insn->type,
274 &insn->immediate);
275 if (ret)
276 return ret;
277
278 if (!insn->type || insn->type > INSN_LAST) {
279 WARN_FUNC("invalid instruction type %d",
280 insn->sec, insn->offset, insn->type);
281 return -1;
282 }
283
284 hash_add(file->insn_hash, &insn->hash, insn->offset);
285 list_add_tail(&insn->list, &file->insn_list);
286 }
287
288 list_for_each_entry(func, &sec->symbol_list, list) {
289 if (func->type != STT_FUNC)
290 continue;
291
292 if (!find_insn(file, sec, func->offset)) {
293 WARN("%s(): can't find starting instruction",
294 func->name);
295 return -1;
296 }
297
298 func_for_each_insn(file, func, insn)
299 if (!insn->func)
300 insn->func = func;
301 }
302 }
303
304 return 0;
305}
306
307
308
309
310static void add_ignores(struct objtool_file *file)
311{
312 struct instruction *insn;
313 struct section *sec;
314 struct symbol *func;
315
316 list_for_each_entry(sec, &file->elf->sections, list) {
317 list_for_each_entry(func, &sec->symbol_list, list) {
318 if (func->type != STT_FUNC)
319 continue;
320
321 if (!ignore_func(file, func))
322 continue;
323
324 func_for_each_insn(file, func, insn)
325 insn->visited = true;
326 }
327 }
328}
329
330
331
332
333static int add_jump_destinations(struct objtool_file *file)
334{
335 struct instruction *insn;
336 struct rela *rela;
337 struct section *dest_sec;
338 unsigned long dest_off;
339
340 for_each_insn(file, insn) {
341 if (insn->type != INSN_JUMP_CONDITIONAL &&
342 insn->type != INSN_JUMP_UNCONDITIONAL)
343 continue;
344
345
346 if (insn->visited)
347 continue;
348
349 rela = find_rela_by_dest_range(insn->sec, insn->offset,
350 insn->len);
351 if (!rela) {
352 dest_sec = insn->sec;
353 dest_off = insn->offset + insn->len + insn->immediate;
354 } else if (rela->sym->type == STT_SECTION) {
355 dest_sec = rela->sym->sec;
356 dest_off = rela->addend + 4;
357 } else if (rela->sym->sec->idx) {
358 dest_sec = rela->sym->sec;
359 dest_off = rela->sym->sym.st_value + rela->addend + 4;
360 } else {
361
362 insn->jump_dest = 0;
363 continue;
364 }
365
366 insn->jump_dest = find_insn(file, dest_sec, dest_off);
367 if (!insn->jump_dest) {
368
369
370
371
372
373
374 if (!strcmp(insn->sec->name, ".altinstr_replacement"))
375 continue;
376
377 WARN_FUNC("can't find jump dest instruction at %s+0x%lx",
378 insn->sec, insn->offset, dest_sec->name,
379 dest_off);
380 return -1;
381 }
382 }
383
384 return 0;
385}
386
387
388
389
390static int add_call_destinations(struct objtool_file *file)
391{
392 struct instruction *insn;
393 unsigned long dest_off;
394 struct rela *rela;
395
396 for_each_insn(file, insn) {
397 if (insn->type != INSN_CALL)
398 continue;
399
400 rela = find_rela_by_dest_range(insn->sec, insn->offset,
401 insn->len);
402 if (!rela) {
403 dest_off = insn->offset + insn->len + insn->immediate;
404 insn->call_dest = find_symbol_by_offset(insn->sec,
405 dest_off);
406 if (!insn->call_dest) {
407 WARN_FUNC("can't find call dest symbol at offset 0x%lx",
408 insn->sec, insn->offset, dest_off);
409 return -1;
410 }
411 } else if (rela->sym->type == STT_SECTION) {
412 insn->call_dest = find_symbol_by_offset(rela->sym->sec,
413 rela->addend+4);
414 if (!insn->call_dest ||
415 insn->call_dest->type != STT_FUNC) {
416 WARN_FUNC("can't find call dest symbol at %s+0x%x",
417 insn->sec, insn->offset,
418 rela->sym->sec->name,
419 rela->addend + 4);
420 return -1;
421 }
422 } else
423 insn->call_dest = rela->sym;
424 }
425
426 return 0;
427}
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449static int handle_group_alt(struct objtool_file *file,
450 struct special_alt *special_alt,
451 struct instruction *orig_insn,
452 struct instruction **new_insn)
453{
454 struct instruction *last_orig_insn, *last_new_insn, *insn, *fake_jump;
455 unsigned long dest_off;
456
457 last_orig_insn = NULL;
458 insn = orig_insn;
459 sec_for_each_insn_from(file, insn) {
460 if (insn->offset >= special_alt->orig_off + special_alt->orig_len)
461 break;
462
463 if (special_alt->skip_orig)
464 insn->type = INSN_NOP;
465
466 insn->alt_group = true;
467 last_orig_insn = insn;
468 }
469
470 if (!next_insn_same_sec(file, last_orig_insn)) {
471 WARN("%s: don't know how to handle alternatives at end of section",
472 special_alt->orig_sec->name);
473 return -1;
474 }
475
476 fake_jump = malloc(sizeof(*fake_jump));
477 if (!fake_jump) {
478 WARN("malloc failed");
479 return -1;
480 }
481 memset(fake_jump, 0, sizeof(*fake_jump));
482 INIT_LIST_HEAD(&fake_jump->alts);
483 fake_jump->sec = special_alt->new_sec;
484 fake_jump->offset = -1;
485 fake_jump->type = INSN_JUMP_UNCONDITIONAL;
486 fake_jump->jump_dest = list_next_entry(last_orig_insn, list);
487
488 if (!special_alt->new_len) {
489 *new_insn = fake_jump;
490 return 0;
491 }
492
493 last_new_insn = NULL;
494 insn = *new_insn;
495 sec_for_each_insn_from(file, insn) {
496 if (insn->offset >= special_alt->new_off + special_alt->new_len)
497 break;
498
499 last_new_insn = insn;
500
501 if (insn->type != INSN_JUMP_CONDITIONAL &&
502 insn->type != INSN_JUMP_UNCONDITIONAL)
503 continue;
504
505 if (!insn->immediate)
506 continue;
507
508 dest_off = insn->offset + insn->len + insn->immediate;
509 if (dest_off == special_alt->new_off + special_alt->new_len)
510 insn->jump_dest = fake_jump;
511
512 if (!insn->jump_dest) {
513 WARN_FUNC("can't find alternative jump destination",
514 insn->sec, insn->offset);
515 return -1;
516 }
517 }
518
519 if (!last_new_insn) {
520 WARN_FUNC("can't find last new alternative instruction",
521 special_alt->new_sec, special_alt->new_off);
522 return -1;
523 }
524
525 list_add(&fake_jump->list, &last_new_insn->list);
526
527 return 0;
528}
529
530
531
532
533
534
535static int handle_jump_alt(struct objtool_file *file,
536 struct special_alt *special_alt,
537 struct instruction *orig_insn,
538 struct instruction **new_insn)
539{
540 if (orig_insn->type == INSN_NOP)
541 return 0;
542
543 if (orig_insn->type != INSN_JUMP_UNCONDITIONAL) {
544 WARN_FUNC("unsupported instruction at jump label",
545 orig_insn->sec, orig_insn->offset);
546 return -1;
547 }
548
549 *new_insn = list_next_entry(orig_insn, list);
550 return 0;
551}
552
553
554
555
556
557
558
559static int add_special_section_alts(struct objtool_file *file)
560{
561 struct list_head special_alts;
562 struct instruction *orig_insn, *new_insn;
563 struct special_alt *special_alt, *tmp;
564 struct alternative *alt;
565 int ret;
566
567 ret = special_get_alts(file->elf, &special_alts);
568 if (ret)
569 return ret;
570
571 list_for_each_entry_safe(special_alt, tmp, &special_alts, list) {
572 alt = malloc(sizeof(*alt));
573 if (!alt) {
574 WARN("malloc failed");
575 ret = -1;
576 goto out;
577 }
578
579 orig_insn = find_insn(file, special_alt->orig_sec,
580 special_alt->orig_off);
581 if (!orig_insn) {
582 WARN_FUNC("special: can't find orig instruction",
583 special_alt->orig_sec, special_alt->orig_off);
584 ret = -1;
585 goto out;
586 }
587
588 new_insn = NULL;
589 if (!special_alt->group || special_alt->new_len) {
590 new_insn = find_insn(file, special_alt->new_sec,
591 special_alt->new_off);
592 if (!new_insn) {
593 WARN_FUNC("special: can't find new instruction",
594 special_alt->new_sec,
595 special_alt->new_off);
596 ret = -1;
597 goto out;
598 }
599 }
600
601 if (special_alt->group) {
602 ret = handle_group_alt(file, special_alt, orig_insn,
603 &new_insn);
604 if (ret)
605 goto out;
606 } else if (special_alt->jump_or_nop) {
607 ret = handle_jump_alt(file, special_alt, orig_insn,
608 &new_insn);
609 if (ret)
610 goto out;
611 }
612
613 alt->insn = new_insn;
614 list_add_tail(&alt->list, &orig_insn->alts);
615
616 list_del(&special_alt->list);
617 free(special_alt);
618 }
619
620out:
621 return ret;
622}
623
624static int add_switch_table(struct objtool_file *file, struct symbol *func,
625 struct instruction *insn, struct rela *table,
626 struct rela *next_table)
627{
628 struct rela *rela = table;
629 struct instruction *alt_insn;
630 struct alternative *alt;
631
632 list_for_each_entry_from(rela, &file->rodata->rela->rela_list, list) {
633 if (rela == next_table)
634 break;
635
636 if (rela->sym->sec != insn->sec ||
637 rela->addend <= func->offset ||
638 rela->addend >= func->offset + func->len)
639 break;
640
641 alt_insn = find_insn(file, insn->sec, rela->addend);
642 if (!alt_insn) {
643 WARN("%s: can't find instruction at %s+0x%x",
644 file->rodata->rela->name, insn->sec->name,
645 rela->addend);
646 return -1;
647 }
648
649 alt = malloc(sizeof(*alt));
650 if (!alt) {
651 WARN("malloc failed");
652 return -1;
653 }
654
655 alt->insn = alt_insn;
656 list_add_tail(&alt->list, &insn->alts);
657 }
658
659 return 0;
660}
661
662static int add_func_switch_tables(struct objtool_file *file,
663 struct symbol *func)
664{
665 struct instruction *insn, *prev_jump;
666 struct rela *text_rela, *rodata_rela, *prev_rela;
667 int ret;
668
669 prev_jump = NULL;
670
671 func_for_each_insn(file, func, insn) {
672 if (insn->type != INSN_JUMP_DYNAMIC)
673 continue;
674
675 text_rela = find_rela_by_dest_range(insn->sec, insn->offset,
676 insn->len);
677 if (!text_rela || text_rela->sym != file->rodata->sym)
678 continue;
679
680
681 rodata_rela = find_rela_by_dest(file->rodata,
682 text_rela->addend);
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713 if (!rodata_rela) {
714 rodata_rela = find_rela_by_dest(file->rodata,
715 text_rela->addend + 4);
716 if (rodata_rela)
717 file->ignore_unreachables = true;
718 }
719
720 if (!rodata_rela)
721 continue;
722
723
724
725
726
727
728 if (prev_jump) {
729 ret = add_switch_table(file, func, prev_jump, prev_rela,
730 rodata_rela);
731 if (ret)
732 return ret;
733 }
734
735 prev_jump = insn;
736 prev_rela = rodata_rela;
737 }
738
739 if (prev_jump) {
740 ret = add_switch_table(file, func, prev_jump, prev_rela, NULL);
741 if (ret)
742 return ret;
743 }
744
745 return 0;
746}
747
748
749
750
751
752
753static int add_switch_table_alts(struct objtool_file *file)
754{
755 struct section *sec;
756 struct symbol *func;
757 int ret;
758
759 if (!file->rodata || !file->rodata->rela)
760 return 0;
761
762 list_for_each_entry(sec, &file->elf->sections, list) {
763 list_for_each_entry(func, &sec->symbol_list, list) {
764 if (func->type != STT_FUNC)
765 continue;
766
767 ret = add_func_switch_tables(file, func);
768 if (ret)
769 return ret;
770 }
771 }
772
773 return 0;
774}
775
776static int decode_sections(struct objtool_file *file)
777{
778 int ret;
779
780 ret = decode_instructions(file);
781 if (ret)
782 return ret;
783
784 add_ignores(file);
785
786 ret = add_jump_destinations(file);
787 if (ret)
788 return ret;
789
790 ret = add_call_destinations(file);
791 if (ret)
792 return ret;
793
794 ret = add_special_section_alts(file);
795 if (ret)
796 return ret;
797
798 ret = add_switch_table_alts(file);
799 if (ret)
800 return ret;
801
802 return 0;
803}
804
805static bool is_fentry_call(struct instruction *insn)
806{
807 if (insn->type == INSN_CALL &&
808 insn->call_dest->type == STT_NOTYPE &&
809 !strcmp(insn->call_dest->name, "__fentry__"))
810 return true;
811
812 return false;
813}
814
815static bool has_modified_stack_frame(struct instruction *insn)
816{
817 return (insn->state & STATE_FP_SAVED) ||
818 (insn->state & STATE_FP_SETUP);
819}
820
821static bool has_valid_stack_frame(struct instruction *insn)
822{
823 return (insn->state & STATE_FP_SAVED) &&
824 (insn->state & STATE_FP_SETUP);
825}
826
827static unsigned int frame_state(unsigned long state)
828{
829 return (state & (STATE_FP_SAVED | STATE_FP_SETUP));
830}
831
832
833
834
835
836
837
838static int validate_branch(struct objtool_file *file,
839 struct instruction *first, unsigned char first_state)
840{
841 struct alternative *alt;
842 struct instruction *insn;
843 struct section *sec;
844 struct symbol *func = NULL;
845 unsigned char state;
846 int ret;
847
848 insn = first;
849 sec = insn->sec;
850 state = first_state;
851
852 if (insn->alt_group && list_empty(&insn->alts)) {
853 WARN_FUNC("don't know how to handle branch to middle of alternative instruction group",
854 sec, insn->offset);
855 return 1;
856 }
857
858 while (1) {
859 if (file->c_file && insn->func) {
860 if (func && func != insn->func) {
861 WARN("%s() falls through to next function %s()",
862 func->name, insn->func->name);
863 return 1;
864 }
865
866 func = insn->func;
867 }
868
869 if (insn->visited) {
870 if (frame_state(insn->state) != frame_state(state)) {
871 WARN_FUNC("frame pointer state mismatch",
872 sec, insn->offset);
873 return 1;
874 }
875
876 return 0;
877 }
878
879 insn->visited = true;
880 insn->state = state;
881
882 list_for_each_entry(alt, &insn->alts, list) {
883 ret = validate_branch(file, alt->insn, state);
884 if (ret)
885 return 1;
886 }
887
888 switch (insn->type) {
889
890 case INSN_FP_SAVE:
891 if (!nofp) {
892 if (state & STATE_FP_SAVED) {
893 WARN_FUNC("duplicate frame pointer save",
894 sec, insn->offset);
895 return 1;
896 }
897 state |= STATE_FP_SAVED;
898 }
899 break;
900
901 case INSN_FP_SETUP:
902 if (!nofp) {
903 if (state & STATE_FP_SETUP) {
904 WARN_FUNC("duplicate frame pointer setup",
905 sec, insn->offset);
906 return 1;
907 }
908 state |= STATE_FP_SETUP;
909 }
910 break;
911
912 case INSN_FP_RESTORE:
913 if (!nofp) {
914 if (has_valid_stack_frame(insn))
915 state &= ~STATE_FP_SETUP;
916
917 state &= ~STATE_FP_SAVED;
918 }
919 break;
920
921 case INSN_RETURN:
922 if (!nofp && has_modified_stack_frame(insn)) {
923 WARN_FUNC("return without frame pointer restore",
924 sec, insn->offset);
925 return 1;
926 }
927 return 0;
928
929 case INSN_CALL:
930 if (is_fentry_call(insn)) {
931 state |= STATE_FENTRY;
932 break;
933 }
934
935 ret = dead_end_function(file, insn->call_dest);
936 if (ret == 1)
937 return 0;
938 if (ret == -1)
939 return 1;
940
941
942 case INSN_CALL_DYNAMIC:
943 if (!nofp && !has_valid_stack_frame(insn)) {
944 WARN_FUNC("call without frame pointer save/setup",
945 sec, insn->offset);
946 return 1;
947 }
948 break;
949
950 case INSN_JUMP_CONDITIONAL:
951 case INSN_JUMP_UNCONDITIONAL:
952 if (insn->jump_dest) {
953 ret = validate_branch(file, insn->jump_dest,
954 state);
955 if (ret)
956 return 1;
957 } else if (has_modified_stack_frame(insn)) {
958 WARN_FUNC("sibling call from callable instruction with changed frame pointer",
959 sec, insn->offset);
960 return 1;
961 }
962
963 if (insn->type == INSN_JUMP_UNCONDITIONAL)
964 return 0;
965
966 break;
967
968 case INSN_JUMP_DYNAMIC:
969 if (list_empty(&insn->alts) &&
970 has_modified_stack_frame(insn)) {
971 WARN_FUNC("sibling call from callable instruction with changed frame pointer",
972 sec, insn->offset);
973 return 1;
974 }
975
976 return 0;
977
978 case INSN_BUG:
979 return 0;
980
981 default:
982 break;
983 }
984
985 insn = next_insn_same_sec(file, insn);
986 if (!insn) {
987 WARN("%s: unexpected end of section", sec->name);
988 return 1;
989 }
990 }
991
992 return 0;
993}
994
995static bool is_gcov_insn(struct instruction *insn)
996{
997 struct rela *rela;
998 struct section *sec;
999 struct symbol *sym;
1000 unsigned long offset;
1001
1002 rela = find_rela_by_dest_range(insn->sec, insn->offset, insn->len);
1003 if (!rela)
1004 return false;
1005
1006 if (rela->sym->type != STT_SECTION)
1007 return false;
1008
1009 sec = rela->sym->sec;
1010 offset = rela->addend + insn->offset + insn->len - rela->offset;
1011
1012 list_for_each_entry(sym, &sec->symbol_list, list) {
1013 if (sym->type != STT_OBJECT)
1014 continue;
1015
1016 if (offset >= sym->offset && offset < sym->offset + sym->len)
1017 return (!memcmp(sym->name, "__gcov0.", 8));
1018 }
1019
1020 return false;
1021}
1022
1023static bool is_kasan_insn(struct instruction *insn)
1024{
1025 return (insn->type == INSN_CALL &&
1026 !strcmp(insn->call_dest->name, "__asan_handle_no_return"));
1027}
1028
1029static bool is_ubsan_insn(struct instruction *insn)
1030{
1031 return (insn->type == INSN_CALL &&
1032 !strcmp(insn->call_dest->name,
1033 "__ubsan_handle_builtin_unreachable"));
1034}
1035
1036static bool ignore_unreachable_insn(struct symbol *func,
1037 struct instruction *insn)
1038{
1039 int i;
1040
1041 if (insn->type == INSN_NOP)
1042 return true;
1043
1044 if (is_gcov_insn(insn))
1045 return true;
1046
1047
1048
1049
1050
1051
1052
1053 for (i = 0; i < 5; i++) {
1054
1055 if (is_kasan_insn(insn) || is_ubsan_insn(insn))
1056 return true;
1057
1058 if (insn->type == INSN_JUMP_UNCONDITIONAL && insn->jump_dest) {
1059 insn = insn->jump_dest;
1060 continue;
1061 }
1062
1063 if (insn->offset + insn->len >= func->offset + func->len)
1064 break;
1065 insn = list_next_entry(insn, list);
1066 }
1067
1068 return false;
1069}
1070
1071static int validate_functions(struct objtool_file *file)
1072{
1073 struct section *sec;
1074 struct symbol *func;
1075 struct instruction *insn;
1076 int ret, warnings = 0;
1077
1078 list_for_each_entry(sec, &file->elf->sections, list) {
1079 list_for_each_entry(func, &sec->symbol_list, list) {
1080 if (func->type != STT_FUNC)
1081 continue;
1082
1083 insn = find_insn(file, sec, func->offset);
1084 if (!insn)
1085 continue;
1086
1087 ret = validate_branch(file, insn, 0);
1088 warnings += ret;
1089 }
1090 }
1091
1092 list_for_each_entry(sec, &file->elf->sections, list) {
1093 list_for_each_entry(func, &sec->symbol_list, list) {
1094 if (func->type != STT_FUNC)
1095 continue;
1096
1097 func_for_each_insn(file, func, insn) {
1098 if (insn->visited)
1099 continue;
1100
1101 insn->visited = true;
1102
1103 if (file->ignore_unreachables || warnings ||
1104 ignore_unreachable_insn(func, insn))
1105 continue;
1106
1107 WARN_FUNC("function has unreachable instruction", insn->sec, insn->offset);
1108 warnings++;
1109 }
1110 }
1111 }
1112
1113 return warnings;
1114}
1115
1116static int validate_uncallable_instructions(struct objtool_file *file)
1117{
1118 struct instruction *insn;
1119 int warnings = 0;
1120
1121 for_each_insn(file, insn) {
1122 if (!insn->visited && insn->type == INSN_RETURN) {
1123 WARN_FUNC("return instruction outside of a callable function",
1124 insn->sec, insn->offset);
1125 warnings++;
1126 }
1127 }
1128
1129 return warnings;
1130}
1131
1132static void cleanup(struct objtool_file *file)
1133{
1134 struct instruction *insn, *tmpinsn;
1135 struct alternative *alt, *tmpalt;
1136
1137 list_for_each_entry_safe(insn, tmpinsn, &file->insn_list, list) {
1138 list_for_each_entry_safe(alt, tmpalt, &insn->alts, list) {
1139 list_del(&alt->list);
1140 free(alt);
1141 }
1142 list_del(&insn->list);
1143 hash_del(&insn->hash);
1144 free(insn);
1145 }
1146 elf_close(file->elf);
1147}
1148
1149const char * const check_usage[] = {
1150 "objtool check [<options>] file.o",
1151 NULL,
1152};
1153
1154int cmd_check(int argc, const char **argv)
1155{
1156 struct objtool_file file;
1157 int ret, warnings = 0;
1158
1159 const struct option options[] = {
1160 OPT_BOOLEAN('f', "no-fp", &nofp, "Skip frame pointer validation"),
1161 OPT_END(),
1162 };
1163
1164 argc = parse_options(argc, argv, options, check_usage, 0);
1165
1166 if (argc != 1)
1167 usage_with_options(check_usage, options);
1168
1169 objname = argv[0];
1170
1171 file.elf = elf_open(objname);
1172 if (!file.elf) {
1173 fprintf(stderr, "error reading elf file %s\n", objname);
1174 return 1;
1175 }
1176
1177 INIT_LIST_HEAD(&file.insn_list);
1178 hash_init(file.insn_hash);
1179 file.whitelist = find_section_by_name(file.elf, "__func_stack_frame_non_standard");
1180 file.rodata = find_section_by_name(file.elf, ".rodata");
1181 file.ignore_unreachables = false;
1182 file.c_file = find_section_by_name(file.elf, ".comment");
1183
1184 ret = decode_sections(&file);
1185 if (ret < 0)
1186 goto out;
1187 warnings += ret;
1188
1189 ret = validate_functions(&file);
1190 if (ret < 0)
1191 goto out;
1192 warnings += ret;
1193
1194 ret = validate_uncallable_instructions(&file);
1195 if (ret < 0)
1196 goto out;
1197 warnings += ret;
1198
1199out:
1200 cleanup(&file);
1201
1202
1203 if (ret || warnings)
1204 return 0;
1205 return 0;
1206}
1207