aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/include/asm/cpufeature.h15
-rw-r--r--arch/x86/include/asm/insn.h18
-rw-r--r--arch/x86/kernel/kprobes/core.c4
-rw-r--r--arch/x86/kernel/machine_kexec_32.c6
-rw-r--r--arch/x86/kernel/machine_kexec_64.c5
-rw-r--r--arch/x86/kernel/uprobes.c4
-rw-r--r--samples/bpf/Makefile2
-rw-r--r--tools/objtool/arch/x86/include/asm/insn.h18
-rw-r--r--tools/objtool/check.c167
-rw-r--r--tools/objtool/elf.c42
-rw-r--r--tools/objtool/elf.h2
11 files changed, 206 insertions, 77 deletions
diff --git a/arch/x86/include/asm/cpufeature.h b/arch/x86/include/asm/cpufeature.h
index b27da9602a6d..aced6c9290d6 100644
--- a/arch/x86/include/asm/cpufeature.h
+++ b/arch/x86/include/asm/cpufeature.h
@@ -140,6 +140,20 @@ extern void clear_cpu_cap(struct cpuinfo_x86 *c, unsigned int bit);
140 140
141#define setup_force_cpu_bug(bit) setup_force_cpu_cap(bit) 141#define setup_force_cpu_bug(bit) setup_force_cpu_cap(bit)
142 142
143#if defined(__clang__) && !defined(CC_HAVE_ASM_GOTO)
144
145/*
146 * Workaround for the sake of BPF compilation which utilizes kernel
147 * headers, but clang does not support ASM GOTO and fails the build.
148 */
149#ifndef __BPF_TRACING__
150#warning "Compiler lacks ASM_GOTO support. Add -D __BPF_TRACING__ to your compiler arguments"
151#endif
152
153#define static_cpu_has(bit) boot_cpu_has(bit)
154
155#else
156
143/* 157/*
144 * Static testing of CPU features. Used the same as boot_cpu_has(). 158 * Static testing of CPU features. Used the same as boot_cpu_has().
145 * These will statically patch the target code for additional 159 * These will statically patch the target code for additional
@@ -195,6 +209,7 @@ t_no:
195 boot_cpu_has(bit) : \ 209 boot_cpu_has(bit) : \
196 _static_cpu_has(bit) \ 210 _static_cpu_has(bit) \
197) 211)
212#endif
198 213
199#define cpu_has_bug(c, bit) cpu_has(c, (bit)) 214#define cpu_has_bug(c, bit) cpu_has(c, (bit))
200#define set_cpu_bug(c, bit) set_cpu_cap(c, (bit)) 215#define set_cpu_bug(c, bit) set_cpu_cap(c, (bit))
diff --git a/arch/x86/include/asm/insn.h b/arch/x86/include/asm/insn.h
index b3e32b010ab1..c2c01f84df75 100644
--- a/arch/x86/include/asm/insn.h
+++ b/arch/x86/include/asm/insn.h
@@ -208,4 +208,22 @@ static inline int insn_offset_immediate(struct insn *insn)
208 return insn_offset_displacement(insn) + insn->displacement.nbytes; 208 return insn_offset_displacement(insn) + insn->displacement.nbytes;
209} 209}
210 210
211#define POP_SS_OPCODE 0x1f
212#define MOV_SREG_OPCODE 0x8e
213
214/*
215 * Intel SDM Vol.3A 6.8.3 states;
216 * "Any single-step trap that would be delivered following the MOV to SS
217 * instruction or POP to SS instruction (because EFLAGS.TF is 1) is
218 * suppressed."
219 * This function returns true if @insn is MOV SS or POP SS. On these
220 * instructions, single stepping is suppressed.
221 */
222static inline int insn_masking_exception(struct insn *insn)
223{
224 return insn->opcode.bytes[0] == POP_SS_OPCODE ||
225 (insn->opcode.bytes[0] == MOV_SREG_OPCODE &&
226 X86_MODRM_REG(insn->modrm.bytes[0]) == 2);
227}
228
211#endif /* _ASM_X86_INSN_H */ 229#endif /* _ASM_X86_INSN_H */
diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c
index 0715f827607c..6f4d42377fe5 100644
--- a/arch/x86/kernel/kprobes/core.c
+++ b/arch/x86/kernel/kprobes/core.c
@@ -370,6 +370,10 @@ int __copy_instruction(u8 *dest, u8 *src, u8 *real, struct insn *insn)
370 if (insn->opcode.bytes[0] == BREAKPOINT_INSTRUCTION) 370 if (insn->opcode.bytes[0] == BREAKPOINT_INSTRUCTION)
371 return 0; 371 return 0;
372 372
373 /* We should not singlestep on the exception masking instructions */
374 if (insn_masking_exception(insn))
375 return 0;
376
373#ifdef CONFIG_X86_64 377#ifdef CONFIG_X86_64
374 /* Only x86_64 has RIP relative instructions */ 378 /* Only x86_64 has RIP relative instructions */
375 if (insn_rip_relative(insn)) { 379 if (insn_rip_relative(insn)) {
diff --git a/arch/x86/kernel/machine_kexec_32.c b/arch/x86/kernel/machine_kexec_32.c
index 60cdec6628b0..d1ab07ec8c9a 100644
--- a/arch/x86/kernel/machine_kexec_32.c
+++ b/arch/x86/kernel/machine_kexec_32.c
@@ -57,12 +57,17 @@ static void load_segments(void)
57static void machine_kexec_free_page_tables(struct kimage *image) 57static void machine_kexec_free_page_tables(struct kimage *image)
58{ 58{
59 free_page((unsigned long)image->arch.pgd); 59 free_page((unsigned long)image->arch.pgd);
60 image->arch.pgd = NULL;
60#ifdef CONFIG_X86_PAE 61#ifdef CONFIG_X86_PAE
61 free_page((unsigned long)image->arch.pmd0); 62 free_page((unsigned long)image->arch.pmd0);
63 image->arch.pmd0 = NULL;
62 free_page((unsigned long)image->arch.pmd1); 64 free_page((unsigned long)image->arch.pmd1);
65 image->arch.pmd1 = NULL;
63#endif 66#endif
64 free_page((unsigned long)image->arch.pte0); 67 free_page((unsigned long)image->arch.pte0);
68 image->arch.pte0 = NULL;
65 free_page((unsigned long)image->arch.pte1); 69 free_page((unsigned long)image->arch.pte1);
70 image->arch.pte1 = NULL;
66} 71}
67 72
68static int machine_kexec_alloc_page_tables(struct kimage *image) 73static int machine_kexec_alloc_page_tables(struct kimage *image)
@@ -79,7 +84,6 @@ static int machine_kexec_alloc_page_tables(struct kimage *image)
79 !image->arch.pmd0 || !image->arch.pmd1 || 84 !image->arch.pmd0 || !image->arch.pmd1 ||
80#endif 85#endif
81 !image->arch.pte0 || !image->arch.pte1) { 86 !image->arch.pte0 || !image->arch.pte1) {
82 machine_kexec_free_page_tables(image);
83 return -ENOMEM; 87 return -ENOMEM;
84 } 88 }
85 return 0; 89 return 0;
diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c
index a5e55d832d0a..6010449ca6d2 100644
--- a/arch/x86/kernel/machine_kexec_64.c
+++ b/arch/x86/kernel/machine_kexec_64.c
@@ -39,9 +39,13 @@ const struct kexec_file_ops * const kexec_file_loaders[] = {
39static void free_transition_pgtable(struct kimage *image) 39static void free_transition_pgtable(struct kimage *image)
40{ 40{
41 free_page((unsigned long)image->arch.p4d); 41 free_page((unsigned long)image->arch.p4d);
42 image->arch.p4d = NULL;
42 free_page((unsigned long)image->arch.pud); 43 free_page((unsigned long)image->arch.pud);
44 image->arch.pud = NULL;
43 free_page((unsigned long)image->arch.pmd); 45 free_page((unsigned long)image->arch.pmd);
46 image->arch.pmd = NULL;
44 free_page((unsigned long)image->arch.pte); 47 free_page((unsigned long)image->arch.pte);
48 image->arch.pte = NULL;
45} 49}
46 50
47static int init_transition_pgtable(struct kimage *image, pgd_t *pgd) 51static int init_transition_pgtable(struct kimage *image, pgd_t *pgd)
@@ -91,7 +95,6 @@ static int init_transition_pgtable(struct kimage *image, pgd_t *pgd)
91 set_pte(pte, pfn_pte(paddr >> PAGE_SHIFT, PAGE_KERNEL_EXEC_NOENC)); 95 set_pte(pte, pfn_pte(paddr >> PAGE_SHIFT, PAGE_KERNEL_EXEC_NOENC));
92 return 0; 96 return 0;
93err: 97err:
94 free_transition_pgtable(image);
95 return result; 98 return result;
96} 99}
97 100
diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c
index 85c7ef23d99f..c84bb5396958 100644
--- a/arch/x86/kernel/uprobes.c
+++ b/arch/x86/kernel/uprobes.c
@@ -299,6 +299,10 @@ static int uprobe_init_insn(struct arch_uprobe *auprobe, struct insn *insn, bool
299 if (is_prefix_bad(insn)) 299 if (is_prefix_bad(insn))
300 return -ENOTSUPP; 300 return -ENOTSUPP;
301 301
302 /* We should not singlestep on the exception masking instructions */
303 if (insn_masking_exception(insn))
304 return -ENOTSUPP;
305
302 if (x86_64) 306 if (x86_64)
303 good_insns = good_insns_64; 307 good_insns = good_insns_64;
304 else 308 else
diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
index 4d6a6edd4bf6..092947676143 100644
--- a/samples/bpf/Makefile
+++ b/samples/bpf/Makefile
@@ -255,7 +255,7 @@ $(obj)/tracex5_kern.o: $(obj)/syscall_nrs.h
255$(obj)/%.o: $(src)/%.c 255$(obj)/%.o: $(src)/%.c
256 $(CLANG) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) $(EXTRA_CFLAGS) -I$(obj) \ 256 $(CLANG) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) $(EXTRA_CFLAGS) -I$(obj) \
257 -I$(srctree)/tools/testing/selftests/bpf/ \ 257 -I$(srctree)/tools/testing/selftests/bpf/ \
258 -D__KERNEL__ -Wno-unused-value -Wno-pointer-sign \ 258 -D__KERNEL__ -D__BPF_TRACING__ -Wno-unused-value -Wno-pointer-sign \
259 -D__TARGET_ARCH_$(ARCH) -Wno-compare-distinct-pointer-types \ 259 -D__TARGET_ARCH_$(ARCH) -Wno-compare-distinct-pointer-types \
260 -Wno-gnu-variable-sized-type-not-at-end \ 260 -Wno-gnu-variable-sized-type-not-at-end \
261 -Wno-address-of-packed-member -Wno-tautological-compare \ 261 -Wno-address-of-packed-member -Wno-tautological-compare \
diff --git a/tools/objtool/arch/x86/include/asm/insn.h b/tools/objtool/arch/x86/include/asm/insn.h
index b3e32b010ab1..c2c01f84df75 100644
--- a/tools/objtool/arch/x86/include/asm/insn.h
+++ b/tools/objtool/arch/x86/include/asm/insn.h
@@ -208,4 +208,22 @@ static inline int insn_offset_immediate(struct insn *insn)
208 return insn_offset_displacement(insn) + insn->displacement.nbytes; 208 return insn_offset_displacement(insn) + insn->displacement.nbytes;
209} 209}
210 210
211#define POP_SS_OPCODE 0x1f
212#define MOV_SREG_OPCODE 0x8e
213
214/*
215 * Intel SDM Vol.3A 6.8.3 states;
216 * "Any single-step trap that would be delivered following the MOV to SS
217 * instruction or POP to SS instruction (because EFLAGS.TF is 1) is
218 * suppressed."
219 * This function returns true if @insn is MOV SS or POP SS. On these
220 * instructions, single stepping is suppressed.
221 */
222static inline int insn_masking_exception(struct insn *insn)
223{
224 return insn->opcode.bytes[0] == POP_SS_OPCODE ||
225 (insn->opcode.bytes[0] == MOV_SREG_OPCODE &&
226 X86_MODRM_REG(insn->modrm.bytes[0]) == 2);
227}
228
211#endif /* _ASM_X86_INSN_H */ 229#endif /* _ASM_X86_INSN_H */
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 5409f6f6c48d..3a31b238f885 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -59,6 +59,31 @@ static struct instruction *next_insn_same_sec(struct objtool_file *file,
59 return next; 59 return next;
60} 60}
61 61
62static struct instruction *next_insn_same_func(struct objtool_file *file,
63 struct instruction *insn)
64{
65 struct instruction *next = list_next_entry(insn, list);
66 struct symbol *func = insn->func;
67
68 if (!func)
69 return NULL;
70
71 if (&next->list != &file->insn_list && next->func == func)
72 return next;
73
74 /* Check if we're already in the subfunction: */
75 if (func == func->cfunc)
76 return NULL;
77
78 /* Move to the subfunction: */
79 return find_insn(file, func->cfunc->sec, func->cfunc->offset);
80}
81
82#define func_for_each_insn_all(file, func, insn) \
83 for (insn = find_insn(file, func->sec, func->offset); \
84 insn; \
85 insn = next_insn_same_func(file, insn))
86
62#define func_for_each_insn(file, func, insn) \ 87#define func_for_each_insn(file, func, insn) \
63 for (insn = find_insn(file, func->sec, func->offset); \ 88 for (insn = find_insn(file, func->sec, func->offset); \
64 insn && &insn->list != &file->insn_list && \ 89 insn && &insn->list != &file->insn_list && \
@@ -149,10 +174,14 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func,
149 if (!strcmp(func->name, global_noreturns[i])) 174 if (!strcmp(func->name, global_noreturns[i]))
150 return 1; 175 return 1;
151 176
152 if (!func->sec) 177 if (!func->len)
153 return 0; 178 return 0;
154 179
155 func_for_each_insn(file, func, insn) { 180 insn = find_insn(file, func->sec, func->offset);
181 if (!insn->func)
182 return 0;
183
184 func_for_each_insn_all(file, func, insn) {
156 empty = false; 185 empty = false;
157 186
158 if (insn->type == INSN_RETURN) 187 if (insn->type == INSN_RETURN)
@@ -167,35 +196,28 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func,
167 * case, the function's dead-end status depends on whether the target 196 * case, the function's dead-end status depends on whether the target
168 * of the sibling call returns. 197 * of the sibling call returns.
169 */ 198 */
170 func_for_each_insn(file, func, insn) { 199 func_for_each_insn_all(file, func, insn) {
171 if (insn->sec != func->sec ||
172 insn->offset >= func->offset + func->len)
173 break;
174
175 if (insn->type == INSN_JUMP_UNCONDITIONAL) { 200 if (insn->type == INSN_JUMP_UNCONDITIONAL) {
176 struct instruction *dest = insn->jump_dest; 201 struct instruction *dest = insn->jump_dest;
177 struct symbol *dest_func;
178 202
179 if (!dest) 203 if (!dest)
180 /* sibling call to another file */ 204 /* sibling call to another file */
181 return 0; 205 return 0;
182 206
183 if (dest->sec != func->sec || 207 if (dest->func && dest->func->pfunc != insn->func->pfunc) {
184 dest->offset < func->offset ||
185 dest->offset >= func->offset + func->len) {
186 /* local sibling call */
187 dest_func = find_symbol_by_offset(dest->sec,
188 dest->offset);
189 if (!dest_func)
190 continue;
191 208
209 /* local sibling call */
192 if (recursion == 5) { 210 if (recursion == 5) {
193 WARN_FUNC("infinite recursion (objtool bug!)", 211 /*
194 dest->sec, dest->offset); 212 * Infinite recursion: two functions
195 return -1; 213 * have sibling calls to each other.
214 * This is a very rare case. It means
215 * they aren't dead ends.
216 */
217 return 0;
196 } 218 }
197 219
198 return __dead_end_function(file, dest_func, 220 return __dead_end_function(file, dest->func,
199 recursion + 1); 221 recursion + 1);
200 } 222 }
201 } 223 }
@@ -422,7 +444,7 @@ static void add_ignores(struct objtool_file *file)
422 if (!ignore_func(file, func)) 444 if (!ignore_func(file, func))
423 continue; 445 continue;
424 446
425 func_for_each_insn(file, func, insn) 447 func_for_each_insn_all(file, func, insn)
426 insn->ignore = true; 448 insn->ignore = true;
427 } 449 }
428 } 450 }
@@ -782,30 +804,35 @@ out:
782 return ret; 804 return ret;
783} 805}
784 806
785static int add_switch_table(struct objtool_file *file, struct symbol *func, 807static int add_switch_table(struct objtool_file *file, struct instruction *insn,
786 struct instruction *insn, struct rela *table, 808 struct rela *table, struct rela *next_table)
787 struct rela *next_table)
788{ 809{
789 struct rela *rela = table; 810 struct rela *rela = table;
790 struct instruction *alt_insn; 811 struct instruction *alt_insn;
791 struct alternative *alt; 812 struct alternative *alt;
813 struct symbol *pfunc = insn->func->pfunc;
814 unsigned int prev_offset = 0;
792 815
793 list_for_each_entry_from(rela, &file->rodata->rela->rela_list, list) { 816 list_for_each_entry_from(rela, &file->rodata->rela->rela_list, list) {
794 if (rela == next_table) 817 if (rela == next_table)
795 break; 818 break;
796 819
797 if (rela->sym->sec != insn->sec || 820 /* Make sure the switch table entries are consecutive: */
798 rela->addend <= func->offset || 821 if (prev_offset && rela->offset != prev_offset + 8)
799 rela->addend >= func->offset + func->len)
800 break; 822 break;
801 823
802 alt_insn = find_insn(file, insn->sec, rela->addend); 824 /* Detect function pointers from contiguous objects: */
803 if (!alt_insn) { 825 if (rela->sym->sec == pfunc->sec &&
804 WARN("%s: can't find instruction at %s+0x%x", 826 rela->addend == pfunc->offset)
805 file->rodata->rela->name, insn->sec->name, 827 break;
806 rela->addend); 828
807 return -1; 829 alt_insn = find_insn(file, rela->sym->sec, rela->addend);
808 } 830 if (!alt_insn)
831 break;
832
833 /* Make sure the jmp dest is in the function or subfunction: */
834 if (alt_insn->func->pfunc != pfunc)
835 break;
809 836
810 alt = malloc(sizeof(*alt)); 837 alt = malloc(sizeof(*alt));
811 if (!alt) { 838 if (!alt) {
@@ -815,6 +842,13 @@ static int add_switch_table(struct objtool_file *file, struct symbol *func,
815 842
816 alt->insn = alt_insn; 843 alt->insn = alt_insn;
817 list_add_tail(&alt->list, &insn->alts); 844 list_add_tail(&alt->list, &insn->alts);
845 prev_offset = rela->offset;
846 }
847
848 if (!prev_offset) {
849 WARN_FUNC("can't find switch jump table",
850 insn->sec, insn->offset);
851 return -1;
818 } 852 }
819 853
820 return 0; 854 return 0;
@@ -869,40 +903,21 @@ static struct rela *find_switch_table(struct objtool_file *file,
869{ 903{
870 struct rela *text_rela, *rodata_rela; 904 struct rela *text_rela, *rodata_rela;
871 struct instruction *orig_insn = insn; 905 struct instruction *orig_insn = insn;
906 unsigned long table_offset;
872 907
873 text_rela = find_rela_by_dest_range(insn->sec, insn->offset, insn->len);
874 if (text_rela && text_rela->sym == file->rodata->sym) {
875 /* case 1 */
876 rodata_rela = find_rela_by_dest(file->rodata,
877 text_rela->addend);
878 if (rodata_rela)
879 return rodata_rela;
880
881 /* case 2 */
882 rodata_rela = find_rela_by_dest(file->rodata,
883 text_rela->addend + 4);
884 if (!rodata_rela)
885 return NULL;
886
887 file->ignore_unreachables = true;
888 return rodata_rela;
889 }
890
891 /* case 3 */
892 /* 908 /*
893 * Backward search using the @first_jump_src links, these help avoid 909 * Backward search using the @first_jump_src links, these help avoid
894 * much of the 'in between' code. Which avoids us getting confused by 910 * much of the 'in between' code. Which avoids us getting confused by
895 * it. 911 * it.
896 */ 912 */
897 for (insn = list_prev_entry(insn, list); 913 for (;
898
899 &insn->list != &file->insn_list && 914 &insn->list != &file->insn_list &&
900 insn->sec == func->sec && 915 insn->sec == func->sec &&
901 insn->offset >= func->offset; 916 insn->offset >= func->offset;
902 917
903 insn = insn->first_jump_src ?: list_prev_entry(insn, list)) { 918 insn = insn->first_jump_src ?: list_prev_entry(insn, list)) {
904 919
905 if (insn->type == INSN_JUMP_DYNAMIC) 920 if (insn != orig_insn && insn->type == INSN_JUMP_DYNAMIC)
906 break; 921 break;
907 922
908 /* allow small jumps within the range */ 923 /* allow small jumps within the range */
@@ -918,18 +933,29 @@ static struct rela *find_switch_table(struct objtool_file *file,
918 if (!text_rela || text_rela->sym != file->rodata->sym) 933 if (!text_rela || text_rela->sym != file->rodata->sym)
919 continue; 934 continue;
920 935
936 table_offset = text_rela->addend;
937 if (text_rela->type == R_X86_64_PC32)
938 table_offset += 4;
939
921 /* 940 /*
922 * Make sure the .rodata address isn't associated with a 941 * Make sure the .rodata address isn't associated with a
923 * symbol. gcc jump tables are anonymous data. 942 * symbol. gcc jump tables are anonymous data.
924 */ 943 */
925 if (find_symbol_containing(file->rodata, text_rela->addend)) 944 if (find_symbol_containing(file->rodata, table_offset))
926 continue; 945 continue;
927 946
928 rodata_rela = find_rela_by_dest(file->rodata, text_rela->addend); 947 rodata_rela = find_rela_by_dest(file->rodata, table_offset);
929 if (!rodata_rela) 948 if (rodata_rela) {
930 continue; 949 /*
950 * Use of RIP-relative switch jumps is quite rare, and
951 * indicates a rare GCC quirk/bug which can leave dead
952 * code behind.
953 */
954 if (text_rela->type == R_X86_64_PC32)
955 file->ignore_unreachables = true;
931 956
932 return rodata_rela; 957 return rodata_rela;
958 }
933 } 959 }
934 960
935 return NULL; 961 return NULL;
@@ -943,7 +969,7 @@ static int add_func_switch_tables(struct objtool_file *file,
943 struct rela *rela, *prev_rela = NULL; 969 struct rela *rela, *prev_rela = NULL;
944 int ret; 970 int ret;
945 971
946 func_for_each_insn(file, func, insn) { 972 func_for_each_insn_all(file, func, insn) {
947 if (!last) 973 if (!last)
948 last = insn; 974 last = insn;
949 975
@@ -974,8 +1000,7 @@ static int add_func_switch_tables(struct objtool_file *file,
974 * the beginning of another switch table in the same function. 1000 * the beginning of another switch table in the same function.
975 */ 1001 */
976 if (prev_jump) { 1002 if (prev_jump) {
977 ret = add_switch_table(file, func, prev_jump, prev_rela, 1003 ret = add_switch_table(file, prev_jump, prev_rela, rela);
978 rela);
979 if (ret) 1004 if (ret)
980 return ret; 1005 return ret;
981 } 1006 }
@@ -985,7 +1010,7 @@ static int add_func_switch_tables(struct objtool_file *file,
985 } 1010 }
986 1011
987 if (prev_jump) { 1012 if (prev_jump) {
988 ret = add_switch_table(file, func, prev_jump, prev_rela, NULL); 1013 ret = add_switch_table(file, prev_jump, prev_rela, NULL);
989 if (ret) 1014 if (ret)
990 return ret; 1015 return ret;
991 } 1016 }
@@ -1749,15 +1774,13 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
1749 while (1) { 1774 while (1) {
1750 next_insn = next_insn_same_sec(file, insn); 1775 next_insn = next_insn_same_sec(file, insn);
1751 1776
1752 1777 if (file->c_file && func && insn->func && func != insn->func->pfunc) {
1753 if (file->c_file && func && insn->func && func != insn->func) {
1754 WARN("%s() falls through to next function %s()", 1778 WARN("%s() falls through to next function %s()",
1755 func->name, insn->func->name); 1779 func->name, insn->func->name);
1756 return 1; 1780 return 1;
1757 } 1781 }
1758 1782
1759 if (insn->func) 1783 func = insn->func ? insn->func->pfunc : NULL;
1760 func = insn->func;
1761 1784
1762 if (func && insn->ignore) { 1785 if (func && insn->ignore) {
1763 WARN_FUNC("BUG: why am I validating an ignored function?", 1786 WARN_FUNC("BUG: why am I validating an ignored function?",
@@ -1778,7 +1801,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
1778 1801
1779 i = insn; 1802 i = insn;
1780 save_insn = NULL; 1803 save_insn = NULL;
1781 func_for_each_insn_continue_reverse(file, func, i) { 1804 func_for_each_insn_continue_reverse(file, insn->func, i) {
1782 if (i->save) { 1805 if (i->save) {
1783 save_insn = i; 1806 save_insn = i;
1784 break; 1807 break;
@@ -1865,7 +1888,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
1865 case INSN_JUMP_UNCONDITIONAL: 1888 case INSN_JUMP_UNCONDITIONAL:
1866 if (insn->jump_dest && 1889 if (insn->jump_dest &&
1867 (!func || !insn->jump_dest->func || 1890 (!func || !insn->jump_dest->func ||
1868 func == insn->jump_dest->func)) { 1891 insn->jump_dest->func->pfunc == func)) {
1869 ret = validate_branch(file, insn->jump_dest, 1892 ret = validate_branch(file, insn->jump_dest,
1870 state); 1893 state);
1871 if (ret) 1894 if (ret)
@@ -2060,7 +2083,7 @@ static int validate_functions(struct objtool_file *file)
2060 2083
2061 for_each_sec(file, sec) { 2084 for_each_sec(file, sec) {
2062 list_for_each_entry(func, &sec->symbol_list, list) { 2085 list_for_each_entry(func, &sec->symbol_list, list) {
2063 if (func->type != STT_FUNC) 2086 if (func->type != STT_FUNC || func->pfunc != func)
2064 continue; 2087 continue;
2065 2088
2066 insn = find_insn(file, sec, func->offset); 2089 insn = find_insn(file, sec, func->offset);
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index c1c338661699..4e60e105583e 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -79,6 +79,19 @@ struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset)
79 return NULL; 79 return NULL;
80} 80}
81 81
82struct symbol *find_symbol_by_name(struct elf *elf, const char *name)
83{
84 struct section *sec;
85 struct symbol *sym;
86
87 list_for_each_entry(sec, &elf->sections, list)
88 list_for_each_entry(sym, &sec->symbol_list, list)
89 if (!strcmp(sym->name, name))
90 return sym;
91
92 return NULL;
93}
94
82struct symbol *find_symbol_containing(struct section *sec, unsigned long offset) 95struct symbol *find_symbol_containing(struct section *sec, unsigned long offset)
83{ 96{
84 struct symbol *sym; 97 struct symbol *sym;
@@ -203,10 +216,11 @@ static int read_sections(struct elf *elf)
203 216
204static int read_symbols(struct elf *elf) 217static int read_symbols(struct elf *elf)
205{ 218{
206 struct section *symtab; 219 struct section *symtab, *sec;
207 struct symbol *sym; 220 struct symbol *sym, *pfunc;
208 struct list_head *entry, *tmp; 221 struct list_head *entry, *tmp;
209 int symbols_nr, i; 222 int symbols_nr, i;
223 char *coldstr;
210 224
211 symtab = find_section_by_name(elf, ".symtab"); 225 symtab = find_section_by_name(elf, ".symtab");
212 if (!symtab) { 226 if (!symtab) {
@@ -281,6 +295,30 @@ static int read_symbols(struct elf *elf)
281 hash_add(sym->sec->symbol_hash, &sym->hash, sym->idx); 295 hash_add(sym->sec->symbol_hash, &sym->hash, sym->idx);
282 } 296 }
283 297
298 /* Create parent/child links for any cold subfunctions */
299 list_for_each_entry(sec, &elf->sections, list) {
300 list_for_each_entry(sym, &sec->symbol_list, list) {
301 if (sym->type != STT_FUNC)
302 continue;
303 sym->pfunc = sym->cfunc = sym;
304 coldstr = strstr(sym->name, ".cold.");
305 if (coldstr) {
306 coldstr[0] = '\0';
307 pfunc = find_symbol_by_name(elf, sym->name);
308 coldstr[0] = '.';
309
310 if (!pfunc) {
311 WARN("%s(): can't find parent function",
312 sym->name);
313 goto err;
314 }
315
316 sym->pfunc = pfunc;
317 pfunc->cfunc = sym;
318 }
319 }
320 }
321
284 return 0; 322 return 0;
285 323
286err: 324err:
diff --git a/tools/objtool/elf.h b/tools/objtool/elf.h
index d86e2ff14466..de5cd2ddded9 100644
--- a/tools/objtool/elf.h
+++ b/tools/objtool/elf.h
@@ -61,6 +61,7 @@ struct symbol {
61 unsigned char bind, type; 61 unsigned char bind, type;
62 unsigned long offset; 62 unsigned long offset;
63 unsigned int len; 63 unsigned int len;
64 struct symbol *pfunc, *cfunc;
64}; 65};
65 66
66struct rela { 67struct rela {
@@ -86,6 +87,7 @@ struct elf {
86struct elf *elf_open(const char *name, int flags); 87struct elf *elf_open(const char *name, int flags);
87struct section *find_section_by_name(struct elf *elf, const char *name); 88struct section *find_section_by_name(struct elf *elf, const char *name);
88struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset); 89struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset);
90struct symbol *find_symbol_by_name(struct elf *elf, const char *name);
89struct symbol *find_symbol_containing(struct section *sec, unsigned long offset); 91struct symbol *find_symbol_containing(struct section *sec, unsigned long offset);
90struct rela *find_rela_by_dest(struct section *sec, unsigned long offset); 92struct rela *find_rela_by_dest(struct section *sec, unsigned long offset);
91struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset, 93struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset,