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