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