1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18#include <string.h>
19#include <stdlib.h>
20
21#include "check.h"
22#include "elf.h"
23#include "special.h"
24#include "arch.h"
25#include "warn.h"
26
27#include <linux/hashtable.h>
28#include <linux/kernel.h>
29
30struct alternative {
31 struct list_head list;
32 struct instruction *insn;
33};
34
35const char *objname;
36static bool nofp;
37struct cfi_state initial_func_cfi;
38
39static struct instruction *find_insn(struct objtool_file *file,
40 struct section *sec, unsigned long offset)
41{
42 struct instruction *insn;
43
44 hash_for_each_possible(file->insn_hash, insn, hash, offset)
45 if (insn->sec == sec && insn->offset == offset)
46 return insn;
47
48 return NULL;
49}
50
51static struct instruction *next_insn_same_sec(struct objtool_file *file,
52 struct instruction *insn)
53{
54 struct instruction *next = list_next_entry(insn, list);
55
56 if (!next || &next->list == &file->insn_list || next->sec != insn->sec)
57 return NULL;
58
59 return next;
60}
61
62static bool gcov_enabled(struct objtool_file *file)
63{
64 struct section *sec;
65 struct symbol *sym;
66
67 for_each_sec(file, sec)
68 list_for_each_entry(sym, &sec->symbol_list, list)
69 if (!strncmp(sym->name, "__gcov_.", 8))
70 return true;
71
72 return false;
73}
74
75#define func_for_each_insn(file, func, insn) \
76 for (insn = find_insn(file, func->sec, func->offset); \
77 insn && &insn->list != &file->insn_list && \
78 insn->sec == func->sec && \
79 insn->offset < func->offset + func->len; \
80 insn = list_next_entry(insn, list))
81
82#define func_for_each_insn_continue_reverse(file, func, insn) \
83 for (insn = list_prev_entry(insn, list); \
84 &insn->list != &file->insn_list && \
85 insn->sec == func->sec && insn->offset >= func->offset; \
86 insn = list_prev_entry(insn, list))
87
88#define sec_for_each_insn_from(file, insn) \
89 for (; insn; insn = next_insn_same_sec(file, insn))
90
91#define sec_for_each_insn_continue(file, insn) \
92 for (insn = next_insn_same_sec(file, insn); insn; \
93 insn = next_insn_same_sec(file, insn))
94
95
96
97
98
99
100static bool ignore_func(struct objtool_file *file, struct symbol *func)
101{
102 struct rela *rela;
103 struct instruction *insn;
104
105
106 if (file->whitelist && file->whitelist->rela)
107 list_for_each_entry(rela, &file->whitelist->rela->rela_list, list) {
108 if (rela->sym->type == STT_SECTION &&
109 rela->sym->sec == func->sec &&
110 rela->addend == func->offset)
111 return true;
112 if (rela->sym->type == STT_FUNC && rela->sym == func)
113 return true;
114 }
115
116
117 func_for_each_insn(file, func, insn)
118 if (insn->type == INSN_CONTEXT_SWITCH)
119 return true;
120
121 return false;
122}
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138static int __dead_end_function(struct objtool_file *file, struct symbol *func,
139 int recursion)
140{
141 int i;
142 struct instruction *insn;
143 bool empty = true;
144
145
146
147
148
149 static const char * const global_noreturns[] = {
150 "__stack_chk_fail",
151 "panic",
152 "do_exit",
153 "do_task_dead",
154 "__module_put_and_exit",
155 "complete_and_exit",
156 "kvm_spurious_fault",
157 "__reiserfs_panic",
158 "lbug_with_loc",
159 "fortify_panic",
160 };
161
162 if (func->bind == STB_WEAK)
163 return 0;
164
165 if (func->bind == STB_GLOBAL)
166 for (i = 0; i < ARRAY_SIZE(global_noreturns); i++)
167 if (!strcmp(func->name, global_noreturns[i]))
168 return 1;
169
170 if (!func->sec)
171 return 0;
172
173 func_for_each_insn(file, func, insn) {
174 empty = false;
175
176 if (insn->type == INSN_RETURN)
177 return 0;
178 }
179
180 if (empty)
181 return 0;
182
183
184
185
186
187
188 func_for_each_insn(file, func, insn) {
189 if (insn->sec != func->sec ||
190 insn->offset >= func->offset + func->len)
191 break;
192
193 if (insn->type == INSN_JUMP_UNCONDITIONAL) {
194 struct instruction *dest = insn->jump_dest;
195 struct symbol *dest_func;
196
197 if (!dest)
198
199 return 0;
200
201 if (dest->sec != func->sec ||
202 dest->offset < func->offset ||
203 dest->offset >= func->offset + func->len) {
204
205 dest_func = find_symbol_by_offset(dest->sec,
206 dest->offset);
207 if (!dest_func)
208 continue;
209
210 if (recursion == 5) {
211 WARN_FUNC("infinite recursion (objtool bug!)",
212 dest->sec, dest->offset);
213 return -1;
214 }
215
216 return __dead_end_function(file, dest_func,
217 recursion + 1);
218 }
219 }
220
221 if (insn->type == INSN_JUMP_DYNAMIC && list_empty(&insn->alts))
222
223 return 0;
224 }
225
226 return 1;
227}
228
229static int dead_end_function(struct objtool_file *file, struct symbol *func)
230{
231 return __dead_end_function(file, func, 0);
232}
233
234static void clear_insn_state(struct insn_state *state)
235{
236 int i;
237
238 memset(state, 0, sizeof(*state));
239 state->cfa.base = CFI_UNDEFINED;
240 for (i = 0; i < CFI_NUM_REGS; i++)
241 state->regs[i].base = CFI_UNDEFINED;
242 state->drap_reg = CFI_UNDEFINED;
243}
244
245
246
247
248
249static int decode_instructions(struct objtool_file *file)
250{
251 struct section *sec;
252 struct symbol *func;
253 unsigned long offset;
254 struct instruction *insn;
255 int ret;
256
257 for_each_sec(file, sec) {
258
259 if (!(sec->sh.sh_flags & SHF_EXECINSTR))
260 continue;
261
262 for (offset = 0; offset < sec->len; offset += insn->len) {
263 insn = malloc(sizeof(*insn));
264 if (!insn) {
265 WARN("malloc failed");
266 return -1;
267 }
268 memset(insn, 0, sizeof(*insn));
269 INIT_LIST_HEAD(&insn->alts);
270 clear_insn_state(&insn->state);
271
272 insn->sec = sec;
273 insn->offset = offset;
274
275 ret = arch_decode_instruction(file->elf, sec, offset,
276 sec->len - offset,
277 &insn->len, &insn->type,
278 &insn->immediate,
279 &insn->stack_op);
280 if (ret)
281 return ret;
282
283 if (!insn->type || insn->type > INSN_LAST) {
284 WARN_FUNC("invalid instruction type %d",
285 insn->sec, insn->offset, insn->type);
286 return -1;
287 }
288
289 hash_add(file->insn_hash, &insn->hash, insn->offset);
290 list_add_tail(&insn->list, &file->insn_list);
291 }
292
293 list_for_each_entry(func, &sec->symbol_list, list) {
294 if (func->type != STT_FUNC)
295 continue;
296
297 if (!find_insn(file, sec, func->offset)) {
298 WARN("%s(): can't find starting instruction",
299 func->name);
300 return -1;
301 }
302
303 func_for_each_insn(file, func, insn)
304 if (!insn->func)
305 insn->func = func;
306 }
307 }
308
309 return 0;
310}
311
312
313
314
315static int add_dead_ends(struct objtool_file *file)
316{
317 struct section *sec;
318 struct rela *rela;
319 struct instruction *insn;
320 bool found;
321
322 sec = find_section_by_name(file->elf, ".rela.discard.unreachable");
323 if (!sec)
324 return 0;
325
326 list_for_each_entry(rela, &sec->rela_list, list) {
327 if (rela->sym->type != STT_SECTION) {
328 WARN("unexpected relocation symbol type in %s", sec->name);
329 return -1;
330 }
331 insn = find_insn(file, rela->sym->sec, rela->addend);
332 if (insn)
333 insn = list_prev_entry(insn, list);
334 else if (rela->addend == rela->sym->sec->len) {
335 found = false;
336 list_for_each_entry_reverse(insn, &file->insn_list, list) {
337 if (insn->sec == rela->sym->sec) {
338 found = true;
339 break;
340 }
341 }
342
343 if (!found) {
344 WARN("can't find unreachable insn at %s+0x%x",
345 rela->sym->sec->name, rela->addend);
346 return -1;
347 }
348 } else {
349 WARN("can't find unreachable insn at %s+0x%x",
350 rela->sym->sec->name, rela->addend);
351 return -1;
352 }
353
354 insn->dead_end = true;
355 }
356
357 return 0;
358}
359
360
361
362
363static void add_ignores(struct objtool_file *file)
364{
365 struct instruction *insn;
366 struct section *sec;
367 struct symbol *func;
368
369 for_each_sec(file, sec) {
370 list_for_each_entry(func, &sec->symbol_list, list) {
371 if (func->type != STT_FUNC)
372 continue;
373
374 if (!ignore_func(file, func))
375 continue;
376
377 func_for_each_insn(file, func, insn)
378 insn->ignore = true;
379 }
380 }
381}
382
383
384
385
386static int add_jump_destinations(struct objtool_file *file)
387{
388 struct instruction *insn;
389 struct rela *rela;
390 struct section *dest_sec;
391 unsigned long dest_off;
392
393 for_each_insn(file, insn) {
394 if (insn->type != INSN_JUMP_CONDITIONAL &&
395 insn->type != INSN_JUMP_UNCONDITIONAL)
396 continue;
397
398 if (insn->ignore)
399 continue;
400
401 rela = find_rela_by_dest_range(insn->sec, insn->offset,
402 insn->len);
403 if (!rela) {
404 dest_sec = insn->sec;
405 dest_off = insn->offset + insn->len + insn->immediate;
406 } else if (rela->sym->type == STT_SECTION) {
407 dest_sec = rela->sym->sec;
408 dest_off = rela->addend + 4;
409 } else if (rela->sym->sec->idx) {
410 dest_sec = rela->sym->sec;
411 dest_off = rela->sym->sym.st_value + rela->addend + 4;
412 } else {
413
414 insn->jump_dest = 0;
415 continue;
416 }
417
418 insn->jump_dest = find_insn(file, dest_sec, dest_off);
419 if (!insn->jump_dest) {
420
421
422
423
424
425
426 if (!strcmp(insn->sec->name, ".altinstr_replacement"))
427 continue;
428
429 WARN_FUNC("can't find jump dest instruction at %s+0x%lx",
430 insn->sec, insn->offset, dest_sec->name,
431 dest_off);
432 return -1;
433 }
434 }
435
436 return 0;
437}
438
439
440
441
442static int add_call_destinations(struct objtool_file *file)
443{
444 struct instruction *insn;
445 unsigned long dest_off;
446 struct rela *rela;
447
448 for_each_insn(file, insn) {
449 if (insn->type != INSN_CALL)
450 continue;
451
452 rela = find_rela_by_dest_range(insn->sec, insn->offset,
453 insn->len);
454 if (!rela) {
455 dest_off = insn->offset + insn->len + insn->immediate;
456 insn->call_dest = find_symbol_by_offset(insn->sec,
457 dest_off);
458 if (!insn->call_dest) {
459 WARN_FUNC("can't find call dest symbol at offset 0x%lx",
460 insn->sec, insn->offset, dest_off);
461 return -1;
462 }
463 } else if (rela->sym->type == STT_SECTION) {
464 insn->call_dest = find_symbol_by_offset(rela->sym->sec,
465 rela->addend+4);
466 if (!insn->call_dest ||
467 insn->call_dest->type != STT_FUNC) {
468 WARN_FUNC("can't find call dest symbol at %s+0x%x",
469 insn->sec, insn->offset,
470 rela->sym->sec->name,
471 rela->addend + 4);
472 return -1;
473 }
474 } else
475 insn->call_dest = rela->sym;
476 }
477
478 return 0;
479}
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501static int handle_group_alt(struct objtool_file *file,
502 struct special_alt *special_alt,
503 struct instruction *orig_insn,
504 struct instruction **new_insn)
505{
506 struct instruction *last_orig_insn, *last_new_insn, *insn, *fake_jump;
507 unsigned long dest_off;
508
509 last_orig_insn = NULL;
510 insn = orig_insn;
511 sec_for_each_insn_from(file, insn) {
512 if (insn->offset >= special_alt->orig_off + special_alt->orig_len)
513 break;
514
515 if (special_alt->skip_orig)
516 insn->type = INSN_NOP;
517
518 insn->alt_group = true;
519 last_orig_insn = insn;
520 }
521
522 if (!next_insn_same_sec(file, last_orig_insn)) {
523 WARN("%s: don't know how to handle alternatives at end of section",
524 special_alt->orig_sec->name);
525 return -1;
526 }
527
528 fake_jump = malloc(sizeof(*fake_jump));
529 if (!fake_jump) {
530 WARN("malloc failed");
531 return -1;
532 }
533 memset(fake_jump, 0, sizeof(*fake_jump));
534 INIT_LIST_HEAD(&fake_jump->alts);
535 clear_insn_state(&fake_jump->state);
536
537 fake_jump->sec = special_alt->new_sec;
538 fake_jump->offset = -1;
539 fake_jump->type = INSN_JUMP_UNCONDITIONAL;
540 fake_jump->jump_dest = list_next_entry(last_orig_insn, list);
541 fake_jump->ignore = true;
542
543 if (!special_alt->new_len) {
544 *new_insn = fake_jump;
545 return 0;
546 }
547
548 last_new_insn = NULL;
549 insn = *new_insn;
550 sec_for_each_insn_from(file, insn) {
551 if (insn->offset >= special_alt->new_off + special_alt->new_len)
552 break;
553
554 last_new_insn = insn;
555
556 if (insn->type != INSN_JUMP_CONDITIONAL &&
557 insn->type != INSN_JUMP_UNCONDITIONAL)
558 continue;
559
560 if (!insn->immediate)
561 continue;
562
563 dest_off = insn->offset + insn->len + insn->immediate;
564 if (dest_off == special_alt->new_off + special_alt->new_len)
565 insn->jump_dest = fake_jump;
566
567 if (!insn->jump_dest) {
568 WARN_FUNC("can't find alternative jump destination",
569 insn->sec, insn->offset);
570 return -1;
571 }
572 }
573
574 if (!last_new_insn) {
575 WARN_FUNC("can't find last new alternative instruction",
576 special_alt->new_sec, special_alt->new_off);
577 return -1;
578 }
579
580 list_add(&fake_jump->list, &last_new_insn->list);
581
582 return 0;
583}
584
585
586
587
588
589
590static int handle_jump_alt(struct objtool_file *file,
591 struct special_alt *special_alt,
592 struct instruction *orig_insn,
593 struct instruction **new_insn)
594{
595 if (orig_insn->type == INSN_NOP)
596 return 0;
597
598 if (orig_insn->type != INSN_JUMP_UNCONDITIONAL) {
599 WARN_FUNC("unsupported instruction at jump label",
600 orig_insn->sec, orig_insn->offset);
601 return -1;
602 }
603
604 *new_insn = list_next_entry(orig_insn, list);
605 return 0;
606}
607
608
609
610
611
612
613
614static int add_special_section_alts(struct objtool_file *file)
615{
616 struct list_head special_alts;
617 struct instruction *orig_insn, *new_insn;
618 struct special_alt *special_alt, *tmp;
619 struct alternative *alt;
620 int ret;
621
622 ret = special_get_alts(file->elf, &special_alts);
623 if (ret)
624 return ret;
625
626 list_for_each_entry_safe(special_alt, tmp, &special_alts, list) {
627 alt = malloc(sizeof(*alt));
628 if (!alt) {
629 WARN("malloc failed");
630 ret = -1;
631 goto out;
632 }
633
634 orig_insn = find_insn(file, special_alt->orig_sec,
635 special_alt->orig_off);
636 if (!orig_insn) {
637 WARN_FUNC("special: can't find orig instruction",
638 special_alt->orig_sec, special_alt->orig_off);
639 ret = -1;
640 goto out;
641 }
642
643 new_insn = NULL;
644 if (!special_alt->group || special_alt->new_len) {
645 new_insn = find_insn(file, special_alt->new_sec,
646 special_alt->new_off);
647 if (!new_insn) {
648 WARN_FUNC("special: can't find new instruction",
649 special_alt->new_sec,
650 special_alt->new_off);
651 ret = -1;
652 goto out;
653 }
654 }
655
656 if (special_alt->group) {
657 ret = handle_group_alt(file, special_alt, orig_insn,
658 &new_insn);
659 if (ret)
660 goto out;
661 } else if (special_alt->jump_or_nop) {
662 ret = handle_jump_alt(file, special_alt, orig_insn,
663 &new_insn);
664 if (ret)
665 goto out;
666 }
667
668 alt->insn = new_insn;
669 list_add_tail(&alt->list, &orig_insn->alts);
670
671 list_del(&special_alt->list);
672 free(special_alt);
673 }
674
675out:
676 return ret;
677}
678
679static int add_switch_table(struct objtool_file *file, struct symbol *func,
680 struct instruction *insn, struct rela *table,
681 struct rela *next_table)
682{
683 struct rela *rela = table;
684 struct instruction *alt_insn;
685 struct alternative *alt;
686
687 list_for_each_entry_from(rela, &file->rodata->rela->rela_list, list) {
688 if (rela == next_table)
689 break;
690
691 if (rela->sym->sec != insn->sec ||
692 rela->addend <= func->offset ||
693 rela->addend >= func->offset + func->len)
694 break;
695
696 alt_insn = find_insn(file, insn->sec, rela->addend);
697 if (!alt_insn) {
698 WARN("%s: can't find instruction at %s+0x%x",
699 file->rodata->rela->name, insn->sec->name,
700 rela->addend);
701 return -1;
702 }
703
704 alt = malloc(sizeof(*alt));
705 if (!alt) {
706 WARN("malloc failed");
707 return -1;
708 }
709
710 alt->insn = alt_insn;
711 list_add_tail(&alt->list, &insn->alts);
712 }
713
714 return 0;
715}
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754static struct rela *find_switch_table(struct objtool_file *file,
755 struct symbol *func,
756 struct instruction *insn)
757{
758 struct rela *text_rela, *rodata_rela;
759 struct instruction *orig_insn = insn;
760
761 text_rela = find_rela_by_dest_range(insn->sec, insn->offset, insn->len);
762 if (text_rela && text_rela->sym == file->rodata->sym) {
763
764 rodata_rela = find_rela_by_dest(file->rodata,
765 text_rela->addend);
766 if (rodata_rela)
767 return rodata_rela;
768
769
770 rodata_rela = find_rela_by_dest(file->rodata,
771 text_rela->addend + 4);
772 if (!rodata_rela)
773 return NULL;
774 file->ignore_unreachables = true;
775 return rodata_rela;
776 }
777
778
779 func_for_each_insn_continue_reverse(file, func, insn) {
780 if (insn->type == INSN_JUMP_DYNAMIC)
781 break;
782
783
784 if (insn->type == INSN_JUMP_UNCONDITIONAL &&
785 insn->jump_dest &&
786 (insn->jump_dest->offset <= insn->offset ||
787 insn->jump_dest->offset > orig_insn->offset))
788 break;
789
790
791 text_rela = find_rela_by_dest_range(insn->sec, insn->offset,
792 insn->len);
793 if (!text_rela || text_rela->sym != file->rodata->sym)
794 continue;
795
796
797
798
799
800 if (find_symbol_containing(file->rodata, text_rela->addend))
801 continue;
802
803 return find_rela_by_dest(file->rodata, text_rela->addend);
804 }
805
806 return NULL;
807}
808
809static int add_func_switch_tables(struct objtool_file *file,
810 struct symbol *func)
811{
812 struct instruction *insn, *prev_jump = NULL;
813 struct rela *rela, *prev_rela = NULL;
814 int ret;
815
816 func_for_each_insn(file, func, insn) {
817 if (insn->type != INSN_JUMP_DYNAMIC)
818 continue;
819
820 rela = find_switch_table(file, func, insn);
821 if (!rela)
822 continue;
823
824
825
826
827
828
829 if (prev_jump) {
830 ret = add_switch_table(file, func, prev_jump, prev_rela,
831 rela);
832 if (ret)
833 return ret;
834 }
835
836 prev_jump = insn;
837 prev_rela = rela;
838 }
839
840 if (prev_jump) {
841 ret = add_switch_table(file, func, prev_jump, prev_rela, NULL);
842 if (ret)
843 return ret;
844 }
845
846 return 0;
847}
848
849
850
851
852
853
854static int add_switch_table_alts(struct objtool_file *file)
855{
856 struct section *sec;
857 struct symbol *func;
858 int ret;
859
860 if (!file->rodata || !file->rodata->rela)
861 return 0;
862
863 for_each_sec(file, sec) {
864 list_for_each_entry(func, &sec->symbol_list, list) {
865 if (func->type != STT_FUNC)
866 continue;
867
868 ret = add_func_switch_tables(file, func);
869 if (ret)
870 return ret;
871 }
872 }
873
874 return 0;
875}
876
877static int decode_sections(struct objtool_file *file)
878{
879 int ret;
880
881 ret = decode_instructions(file);
882 if (ret)
883 return ret;
884
885 ret = add_dead_ends(file);
886 if (ret)
887 return ret;
888
889 add_ignores(file);
890
891 ret = add_jump_destinations(file);
892 if (ret)
893 return ret;
894
895 ret = add_call_destinations(file);
896 if (ret)
897 return ret;
898
899 ret = add_special_section_alts(file);
900 if (ret)
901 return ret;
902
903 ret = add_switch_table_alts(file);
904 if (ret)
905 return ret;
906
907 return 0;
908}
909
910static bool is_fentry_call(struct instruction *insn)
911{
912 if (insn->type == INSN_CALL &&
913 insn->call_dest->type == STT_NOTYPE &&
914 !strcmp(insn->call_dest->name, "__fentry__"))
915 return true;
916
917 return false;
918}
919
920static bool has_modified_stack_frame(struct insn_state *state)
921{
922 int i;
923
924 if (state->cfa.base != initial_func_cfi.cfa.base ||
925 state->cfa.offset != initial_func_cfi.cfa.offset ||
926 state->stack_size != initial_func_cfi.cfa.offset ||
927 state->drap)
928 return true;
929
930 for (i = 0; i < CFI_NUM_REGS; i++)
931 if (state->regs[i].base != initial_func_cfi.regs[i].base ||
932 state->regs[i].offset != initial_func_cfi.regs[i].offset)
933 return true;
934
935 return false;
936}
937
938static bool has_valid_stack_frame(struct insn_state *state)
939{
940 if (state->cfa.base == CFI_BP && state->regs[CFI_BP].base == CFI_CFA &&
941 state->regs[CFI_BP].offset == -16)
942 return true;
943
944 if (state->drap && state->regs[CFI_BP].base == CFI_BP)
945 return true;
946
947 return false;
948}
949
950static void save_reg(struct insn_state *state, unsigned char reg, int base,
951 int offset)
952{
953 if ((arch_callee_saved_reg(reg) ||
954 (state->drap && reg == state->drap_reg)) &&
955 state->regs[reg].base == CFI_UNDEFINED) {
956 state->regs[reg].base = base;
957 state->regs[reg].offset = offset;
958 }
959}
960
961static void restore_reg(struct insn_state *state, unsigned char reg)
962{
963 state->regs[reg].base = CFI_UNDEFINED;
964 state->regs[reg].offset = 0;
965}
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020static int update_insn_state(struct instruction *insn, struct insn_state *state)
1021{
1022 struct stack_op *op = &insn->stack_op;
1023 struct cfi_reg *cfa = &state->cfa;
1024 struct cfi_reg *regs = state->regs;
1025
1026
1027 if (cfa->base == CFI_UNDEFINED) {
1028 if (insn->func) {
1029 WARN_FUNC("undefined stack state", insn->sec, insn->offset);
1030 return -1;
1031 }
1032 return 0;
1033 }
1034
1035 switch (op->dest.type) {
1036
1037 case OP_DEST_REG:
1038 switch (op->src.type) {
1039
1040 case OP_SRC_REG:
1041 if (cfa->base == op->src.reg && cfa->base == CFI_SP &&
1042 op->dest.reg == CFI_BP && regs[CFI_BP].base == CFI_CFA &&
1043 regs[CFI_BP].offset == -cfa->offset) {
1044
1045
1046 cfa->base = op->dest.reg;
1047 state->bp_scratch = false;
1048 } else if (state->drap) {
1049
1050
1051 regs[CFI_BP].base = CFI_BP;
1052 regs[CFI_BP].offset = -state->stack_size;
1053 state->bp_scratch = false;
1054 } else if (!nofp) {
1055
1056 WARN_FUNC("unknown stack-related register move",
1057 insn->sec, insn->offset);
1058 return -1;
1059 }
1060
1061 break;
1062
1063 case OP_SRC_ADD:
1064 if (op->dest.reg == CFI_SP && op->src.reg == CFI_SP) {
1065
1066
1067 state->stack_size -= op->src.offset;
1068 if (cfa->base == CFI_SP)
1069 cfa->offset -= op->src.offset;
1070 break;
1071 }
1072
1073 if (op->dest.reg == CFI_SP && op->src.reg == CFI_BP) {
1074
1075
1076 state->stack_size = -(op->src.offset + regs[CFI_BP].offset);
1077 break;
1078 }
1079
1080 if (op->dest.reg != CFI_BP && op->src.reg == CFI_SP &&
1081 cfa->base == CFI_SP) {
1082
1083
1084 state->drap_reg = op->dest.reg;
1085 break;
1086 }
1087
1088 if (state->drap && op->dest.reg == CFI_SP &&
1089 op->src.reg == state->drap_reg) {
1090
1091
1092 cfa->base = CFI_SP;
1093 cfa->offset = state->stack_size = -op->src.offset;
1094 state->drap_reg = CFI_UNDEFINED;
1095 state->drap = false;
1096 break;
1097 }
1098
1099 if (op->dest.reg == state->cfa.base) {
1100 WARN_FUNC("unsupported stack register modification",
1101 insn->sec, insn->offset);
1102 return -1;
1103 }
1104
1105 break;
1106
1107 case OP_SRC_AND:
1108 if (op->dest.reg != CFI_SP ||
1109 (state->drap_reg != CFI_UNDEFINED && cfa->base != CFI_SP) ||
1110 (state->drap_reg == CFI_UNDEFINED && cfa->base != CFI_BP)) {
1111 WARN_FUNC("unsupported stack pointer realignment",
1112 insn->sec, insn->offset);
1113 return -1;
1114 }
1115
1116 if (state->drap_reg != CFI_UNDEFINED) {
1117
1118 cfa->base = state->drap_reg;
1119 cfa->offset = state->stack_size = 0;
1120 state->drap = true;
1121
1122 }
1123
1124
1125
1126
1127
1128
1129 break;
1130
1131 case OP_SRC_POP:
1132 if (!state->drap && op->dest.type == OP_DEST_REG &&
1133 op->dest.reg == cfa->base) {
1134
1135
1136 cfa->base = CFI_SP;
1137 }
1138
1139 if (regs[op->dest.reg].offset == -state->stack_size) {
1140
1141 if (state->drap && cfa->base == CFI_BP_INDIRECT &&
1142 op->dest.type == OP_DEST_REG &&
1143 op->dest.reg == state->drap_reg) {
1144
1145
1146 cfa->base = state->drap_reg;
1147 cfa->offset = 0;
1148 }
1149
1150 restore_reg(state, op->dest.reg);
1151 }
1152
1153 state->stack_size -= 8;
1154 if (cfa->base == CFI_SP)
1155 cfa->offset -= 8;
1156
1157 break;
1158
1159 case OP_SRC_REG_INDIRECT:
1160 if (state->drap && op->src.reg == CFI_BP &&
1161 op->src.offset == regs[op->dest.reg].offset) {
1162
1163
1164 if (op->dest.reg == state->drap_reg) {
1165 cfa->base = state->drap_reg;
1166 cfa->offset = 0;
1167 }
1168
1169 restore_reg(state, op->dest.reg);
1170
1171 } else if (op->src.reg == cfa->base &&
1172 op->src.offset == regs[op->dest.reg].offset + cfa->offset) {
1173
1174
1175
1176 restore_reg(state, op->dest.reg);
1177 }
1178
1179 break;
1180
1181 default:
1182 WARN_FUNC("unknown stack-related instruction",
1183 insn->sec, insn->offset);
1184 return -1;
1185 }
1186
1187 break;
1188
1189 case OP_DEST_PUSH:
1190 state->stack_size += 8;
1191 if (cfa->base == CFI_SP)
1192 cfa->offset += 8;
1193
1194 if (op->src.type != OP_SRC_REG)
1195 break;
1196
1197 if (state->drap) {
1198 if (op->src.reg == cfa->base && op->src.reg == state->drap_reg) {
1199
1200
1201 cfa->base = CFI_BP_INDIRECT;
1202 cfa->offset = -state->stack_size;
1203
1204
1205 save_reg(state, op->src.reg, CFI_CFA, -state->stack_size);
1206
1207 } else if (op->src.reg == CFI_BP && cfa->base == state->drap_reg) {
1208
1209
1210 state->stack_size = 0;
1211
1212 } else if (regs[op->src.reg].base == CFI_UNDEFINED) {
1213
1214
1215 save_reg(state, op->src.reg, CFI_BP, -state->stack_size);
1216 }
1217
1218 } else {
1219
1220
1221 save_reg(state, op->src.reg, CFI_CFA, -state->stack_size);
1222 }
1223
1224
1225 if (!nofp && insn->func && op->src.reg == CFI_BP &&
1226 cfa->base != CFI_BP)
1227 state->bp_scratch = true;
1228 break;
1229
1230 case OP_DEST_REG_INDIRECT:
1231
1232 if (state->drap) {
1233 if (op->src.reg == cfa->base && op->src.reg == state->drap_reg) {
1234
1235
1236 cfa->base = CFI_BP_INDIRECT;
1237 cfa->offset = op->dest.offset;
1238
1239
1240 save_reg(state, op->src.reg, CFI_CFA, op->dest.offset);
1241 }
1242
1243 else if (regs[op->src.reg].base == CFI_UNDEFINED) {
1244
1245
1246 save_reg(state, op->src.reg, CFI_BP, op->dest.offset);
1247 }
1248
1249 } else if (op->dest.reg == cfa->base) {
1250
1251
1252
1253 save_reg(state, op->src.reg, CFI_CFA,
1254 op->dest.offset - state->cfa.offset);
1255 }
1256
1257 break;
1258
1259 case OP_DEST_LEAVE:
1260 if ((!state->drap && cfa->base != CFI_BP) ||
1261 (state->drap && cfa->base != state->drap_reg)) {
1262 WARN_FUNC("leave instruction with modified stack frame",
1263 insn->sec, insn->offset);
1264 return -1;
1265 }
1266
1267
1268
1269 state->stack_size = -state->regs[CFI_BP].offset - 8;
1270 restore_reg(state, CFI_BP);
1271
1272 if (!state->drap) {
1273 cfa->base = CFI_SP;
1274 cfa->offset -= 8;
1275 }
1276
1277 break;
1278
1279 case OP_DEST_MEM:
1280 if (op->src.type != OP_SRC_POP) {
1281 WARN_FUNC("unknown stack-related memory operation",
1282 insn->sec, insn->offset);
1283 return -1;
1284 }
1285
1286
1287 state->stack_size -= 8;
1288 if (cfa->base == CFI_SP)
1289 cfa->offset -= 8;
1290
1291 break;
1292
1293 default:
1294 WARN_FUNC("unknown stack-related instruction",
1295 insn->sec, insn->offset);
1296 return -1;
1297 }
1298
1299 return 0;
1300}
1301
1302static bool insn_state_match(struct instruction *insn, struct insn_state *state)
1303{
1304 struct insn_state *state1 = &insn->state, *state2 = state;
1305 int i;
1306
1307 if (memcmp(&state1->cfa, &state2->cfa, sizeof(state1->cfa))) {
1308 WARN_FUNC("stack state mismatch: cfa1=%d%+d cfa2=%d%+d",
1309 insn->sec, insn->offset,
1310 state1->cfa.base, state1->cfa.offset,
1311 state2->cfa.base, state2->cfa.offset);
1312
1313 } else if (memcmp(&state1->regs, &state2->regs, sizeof(state1->regs))) {
1314 for (i = 0; i < CFI_NUM_REGS; i++) {
1315 if (!memcmp(&state1->regs[i], &state2->regs[i],
1316 sizeof(struct cfi_reg)))
1317 continue;
1318
1319 WARN_FUNC("stack state mismatch: reg1[%d]=%d%+d reg2[%d]=%d%+d",
1320 insn->sec, insn->offset,
1321 i, state1->regs[i].base, state1->regs[i].offset,
1322 i, state2->regs[i].base, state2->regs[i].offset);
1323 break;
1324 }
1325
1326 } else if (state1->drap != state2->drap ||
1327 (state1->drap && state1->drap_reg != state2->drap_reg)) {
1328 WARN_FUNC("stack state mismatch: drap1=%d(%d) drap2=%d(%d)",
1329 insn->sec, insn->offset,
1330 state1->drap, state1->drap_reg,
1331 state2->drap, state2->drap_reg);
1332
1333 } else
1334 return true;
1335
1336 return false;
1337}
1338
1339
1340
1341
1342
1343
1344
1345static int validate_branch(struct objtool_file *file, struct instruction *first,
1346 struct insn_state state)
1347{
1348 struct alternative *alt;
1349 struct instruction *insn;
1350 struct section *sec;
1351 struct symbol *func = NULL;
1352 int ret;
1353
1354 insn = first;
1355 sec = insn->sec;
1356
1357 if (insn->alt_group && list_empty(&insn->alts)) {
1358 WARN_FUNC("don't know how to handle branch to middle of alternative instruction group",
1359 sec, insn->offset);
1360 return -1;
1361 }
1362
1363 while (1) {
1364 if (file->c_file && insn->func) {
1365 if (func && func != insn->func) {
1366 WARN("%s() falls through to next function %s()",
1367 func->name, insn->func->name);
1368 return 1;
1369 }
1370 }
1371
1372 func = insn->func;
1373
1374 if (func && insn->ignore) {
1375 WARN_FUNC("BUG: why am I validating an ignored function?",
1376 sec, insn->offset);
1377 return -1;
1378 }
1379
1380 if (insn->visited) {
1381 if (!!insn_state_match(insn, &state))
1382 return 1;
1383
1384 return 0;
1385 }
1386
1387 insn->state = state;
1388
1389 insn->visited = true;
1390
1391 list_for_each_entry(alt, &insn->alts, list) {
1392 ret = validate_branch(file, alt->insn, state);
1393 if (ret)
1394 return 1;
1395 }
1396
1397 switch (insn->type) {
1398
1399 case INSN_RETURN:
1400 if (func && has_modified_stack_frame(&state)) {
1401 WARN_FUNC("return with modified stack frame",
1402 sec, insn->offset);
1403 return 1;
1404 }
1405
1406 if (state.bp_scratch) {
1407 WARN("%s uses BP as a scratch register",
1408 insn->func->name);
1409 return 1;
1410 }
1411
1412 return 0;
1413
1414 case INSN_CALL:
1415 if (is_fentry_call(insn))
1416 break;
1417
1418 ret = dead_end_function(file, insn->call_dest);
1419 if (ret == 1)
1420 return 0;
1421 if (ret == -1)
1422 return 1;
1423
1424
1425 case INSN_CALL_DYNAMIC:
1426 if (!nofp && func && !has_valid_stack_frame(&state)) {
1427 WARN_FUNC("call without frame pointer save/setup",
1428 sec, insn->offset);
1429 return 1;
1430 }
1431 break;
1432
1433 case INSN_JUMP_CONDITIONAL:
1434 case INSN_JUMP_UNCONDITIONAL:
1435 if (insn->jump_dest &&
1436 (!func || !insn->jump_dest->func ||
1437 func == insn->jump_dest->func)) {
1438 ret = validate_branch(file, insn->jump_dest,
1439 state);
1440 if (ret)
1441 return 1;
1442
1443 } else if (func && has_modified_stack_frame(&state)) {
1444 WARN_FUNC("sibling call from callable instruction with modified stack frame",
1445 sec, insn->offset);
1446 return 1;
1447 }
1448
1449 if (insn->type == INSN_JUMP_UNCONDITIONAL)
1450 return 0;
1451
1452 break;
1453
1454 case INSN_JUMP_DYNAMIC:
1455 if (func && list_empty(&insn->alts) &&
1456 has_modified_stack_frame(&state)) {
1457 WARN_FUNC("sibling call from callable instruction with modified stack frame",
1458 sec, insn->offset);
1459 return 1;
1460 }
1461
1462 return 0;
1463
1464 case INSN_STACK:
1465 if (update_insn_state(insn, &state))
1466 return -1;
1467
1468 break;
1469
1470 default:
1471 break;
1472 }
1473
1474 if (insn->dead_end)
1475 return 0;
1476
1477 insn = next_insn_same_sec(file, insn);
1478 if (!insn) {
1479 WARN("%s: unexpected end of section", sec->name);
1480 return 1;
1481 }
1482 }
1483
1484 return 0;
1485}
1486
1487static bool is_kasan_insn(struct instruction *insn)
1488{
1489 return (insn->type == INSN_CALL &&
1490 !strcmp(insn->call_dest->name, "__asan_handle_no_return"));
1491}
1492
1493static bool is_ubsan_insn(struct instruction *insn)
1494{
1495 return (insn->type == INSN_CALL &&
1496 !strcmp(insn->call_dest->name,
1497 "__ubsan_handle_builtin_unreachable"));
1498}
1499
1500static bool ignore_unreachable_insn(struct instruction *insn)
1501{
1502 int i;
1503
1504 if (insn->ignore || insn->type == INSN_NOP)
1505 return true;
1506
1507
1508
1509
1510
1511 if (!strcmp(insn->sec->name, ".fixup"))
1512 return true;
1513
1514
1515
1516
1517
1518
1519
1520 if (!insn->func)
1521 return false;
1522 for (i = 0; i < 5; i++) {
1523
1524 if (is_kasan_insn(insn) || is_ubsan_insn(insn))
1525 return true;
1526
1527 if (insn->type == INSN_JUMP_UNCONDITIONAL && insn->jump_dest) {
1528 insn = insn->jump_dest;
1529 continue;
1530 }
1531
1532 if (insn->offset + insn->len >= insn->func->offset + insn->func->len)
1533 break;
1534 insn = list_next_entry(insn, list);
1535 }
1536
1537 return false;
1538}
1539
1540static int validate_functions(struct objtool_file *file)
1541{
1542 struct section *sec;
1543 struct symbol *func;
1544 struct instruction *insn;
1545 struct insn_state state;
1546 int ret, warnings = 0;
1547
1548 clear_insn_state(&state);
1549
1550 state.cfa = initial_func_cfi.cfa;
1551 memcpy(&state.regs, &initial_func_cfi.regs,
1552 CFI_NUM_REGS * sizeof(struct cfi_reg));
1553 state.stack_size = initial_func_cfi.cfa.offset;
1554
1555 for_each_sec(file, sec) {
1556 list_for_each_entry(func, &sec->symbol_list, list) {
1557 if (func->type != STT_FUNC)
1558 continue;
1559
1560 insn = find_insn(file, sec, func->offset);
1561 if (!insn || insn->ignore)
1562 continue;
1563
1564 ret = validate_branch(file, insn, state);
1565 warnings += ret;
1566 }
1567 }
1568
1569 return warnings;
1570}
1571
1572static int validate_reachable_instructions(struct objtool_file *file)
1573{
1574 struct instruction *insn;
1575
1576 if (file->ignore_unreachables)
1577 return 0;
1578
1579 for_each_insn(file, insn) {
1580 if (insn->visited || ignore_unreachable_insn(insn))
1581 continue;
1582
1583
1584
1585
1586
1587
1588
1589 if (gcov_enabled(file))
1590 return 0;
1591
1592 WARN_FUNC("unreachable instruction", insn->sec, insn->offset);
1593 return 1;
1594 }
1595
1596 return 0;
1597}
1598
1599static void cleanup(struct objtool_file *file)
1600{
1601 struct instruction *insn, *tmpinsn;
1602 struct alternative *alt, *tmpalt;
1603
1604 list_for_each_entry_safe(insn, tmpinsn, &file->insn_list, list) {
1605 list_for_each_entry_safe(alt, tmpalt, &insn->alts, list) {
1606 list_del(&alt->list);
1607 free(alt);
1608 }
1609 list_del(&insn->list);
1610 hash_del(&insn->hash);
1611 free(insn);
1612 }
1613 elf_close(file->elf);
1614}
1615
1616int check(const char *_objname, bool _nofp)
1617{
1618 struct objtool_file file;
1619 int ret, warnings = 0;
1620
1621 objname = _objname;
1622 nofp = _nofp;
1623
1624 file.elf = elf_open(objname);
1625 if (!file.elf)
1626 return 1;
1627
1628 INIT_LIST_HEAD(&file.insn_list);
1629 hash_init(file.insn_hash);
1630 file.whitelist = find_section_by_name(file.elf, ".discard.func_stack_frame_non_standard");
1631 file.rodata = find_section_by_name(file.elf, ".rodata");
1632 file.ignore_unreachables = false;
1633 file.c_file = find_section_by_name(file.elf, ".comment");
1634
1635 arch_initial_func_cfi_state(&initial_func_cfi);
1636
1637 ret = decode_sections(&file);
1638 if (ret < 0)
1639 goto out;
1640 warnings += ret;
1641
1642 if (list_empty(&file.insn_list))
1643 goto out;
1644
1645 ret = validate_functions(&file);
1646 if (ret < 0)
1647 goto out;
1648 warnings += ret;
1649
1650 if (!warnings) {
1651 ret = validate_reachable_instructions(&file);
1652 if (ret < 0)
1653 goto out;
1654 warnings += ret;
1655 }
1656
1657out:
1658 cleanup(&file);
1659
1660
1661 if (ret || warnings)
1662 return 0;
1663 return 0;
1664}
1665