All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 00/26] objtool: vmlinux.o and noinstr validation
@ 2020-03-24 15:31 Peter Zijlstra
  2020-03-24 15:31 ` [PATCH v3 01/26] objtool: Introduce validate_return() Peter Zijlstra
                   ` (25 more replies)
  0 siblings, 26 replies; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 15:31 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, peterz, mhiramat, mbenes, brgerst

Hi all,

Once more, more patches... up 7 from last time.

As should be familiar by now; these patches implement the noinstr
(no-instrument) validation in objtool as requested by Thomas, to ensure
critical code (entry for now, idle later) run no unexpected code.

Functions are marked with: noinstr, which implies notrace, noinline and sticks
things in the .noinstr.text section. Such functions can then use instr_begin()
and instr_end() to allow calls to code outside of this section in sanctioned
areas.

Since a RELA does not include section information, we need to run objtool on
a vmlinux.o which includes all relevant functions; so the first 'few' patches
optimize objtool to allow running on vmlinux.o.

This all is starting to look good and barring any show-stoppers the plan is to
get this merged soonish. There's one wobbly in patch 18, Josh any
suggestions -- all I could come up with was ugleh.



^ permalink raw reply	[flat|nested] 89+ messages in thread

* [PATCH v3 01/26] objtool: Introduce validate_return()
  2020-03-24 15:31 [PATCH v3 00/26] objtool: vmlinux.o and noinstr validation Peter Zijlstra
@ 2020-03-24 15:31 ` Peter Zijlstra
  2020-03-25  8:39   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  2020-03-24 15:31 ` [PATCH v3 02/26] objtool: Rename func_for_each_insn() Peter Zijlstra
                   ` (24 subsequent siblings)
  25 siblings, 2 replies; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 15:31 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, peterz, mhiramat, mbenes, brgerst

Trivial 'cleanup' to save one indentation level and match
validate_call().

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
---
 tools/objtool/check.c |   64 ++++++++++++++++++++++++++++----------------------
 1 file changed, 36 insertions(+), 28 deletions(-)

--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -1935,6 +1935,41 @@ static int validate_sibling_call(struct
 	return validate_call(insn, state);
 }
 
+static int validate_return(struct symbol *func, struct instruction *insn, struct insn_state *state)
+{
+	if (state->uaccess && !func_uaccess_safe(func)) {
+		WARN_FUNC("return with UACCESS enabled",
+			  insn->sec, insn->offset);
+		return 1;
+	}
+
+	if (!state->uaccess && func_uaccess_safe(func)) {
+		WARN_FUNC("return with UACCESS disabled from a UACCESS-safe function",
+			  insn->sec, insn->offset);
+		return 1;
+	}
+
+	if (state->df) {
+		WARN_FUNC("return with DF set",
+			  insn->sec, insn->offset);
+		return 1;
+	}
+
+	if (func && has_modified_stack_frame(state)) {
+		WARN_FUNC("return with modified stack frame",
+			  insn->sec, insn->offset);
+		return 1;
+	}
+
+	if (state->bp_scratch) {
+		WARN("%s uses BP as a scratch register",
+		     func->name);
+		return 1;
+	}
+
+	return 0;
+}
+
 /*
  * Follow the branch starting at the given instruction, and recursively follow
  * any other branches (jumps).  Meanwhile, track the frame pointer state at
@@ -2050,34 +2085,7 @@ static int validate_branch(struct objtoo
 		switch (insn->type) {
 
 		case INSN_RETURN:
-			if (state.uaccess && !func_uaccess_safe(func)) {
-				WARN_FUNC("return with UACCESS enabled", sec, insn->offset);
-				return 1;
-			}
-
-			if (!state.uaccess && func_uaccess_safe(func)) {
-				WARN_FUNC("return with UACCESS disabled from a UACCESS-safe function", sec, insn->offset);
-				return 1;
-			}
-
-			if (state.df) {
-				WARN_FUNC("return with DF set", sec, insn->offset);
-				return 1;
-			}
-
-			if (func && has_modified_stack_frame(&state)) {
-				WARN_FUNC("return with modified stack frame",
-					  sec, insn->offset);
-				return 1;
-			}
-
-			if (state.bp_scratch) {
-				WARN("%s uses BP as a scratch register",
-				     func->name);
-				return 1;
-			}
-
-			return 0;
+			return validate_return(func, insn, &state);
 
 		case INSN_CALL:
 		case INSN_CALL_DYNAMIC:



^ permalink raw reply	[flat|nested] 89+ messages in thread

* [PATCH v3 02/26] objtool: Rename func_for_each_insn()
  2020-03-24 15:31 [PATCH v3 00/26] objtool: vmlinux.o and noinstr validation Peter Zijlstra
  2020-03-24 15:31 ` [PATCH v3 01/26] objtool: Introduce validate_return() Peter Zijlstra
@ 2020-03-24 15:31 ` Peter Zijlstra
  2020-03-25  8:43   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  2020-03-24 15:31 ` [PATCH v3 03/26] objtool: Rename func_for_each_insn_all() Peter Zijlstra
                   ` (23 subsequent siblings)
  25 siblings, 2 replies; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 15:31 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, peterz, mhiramat, mbenes, brgerst

There is func_for_each_insn() and func_for_each_insn_all(), the both
iterate the instructions, but the first uses symbol offset/length
while the second uses insn->func.

Rename func_for_each_insn() to sym_for_eac_insn() because it iterates
on symbol information.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
---
 tools/objtool/check.c |   16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -77,17 +77,17 @@ static struct instruction *next_insn_sam
 	     insn;							\
 	     insn = next_insn_same_func(file, insn))
 
-#define func_for_each_insn(file, func, insn)				\
-	for (insn = find_insn(file, func->sec, func->offset);		\
+#define sym_for_each_insn(file, sym, insn)				\
+	for (insn = find_insn(file, sym->sec, sym->offset);		\
 	     insn && &insn->list != &file->insn_list &&			\
-		insn->sec == func->sec &&				\
-		insn->offset < func->offset + func->len;		\
+		insn->sec == sym->sec &&				\
+		insn->offset < sym->offset + sym->len;			\
 	     insn = list_next_entry(insn, list))
 
-#define func_for_each_insn_continue_reverse(file, func, insn)		\
+#define sym_for_each_insn_continue_reverse(file, sym, insn)		\
 	for (insn = list_prev_entry(insn, list);			\
 	     &insn->list != &file->insn_list &&				\
-		insn->sec == func->sec && insn->offset >= func->offset;	\
+		insn->sec == sym->sec && insn->offset >= sym->offset;	\
 	     insn = list_prev_entry(insn, list))
 
 #define sec_for_each_insn_from(file, insn)				\
@@ -281,7 +281,7 @@ static int decode_instructions(struct ob
 				return -1;
 			}
 
-			func_for_each_insn(file, func, insn)
+			sym_for_each_insn(file, func, insn)
 				insn->func = func;
 		}
 	}
@@ -2024,7 +2024,7 @@ static int validate_branch(struct objtoo
 
 				i = insn;
 				save_insn = NULL;
-				func_for_each_insn_continue_reverse(file, func, i) {
+				sym_for_each_insn_continue_reverse(file, func, i) {
 					if (i->save) {
 						save_insn = i;
 						break;



^ permalink raw reply	[flat|nested] 89+ messages in thread

* [PATCH v3 03/26] objtool: Rename func_for_each_insn_all()
  2020-03-24 15:31 [PATCH v3 00/26] objtool: vmlinux.o and noinstr validation Peter Zijlstra
  2020-03-24 15:31 ` [PATCH v3 01/26] objtool: Introduce validate_return() Peter Zijlstra
  2020-03-24 15:31 ` [PATCH v3 02/26] objtool: Rename func_for_each_insn() Peter Zijlstra
@ 2020-03-24 15:31 ` Peter Zijlstra
  2020-03-25  8:44   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  2020-03-24 15:31 ` [PATCH v3 04/26] x86/kexec: Use RIP relative addressing Peter Zijlstra
                   ` (22 subsequent siblings)
  25 siblings, 2 replies; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 15:31 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, peterz, mhiramat, mbenes, brgerst

Now that func_for_each_insn() is available, rename
func_for_each_insn_all(). This gets us:

  sym_for_each_insn()  - iterate on symbol offset/len
  func_for_each_insn() - iterate on insn->func

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
---
 tools/objtool/check.c |   12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -72,7 +72,7 @@ static struct instruction *next_insn_sam
 	return find_insn(file, func->cfunc->sec, func->cfunc->offset);
 }
 
-#define func_for_each_insn_all(file, func, insn)			\
+#define func_for_each_insn(file, func, insn)				\
 	for (insn = find_insn(file, func->sec, func->offset);		\
 	     insn;							\
 	     insn = next_insn_same_func(file, insn))
@@ -165,7 +165,7 @@ static bool __dead_end_function(struct o
 	if (!insn->func)
 		return false;
 
-	func_for_each_insn_all(file, func, insn) {
+	func_for_each_insn(file, func, insn) {
 		empty = false;
 
 		if (insn->type == INSN_RETURN)
@@ -180,7 +180,7 @@ static bool __dead_end_function(struct o
 	 * case, the function's dead-end status depends on whether the target
 	 * of the sibling call returns.
 	 */
-	func_for_each_insn_all(file, func, insn) {
+	func_for_each_insn(file, func, insn) {
 		if (is_sibling_call(insn)) {
 			struct instruction *dest = insn->jump_dest;
 
@@ -425,7 +425,7 @@ static void add_ignores(struct objtool_f
 			continue;
 		}
 
-		func_for_each_insn_all(file, func, insn)
+		func_for_each_insn(file, func, insn)
 			insn->ignore = true;
 	}
 }
@@ -1082,7 +1082,7 @@ static void mark_func_jump_tables(struct
 	struct instruction *insn, *last = NULL;
 	struct rela *rela;
 
-	func_for_each_insn_all(file, func, insn) {
+	func_for_each_insn(file, func, insn) {
 		if (!last)
 			last = insn;
 
@@ -1117,7 +1117,7 @@ static int add_func_jump_tables(struct o
 	struct instruction *insn;
 	int ret;
 
-	func_for_each_insn_all(file, func, insn) {
+	func_for_each_insn(file, func, insn) {
 		if (!insn->jump_table)
 			continue;
 



^ permalink raw reply	[flat|nested] 89+ messages in thread

* [PATCH v3 04/26] x86/kexec: Use RIP relative addressing
  2020-03-24 15:31 [PATCH v3 00/26] objtool: vmlinux.o and noinstr validation Peter Zijlstra
                   ` (2 preceding siblings ...)
  2020-03-24 15:31 ` [PATCH v3 03/26] objtool: Rename func_for_each_insn_all() Peter Zijlstra
@ 2020-03-24 15:31 ` Peter Zijlstra
  2020-03-25  9:34   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  2020-03-24 15:31 ` [PATCH v3 05/26] x86/kexec: Make relocate_kernel_64.S objtool clean Peter Zijlstra
                   ` (21 subsequent siblings)
  25 siblings, 2 replies; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 15:31 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, peterz, mhiramat, mbenes, brgerst

Normally identity_mapped is not visible to objtool, due to:

  arch/x86/kernel/Makefile:OBJECT_FILES_NON_STANDARD_relocate_kernel_$(BITS).o := y

However, when we want to run objtool on vmlinux.o there is no hiding
it:

  vmlinux.o: warning: objtool: .text+0x4c0f1: unsupported intra-function call

Replace the (i386 inspired) pattern:

	call 1f
  1:	popq %r8
	subq $(1b - relocate_kernel), %r8

With a x86_64 RIP-relative LEA:

	leaq relocate_kernel(%rip), %r8

Suggested-by: Brian Gerst <brgerst@gmail.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
---
 arch/x86/kernel/relocate_kernel_64.S |    5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

--- a/arch/x86/kernel/relocate_kernel_64.S
+++ b/arch/x86/kernel/relocate_kernel_64.S
@@ -196,10 +196,7 @@ SYM_CODE_START_LOCAL_NOALIGN(identity_ma
 
 	/* get the re-entry point of the peer system */
 	movq	0(%rsp), %rbp
-	call	1f
-1:
-	popq	%r8
-	subq	$(1b - relocate_kernel), %r8
+	leaq	relocate_kernel(%rip), %r8
 	movq	CP_PA_SWAP_PAGE(%r8), %r10
 	movq	CP_PA_BACKUP_PAGES_MAP(%r8), %rdi
 	movq	CP_PA_TABLE_PAGE(%r8), %rax



^ permalink raw reply	[flat|nested] 89+ messages in thread

* [PATCH v3 05/26] x86/kexec: Make relocate_kernel_64.S objtool clean
  2020-03-24 15:31 [PATCH v3 00/26] objtool: vmlinux.o and noinstr validation Peter Zijlstra
                   ` (3 preceding siblings ...)
  2020-03-24 15:31 ` [PATCH v3 04/26] x86/kexec: Use RIP relative addressing Peter Zijlstra
@ 2020-03-24 15:31 ` Peter Zijlstra
  2020-03-24 20:55   ` Josh Poimboeuf
                     ` (2 more replies)
  2020-03-24 15:31 ` [PATCH v3 06/26] objtool: Optimize find_symbol_by_index() Peter Zijlstra
                   ` (20 subsequent siblings)
  25 siblings, 3 replies; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 15:31 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, peterz, mhiramat, mbenes, brgerst

Having fixed the biggest objtool issue in this file; fix up the rest
and remove the exception.

Suggested-by: Josh Poimboeuf <jpoimboe@redhat.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
 arch/x86/kernel/Makefile             |    1 -
 arch/x86/kernel/relocate_kernel_64.S |    7 +++++++
 2 files changed, 7 insertions(+), 1 deletion(-)

--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -32,7 +32,6 @@ KASAN_SANITIZE_paravirt.o				:= n
 # by several compilation units. To be safe, disable all instrumentation.
 KCSAN_SANITIZE := n
 
-OBJECT_FILES_NON_STANDARD_relocate_kernel_$(BITS).o	:= y
 OBJECT_FILES_NON_STANDARD_test_nx.o			:= y
 OBJECT_FILES_NON_STANDARD_paravirt_patch.o		:= y
 
--- a/arch/x86/kernel/relocate_kernel_64.S
+++ b/arch/x86/kernel/relocate_kernel_64.S
@@ -9,6 +9,8 @@
 #include <asm/kexec.h>
 #include <asm/processor-flags.h>
 #include <asm/pgtable_types.h>
+#include <asm/nospec-branch.h>
+#include <asm/unwind_hints.h>
 
 /*
  * Must be relocatable PIC code callable as a C function
@@ -39,6 +41,7 @@
 	.align PAGE_SIZE
 	.code64
 SYM_CODE_START_NOALIGN(relocate_kernel)
+	UNWIND_HINT_EMPTY
 	/*
 	 * %rdi indirection_page
 	 * %rsi page_list
@@ -105,6 +108,7 @@ SYM_CODE_START_NOALIGN(relocate_kernel)
 SYM_CODE_END(relocate_kernel)
 
 SYM_CODE_START_LOCAL_NOALIGN(identity_mapped)
+	UNWIND_HINT_EMPTY
 	/* set return address to 0 if not preserving context */
 	pushq	$0
 	/* store the start address on the stack */
@@ -192,6 +196,7 @@ SYM_CODE_START_LOCAL_NOALIGN(identity_ma
 1:
 	popq	%rdx
 	leaq	PAGE_SIZE(%r10), %rsp
+	ANNOTATE_RETPOLINE_SAFE
 	call	*%rdx
 
 	/* get the re-entry point of the peer system */
@@ -209,6 +214,7 @@ SYM_CODE_START_LOCAL_NOALIGN(identity_ma
 SYM_CODE_END(identity_mapped)
 
 SYM_CODE_START_LOCAL_NOALIGN(virtual_mapped)
+	UNWIND_HINT_EMPTY
 	movq	RSP(%r8), %rsp
 	movq	CR4(%r8), %rax
 	movq	%rax, %cr4
@@ -230,6 +236,7 @@ SYM_CODE_END(virtual_mapped)
 
 	/* Do the copies */
 SYM_CODE_START_LOCAL_NOALIGN(swap_pages)
+	UNWIND_HINT_EMPTY
 	movq	%rdi, %rcx 	/* Put the page_list in %rcx */
 	xorl	%edi, %edi
 	xorl	%esi, %esi



^ permalink raw reply	[flat|nested] 89+ messages in thread

* [PATCH v3 06/26] objtool: Optimize find_symbol_by_index()
  2020-03-24 15:31 [PATCH v3 00/26] objtool: vmlinux.o and noinstr validation Peter Zijlstra
                   ` (4 preceding siblings ...)
  2020-03-24 15:31 ` [PATCH v3 05/26] x86/kexec: Make relocate_kernel_64.S objtool clean Peter Zijlstra
@ 2020-03-24 15:31 ` Peter Zijlstra
  2020-03-25 10:01   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  2020-03-24 15:31 ` [PATCH v3 07/26] objtool: Add a statistics mode Peter Zijlstra
                   ` (19 subsequent siblings)
  25 siblings, 2 replies; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 15:31 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, peterz, mhiramat, mbenes, brgerst

The symbol index is object wide, not per section, so it makes no sense
to have the symbol_hash be part of the section object. By moving it to
the elf object we avoid the linear sections iteration.

This reduces the runtime of objtool on vmlinux.o from over 3 hours (I
gave up) to a few minutes. The defconfig vmlinux.o has around 20k
sections.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
---
 tools/objtool/elf.c |   13 +++++--------
 tools/objtool/elf.h |    3 +--
 2 files changed, 6 insertions(+), 10 deletions(-)

--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -46,13 +46,11 @@ static struct section *find_section_by_i
 
 static struct symbol *find_symbol_by_index(struct elf *elf, unsigned int idx)
 {
-	struct section *sec;
 	struct symbol *sym;
 
-	list_for_each_entry(sec, &elf->sections, list)
-		hash_for_each_possible(sec->symbol_hash, sym, hash, idx)
-			if (sym->idx == idx)
-				return sym;
+	hash_for_each_possible(elf->symbol_hash, sym, hash, idx)
+		if (sym->idx == idx)
+			return sym;
 
 	return NULL;
 }
@@ -166,7 +164,6 @@ static int read_sections(struct elf *elf
 		INIT_LIST_HEAD(&sec->symbol_list);
 		INIT_LIST_HEAD(&sec->rela_list);
 		hash_init(sec->rela_hash);
-		hash_init(sec->symbol_hash);
 
 		list_add_tail(&sec->list, &elf->sections);
 
@@ -299,7 +296,7 @@ static int read_symbols(struct elf *elf)
 		}
 		sym->alias = alias;
 		list_add(&sym->list, entry);
-		hash_add(sym->sec->symbol_hash, &sym->hash, sym->idx);
+		hash_add(elf->symbol_hash, &sym->hash, sym->idx);
 	}
 
 	/* Create parent/child links for any cold subfunctions */
@@ -425,6 +422,7 @@ struct elf *elf_read(const char *name, i
 	}
 	memset(elf, 0, sizeof(*elf));
 
+	hash_init(elf->symbol_hash);
 	INIT_LIST_HEAD(&elf->sections);
 
 	elf->fd = open(name, flags);
@@ -486,7 +484,6 @@ struct section *elf_create_section(struc
 	INIT_LIST_HEAD(&sec->symbol_list);
 	INIT_LIST_HEAD(&sec->rela_list);
 	hash_init(sec->rela_hash);
-	hash_init(sec->symbol_hash);
 
 	list_add_tail(&sec->list, &elf->sections);
 
--- a/tools/objtool/elf.h
+++ b/tools/objtool/elf.h
@@ -27,7 +27,6 @@ struct section {
 	struct list_head list;
 	GElf_Shdr sh;
 	struct list_head symbol_list;
-	DECLARE_HASHTABLE(symbol_hash, 8);
 	struct list_head rela_list;
 	DECLARE_HASHTABLE(rela_hash, 16);
 	struct section *base, *rela;
@@ -71,7 +70,7 @@ struct elf {
 	int fd;
 	char *name;
 	struct list_head sections;
-	DECLARE_HASHTABLE(rela_hash, 16);
+	DECLARE_HASHTABLE(symbol_hash, 20);
 };
 
 



^ permalink raw reply	[flat|nested] 89+ messages in thread

* [PATCH v3 07/26] objtool: Add a statistics mode
  2020-03-24 15:31 [PATCH v3 00/26] objtool: vmlinux.o and noinstr validation Peter Zijlstra
                   ` (5 preceding siblings ...)
  2020-03-24 15:31 ` [PATCH v3 06/26] objtool: Optimize find_symbol_by_index() Peter Zijlstra
@ 2020-03-24 15:31 ` Peter Zijlstra
  2020-03-25 10:10   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  2020-03-24 15:31 ` [PATCH v3 08/26] objtool: Optimize find_section_by_index() Peter Zijlstra
                   ` (18 subsequent siblings)
  25 siblings, 2 replies; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 15:31 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, peterz, mhiramat, mbenes, brgerst

Have it print a few numbers which can be used to size the hashtables.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
---
 tools/objtool/builtin-check.c |    3 ++-
 tools/objtool/builtin.h       |    2 +-
 tools/objtool/check.c         |    5 +++++
 tools/objtool/elf.c           |   18 +++++++++++++++++-
 4 files changed, 25 insertions(+), 3 deletions(-)

--- a/tools/objtool/builtin-check.c
+++ b/tools/objtool/builtin-check.c
@@ -17,7 +17,7 @@
 #include "builtin.h"
 #include "check.h"
 
-bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess;
+bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats;
 
 static const char * const check_usage[] = {
 	"objtool check [<options>] file.o",
@@ -31,6 +31,7 @@ const struct option check_options[] = {
 	OPT_BOOLEAN('m', "module", &module, "Indicates the object will be part of a kernel module"),
 	OPT_BOOLEAN('b', "backtrace", &backtrace, "unwind on error"),
 	OPT_BOOLEAN('a', "uaccess", &uaccess, "enable uaccess checking"),
+	OPT_BOOLEAN('s', "stats", &stats, "print statistics"),
 	OPT_END(),
 };
 
--- a/tools/objtool/builtin.h
+++ b/tools/objtool/builtin.h
@@ -8,7 +8,7 @@
 #include <subcmd/parse-options.h>
 
 extern const struct option check_options[];
-extern bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess;
+extern bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats;
 
 extern int cmd_check(int argc, const char **argv);
 extern int cmd_orc(int argc, const char **argv);
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -239,6 +239,7 @@ static int decode_instructions(struct ob
 	struct symbol *func;
 	unsigned long offset;
 	struct instruction *insn;
+	unsigned long nr_insns = 0;
 	int ret;
 
 	for_each_sec(file, sec) {
@@ -274,6 +275,7 @@ static int decode_instructions(struct ob
 
 			hash_add(file->insn_hash, &insn->hash, insn->offset);
 			list_add_tail(&insn->list, &file->insn_list);
+			nr_insns++;
 		}
 
 		list_for_each_entry(func, &sec->symbol_list, list) {
@@ -291,6 +293,9 @@ static int decode_instructions(struct ob
 		}
 	}
 
+	if (stats)
+		printf("nr_insns: %lu\n", nr_insns);
+
 	return 0;
 
 err:
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -15,6 +15,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <errno.h>
+#include "builtin.h"
 
 #include "elf.h"
 #include "warn.h"
@@ -202,6 +203,9 @@ static int read_sections(struct elf *elf
 		sec->len = sec->sh.sh_size;
 	}
 
+	if (stats)
+		printf("nr_sections: %lu\n", (unsigned long)sections_nr);
+
 	/* sanity check, one more call to elf_nextscn() should return NULL */
 	if (elf_nextscn(elf->elf, s)) {
 		WARN("section entry mismatch");
@@ -299,6 +303,9 @@ static int read_symbols(struct elf *elf)
 		hash_add(elf->symbol_hash, &sym->hash, sym->idx);
 	}
 
+	if (stats)
+		printf("nr_symbols: %lu\n", (unsigned long)symbols_nr);
+
 	/* Create parent/child links for any cold subfunctions */
 	list_for_each_entry(sec, &elf->sections, list) {
 		list_for_each_entry(sym, &sec->symbol_list, list) {
@@ -360,6 +367,7 @@ static int read_relas(struct elf *elf)
 	struct rela *rela;
 	int i;
 	unsigned int symndx;
+	unsigned long nr_rela, max_rela = 0, tot_rela = 0;
 
 	list_for_each_entry(sec, &elf->sections, list) {
 		if (sec->sh.sh_type != SHT_RELA)
@@ -374,6 +382,7 @@ static int read_relas(struct elf *elf)
 
 		sec->base->rela = sec;
 
+		nr_rela = 0;
 		for (i = 0; i < sec->sh.sh_size / sec->sh.sh_entsize; i++) {
 			rela = malloc(sizeof(*rela));
 			if (!rela) {
@@ -401,8 +410,15 @@ static int read_relas(struct elf *elf)
 
 			list_add_tail(&rela->list, &sec->rela_list);
 			hash_add(sec->rela_hash, &rela->hash, rela->offset);
-
+			nr_rela++;
 		}
+		max_rela = max(max_rela, nr_rela);
+		tot_rela += nr_rela;
+	}
+
+	if (stats) {
+		printf("max_rela: %lu\n", max_rela);
+		printf("tot_rela: %lu\n", tot_rela);
 	}
 
 	return 0;



^ permalink raw reply	[flat|nested] 89+ messages in thread

* [PATCH v3 08/26] objtool: Optimize find_section_by_index()
  2020-03-24 15:31 [PATCH v3 00/26] objtool: vmlinux.o and noinstr validation Peter Zijlstra
                   ` (6 preceding siblings ...)
  2020-03-24 15:31 ` [PATCH v3 07/26] objtool: Add a statistics mode Peter Zijlstra
@ 2020-03-24 15:31 ` Peter Zijlstra
  2020-03-25 10:12   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  2020-03-24 15:31 ` [PATCH v3 09/26] objtool: Optimize find_section_by_name() Peter Zijlstra
                   ` (17 subsequent siblings)
  25 siblings, 2 replies; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 15:31 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, peterz, mhiramat, mbenes, brgerst

In order to avoid a linear search (over 20k entries), add an
section_hash to the elf object.

This reduces objtool on vmlinux.o from a few minutes to around 45
seconds.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
---
 tools/objtool/elf.c |   13 ++++++++-----
 tools/objtool/elf.h |    2 ++
 2 files changed, 10 insertions(+), 5 deletions(-)

--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -38,7 +38,7 @@ static struct section *find_section_by_i
 {
 	struct section *sec;
 
-	list_for_each_entry(sec, &elf->sections, list)
+	hash_for_each_possible(elf->section_hash, sec, hash, idx)
 		if (sec->idx == idx)
 			return sec;
 
@@ -166,8 +166,6 @@ static int read_sections(struct elf *elf
 		INIT_LIST_HEAD(&sec->rela_list);
 		hash_init(sec->rela_hash);
 
-		list_add_tail(&sec->list, &elf->sections);
-
 		s = elf_getscn(elf->elf, i);
 		if (!s) {
 			WARN_ELF("elf_getscn");
@@ -201,6 +199,9 @@ static int read_sections(struct elf *elf
 			}
 		}
 		sec->len = sec->sh.sh_size;
+
+		list_add_tail(&sec->list, &elf->sections);
+		hash_add(elf->section_hash, &sec->hash, sec->idx);
 	}
 
 	if (stats)
@@ -439,6 +440,7 @@ struct elf *elf_read(const char *name, i
 	memset(elf, 0, sizeof(*elf));
 
 	hash_init(elf->symbol_hash);
+	hash_init(elf->section_hash);
 	INIT_LIST_HEAD(&elf->sections);
 
 	elf->fd = open(name, flags);
@@ -501,8 +503,6 @@ struct section *elf_create_section(struc
 	INIT_LIST_HEAD(&sec->rela_list);
 	hash_init(sec->rela_hash);
 
-	list_add_tail(&sec->list, &elf->sections);
-
 	s = elf_newscn(elf->elf);
 	if (!s) {
 		WARN_ELF("elf_newscn");
@@ -579,6 +579,9 @@ struct section *elf_create_section(struc
 	shstrtab->len += strlen(name) + 1;
 	shstrtab->changed = true;
 
+	list_add_tail(&sec->list, &elf->sections);
+	hash_add(elf->section_hash, &sec->hash, sec->idx);
+
 	return sec;
 }
 
--- a/tools/objtool/elf.h
+++ b/tools/objtool/elf.h
@@ -25,6 +25,7 @@
 
 struct section {
 	struct list_head list;
+	struct hlist_node hash;
 	GElf_Shdr sh;
 	struct list_head symbol_list;
 	struct list_head rela_list;
@@ -71,6 +72,7 @@ struct elf {
 	char *name;
 	struct list_head sections;
 	DECLARE_HASHTABLE(symbol_hash, 20);
+	DECLARE_HASHTABLE(section_hash, 16);
 };
 
 



^ permalink raw reply	[flat|nested] 89+ messages in thread

* [PATCH v3 09/26] objtool: Optimize find_section_by_name()
  2020-03-24 15:31 [PATCH v3 00/26] objtool: vmlinux.o and noinstr validation Peter Zijlstra
                   ` (7 preceding siblings ...)
  2020-03-24 15:31 ` [PATCH v3 08/26] objtool: Optimize find_section_by_index() Peter Zijlstra
@ 2020-03-24 15:31 ` Peter Zijlstra
  2020-03-25 10:18   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  2020-03-24 15:31 ` [PATCH v3 10/26] objtool: Optimize find_symbol_*() and read_symbols() Peter Zijlstra
                   ` (16 subsequent siblings)
  25 siblings, 2 replies; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 15:31 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, peterz, mhiramat, mbenes, brgerst

In order to avoid yet another linear search of (20k) sections, add a
name based hash.

This reduces objtool runtime on vmlinux.o by some 10s to around 35s.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
---
 tools/objtool/elf.c |   10 +++++++++-
 tools/objtool/elf.h |    3 +++
 2 files changed, 12 insertions(+), 1 deletion(-)

--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -22,11 +22,16 @@
 
 #define MAX_NAME_LEN 128
 
+static inline u32 str_hash(const char *str)
+{
+	return jhash(str, strlen(str), 0);
+}
+
 struct section *find_section_by_name(struct elf *elf, const char *name)
 {
 	struct section *sec;
 
-	list_for_each_entry(sec, &elf->sections, list)
+	hash_for_each_possible(elf->section_name_hash, sec, name_hash, str_hash(name))
 		if (!strcmp(sec->name, name))
 			return sec;
 
@@ -202,6 +207,7 @@ static int read_sections(struct elf *elf
 
 		list_add_tail(&sec->list, &elf->sections);
 		hash_add(elf->section_hash, &sec->hash, sec->idx);
+		hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name));
 	}
 
 	if (stats)
@@ -441,6 +447,7 @@ struct elf *elf_read(const char *name, i
 
 	hash_init(elf->symbol_hash);
 	hash_init(elf->section_hash);
+	hash_init(elf->section_name_hash);
 	INIT_LIST_HEAD(&elf->sections);
 
 	elf->fd = open(name, flags);
@@ -581,6 +588,7 @@ struct section *elf_create_section(struc
 
 	list_add_tail(&sec->list, &elf->sections);
 	hash_add(elf->section_hash, &sec->hash, sec->idx);
+	hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name));
 
 	return sec;
 }
--- a/tools/objtool/elf.h
+++ b/tools/objtool/elf.h
@@ -10,6 +10,7 @@
 #include <gelf.h>
 #include <linux/list.h>
 #include <linux/hashtable.h>
+#include <linux/jhash.h>
 
 #ifdef LIBELF_USE_DEPRECATED
 # define elf_getshdrnum    elf_getshnum
@@ -26,6 +27,7 @@
 struct section {
 	struct list_head list;
 	struct hlist_node hash;
+	struct hlist_node name_hash;
 	GElf_Shdr sh;
 	struct list_head symbol_list;
 	struct list_head rela_list;
@@ -73,6 +75,7 @@ struct elf {
 	struct list_head sections;
 	DECLARE_HASHTABLE(symbol_hash, 20);
 	DECLARE_HASHTABLE(section_hash, 16);
+	DECLARE_HASHTABLE(section_name_hash, 16);
 };
 
 



^ permalink raw reply	[flat|nested] 89+ messages in thread

* [PATCH v3 10/26] objtool: Optimize find_symbol_*() and read_symbols()
  2020-03-24 15:31 [PATCH v3 00/26] objtool: vmlinux.o and noinstr validation Peter Zijlstra
                   ` (8 preceding siblings ...)
  2020-03-24 15:31 ` [PATCH v3 09/26] objtool: Optimize find_section_by_name() Peter Zijlstra
@ 2020-03-24 15:31 ` Peter Zijlstra
  2020-03-25 10:20   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  2020-03-24 15:31 ` [PATCH v3 11/26] objtool: Rename find_containing_func() Peter Zijlstra
                   ` (15 subsequent siblings)
  25 siblings, 2 replies; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 15:31 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, peterz, mhiramat, mbenes, brgerst

All of:

  read_symbols(), find_symbol_by_offset(), find_symbol_containing(),
  find_containing_func()

do a linear search of the symbols. Add an RB tree to make it go
faster.

This about halves objtool runtime on vmlinux.o, from 34s to 18s.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
---
 tools/objtool/Build |    5 +
 tools/objtool/elf.c |  194 ++++++++++++++++++++++++++++++++++++----------------
 tools/objtool/elf.h |    3 
 3 files changed, 144 insertions(+), 58 deletions(-)

--- a/tools/objtool/Build
+++ b/tools/objtool/Build
@@ -11,6 +11,7 @@ objtool-y += objtool.o
 objtool-y += libstring.o
 objtool-y += libctype.o
 objtool-y += str_error_r.o
+objtool-y += librbtree.o
 
 CFLAGS += -I$(srctree)/tools/lib
 
@@ -25,3 +26,7 @@ $(OUTPUT)libctype.o: ../lib/ctype.c FORC
 $(OUTPUT)str_error_r.o: ../lib/str_error_r.c FORCE
 	$(call rule_mkdir)
 	$(call if_changed_dep,cc_o_c)
+
+$(OUTPUT)librbtree.o: ../lib/rbtree.c FORCE
+	$(call rule_mkdir)
+	$(call if_changed_dep,cc_o_c)
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -27,6 +27,90 @@ static inline u32 str_hash(const char *s
 	return jhash(str, strlen(str), 0);
 }
 
+static void rb_add(struct rb_root *tree, struct rb_node *node,
+		   int (*cmp)(struct rb_node *, const struct rb_node *))
+{
+	struct rb_node **link = &tree->rb_node;
+	struct rb_node *parent = NULL;
+
+	while (*link) {
+		parent = *link;
+		if (cmp(node, parent) < 0)
+			link = &parent->rb_left;
+		else
+			link = &parent->rb_right;
+	}
+
+	rb_link_node(node, parent, link);
+	rb_insert_color(node, tree);
+}
+
+static struct rb_node *rb_find_first(struct rb_root *tree, const void *key,
+			       int (*cmp)(const void *key, const struct rb_node *))
+{
+	struct rb_node *node = tree->rb_node;
+	struct rb_node *match = NULL;
+
+	while (node) {
+		int c = cmp(key, node);
+		if (c <= 0) {
+			if (!c)
+				match = node;
+			node = node->rb_left;
+		} else if (c > 0) {
+			node = node->rb_right;
+		}
+	}
+
+	return match;
+}
+
+static struct rb_node *rb_next_match(struct rb_node *node, const void *key,
+				    int (*cmp)(const void *key, const struct rb_node *))
+{
+	node = rb_next(node);
+	if (node && cmp(key, node))
+		node = NULL;
+	return node;
+}
+
+#define rb_for_each(tree, node, key, cmp) \
+	for ((node) = rb_find_first((tree), (key), (cmp)); \
+	     (node); (node) = rb_next_match((node), (key), (cmp)))
+
+static int symbol_to_offset(struct rb_node *a, const struct rb_node *b)
+{
+	struct symbol *sa = rb_entry(a, struct symbol, node);
+	struct symbol *sb = rb_entry(b, struct symbol, node);
+
+	if (sa->offset < sb->offset)
+		return -1;
+	if (sa->offset > sb->offset)
+		return 1;
+
+	if (sa->len < sb->len)
+		return -1;
+	if (sa->len > sb->len)
+		return 1;
+
+	sa->alias = sb;
+
+	return 0;
+}
+
+static int symbol_by_offset(const void *key, const struct rb_node *node)
+{
+	const struct symbol *s = rb_entry(node, struct symbol, node);
+	const unsigned long *o = key;
+
+	if (*o < s->offset)
+		return -1;
+	if (*o > s->offset + s->len)
+		return 1;
+
+	return 0;
+}
+
 struct section *find_section_by_name(struct elf *elf, const char *name)
 {
 	struct section *sec;
@@ -63,47 +147,69 @@ static struct symbol *find_symbol_by_ind
 
 struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset)
 {
-	struct symbol *sym;
+	struct rb_node *node;
 
-	list_for_each_entry(sym, &sec->symbol_list, list)
-		if (sym->type != STT_SECTION && sym->offset == offset)
-			return sym;
+	rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) {
+		struct symbol *s = rb_entry(node, struct symbol, node);
+
+		if (s->offset == offset && s->type != STT_SECTION)
+			return s;
+	}
 
 	return NULL;
 }
 
 struct symbol *find_func_by_offset(struct section *sec, unsigned long offset)
 {
-	struct symbol *sym;
+	struct rb_node *node;
 
-	list_for_each_entry(sym, &sec->symbol_list, list)
-		if (sym->type == STT_FUNC && sym->offset == offset)
-			return sym;
+	rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) {
+		struct symbol *s = rb_entry(node, struct symbol, node);
+
+		if (s->offset == offset && s->type == STT_FUNC)
+			return s;
+	}
 
 	return NULL;
 }
 
-struct symbol *find_symbol_by_name(struct elf *elf, const char *name)
+struct symbol *find_symbol_containing(struct section *sec, unsigned long offset)
 {
-	struct section *sec;
-	struct symbol *sym;
+	struct rb_node *node;
 
-	list_for_each_entry(sec, &elf->sections, list)
-		list_for_each_entry(sym, &sec->symbol_list, list)
-			if (!strcmp(sym->name, name))
-				return sym;
+	rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) {
+		struct symbol *s = rb_entry(node, struct symbol, node);
+
+		if (s->type != STT_SECTION)
+			return s;
+	}
 
 	return NULL;
 }
 
-struct symbol *find_symbol_containing(struct section *sec, unsigned long offset)
+struct symbol *find_containing_func(struct section *sec, unsigned long offset)
 {
+	struct rb_node *node;
+
+	rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) {
+		struct symbol *s = rb_entry(node, struct symbol, node);
+
+		if (s->type == STT_FUNC)
+			return s;
+	}
+
+	return NULL;
+}
+
+struct symbol *find_symbol_by_name(struct elf *elf, const char *name)
+{
+	struct section *sec;
 	struct symbol *sym;
 
-	list_for_each_entry(sym, &sec->symbol_list, list)
-		if (sym->type != STT_SECTION &&
-		    offset >= sym->offset && offset < sym->offset + sym->len)
-			return sym;
+	list_for_each_entry(sec, &elf->sections, list)
+		list_for_each_entry(sym, &sec->symbol_list, list)
+			if (!strcmp(sym->name, name))
+				return sym;
 
 	return NULL;
 }
@@ -130,18 +236,6 @@ struct rela *find_rela_by_dest(struct se
 	return find_rela_by_dest_range(sec, offset, 1);
 }
 
-struct symbol *find_containing_func(struct section *sec, unsigned long offset)
-{
-	struct symbol *func;
-
-	list_for_each_entry(func, &sec->symbol_list, list)
-		if (func->type == STT_FUNC && offset >= func->offset &&
-		    offset < func->offset + func->len)
-			return func;
-
-	return NULL;
-}
-
 static int read_sections(struct elf *elf)
 {
 	Elf_Scn *s = NULL;
@@ -225,8 +319,9 @@ static int read_sections(struct elf *elf
 static int read_symbols(struct elf *elf)
 {
 	struct section *symtab, *sec;
-	struct symbol *sym, *pfunc, *alias;
-	struct list_head *entry, *tmp;
+	struct symbol *sym, *pfunc;
+	struct list_head *entry;
+	struct rb_node *pnode;
 	int symbols_nr, i;
 	char *coldstr;
 
@@ -245,7 +340,7 @@ static int read_symbols(struct elf *elf)
 			return -1;
 		}
 		memset(sym, 0, sizeof(*sym));
-		alias = sym;
+		sym->alias = sym;
 
 		sym->idx = i;
 
@@ -283,29 +378,12 @@ static int read_symbols(struct elf *elf)
 		sym->offset = sym->sym.st_value;
 		sym->len = sym->sym.st_size;
 
-		/* sorted insert into a per-section list */
-		entry = &sym->sec->symbol_list;
-		list_for_each_prev(tmp, &sym->sec->symbol_list) {
-			struct symbol *s;
-
-			s = list_entry(tmp, struct symbol, list);
-
-			if (sym->offset > s->offset) {
-				entry = tmp;
-				break;
-			}
-
-			if (sym->offset == s->offset) {
-				if (sym->len && sym->len == s->len && alias == sym)
-					alias = s;
-
-				if (sym->len >= s->len) {
-					entry = tmp;
-					break;
-				}
-			}
-		}
-		sym->alias = alias;
+		rb_add(&sym->sec->symbol_tree, &sym->node, symbol_to_offset);
+		pnode = rb_prev(&sym->node);
+		if (pnode)
+			entry = &rb_entry(pnode, struct symbol, node)->list;
+		else
+			entry = &sym->sec->symbol_list;
 		list_add(&sym->list, entry);
 		hash_add(elf->symbol_hash, &sym->hash, sym->idx);
 	}
--- a/tools/objtool/elf.h
+++ b/tools/objtool/elf.h
@@ -10,6 +10,7 @@
 #include <gelf.h>
 #include <linux/list.h>
 #include <linux/hashtable.h>
+#include <linux/rbtree.h>
 #include <linux/jhash.h>
 
 #ifdef LIBELF_USE_DEPRECATED
@@ -29,6 +30,7 @@ struct section {
 	struct hlist_node hash;
 	struct hlist_node name_hash;
 	GElf_Shdr sh;
+	struct rb_root symbol_tree;
 	struct list_head symbol_list;
 	struct list_head rela_list;
 	DECLARE_HASHTABLE(rela_hash, 16);
@@ -43,6 +45,7 @@ struct section {
 
 struct symbol {
 	struct list_head list;
+	struct rb_node node;
 	struct hlist_node hash;
 	GElf_Sym sym;
 	struct section *sec;



^ permalink raw reply	[flat|nested] 89+ messages in thread

* [PATCH v3 11/26] objtool: Rename find_containing_func()
  2020-03-24 15:31 [PATCH v3 00/26] objtool: vmlinux.o and noinstr validation Peter Zijlstra
                   ` (9 preceding siblings ...)
  2020-03-24 15:31 ` [PATCH v3 10/26] objtool: Optimize find_symbol_*() and read_symbols() Peter Zijlstra
@ 2020-03-24 15:31 ` Peter Zijlstra
  2020-03-25 10:21   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  2020-03-24 15:31 ` [PATCH v3 12/26] objtool: Resize insn_hash Peter Zijlstra
                   ` (14 subsequent siblings)
  25 siblings, 2 replies; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 15:31 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, peterz, mhiramat, mbenes, brgerst

For consistency; we have:

  find_symbol_by_offset() / find_symbol_containing()
  find_func_by_offset()   / find_containing_func()

fix that.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
---
 tools/objtool/elf.c  |    2 +-
 tools/objtool/elf.h  |    2 +-
 tools/objtool/warn.h |    2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -187,7 +187,7 @@ struct symbol *find_symbol_containing(st
 	return NULL;
 }
 
-struct symbol *find_containing_func(struct section *sec, unsigned long offset)
+struct symbol *find_func_containing(struct section *sec, unsigned long offset)
 {
 	struct rb_node *node;
 
--- a/tools/objtool/elf.h
+++ b/tools/objtool/elf.h
@@ -91,7 +91,7 @@ struct symbol *find_symbol_containing(st
 struct rela *find_rela_by_dest(struct section *sec, unsigned long offset);
 struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset,
 				     unsigned int len);
-struct symbol *find_containing_func(struct section *sec, unsigned long offset);
+struct symbol *find_func_containing(struct section *sec, unsigned long offset);
 struct section *elf_create_section(struct elf *elf, const char *name, size_t
 				   entsize, int nr);
 struct section *elf_create_rela_section(struct elf *elf, struct section *base);
--- a/tools/objtool/warn.h
+++ b/tools/objtool/warn.h
@@ -21,7 +21,7 @@ static inline char *offstr(struct sectio
 	char *name, *str;
 	unsigned long name_off;
 
-	func = find_containing_func(sec, offset);
+	func = find_func_containing(sec, offset);
 	if (func) {
 		name = func->name;
 		name_off = offset - func->offset;



^ permalink raw reply	[flat|nested] 89+ messages in thread

* [PATCH v3 12/26] objtool: Resize insn_hash
  2020-03-24 15:31 [PATCH v3 00/26] objtool: vmlinux.o and noinstr validation Peter Zijlstra
                   ` (10 preceding siblings ...)
  2020-03-24 15:31 ` [PATCH v3 11/26] objtool: Rename find_containing_func() Peter Zijlstra
@ 2020-03-24 15:31 ` Peter Zijlstra
  2020-03-25 10:21   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  2020-03-24 15:31 ` [PATCH v3 13/26] objtool: Optimize find_symbol_by_name() Peter Zijlstra
                   ` (13 subsequent siblings)
  25 siblings, 2 replies; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 15:31 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, peterz, mhiramat, mbenes, brgerst

Perf shows we're spending a lot of time in find_insn() and the
statistics show we have around 3.2 million instruction. Increase the
hash table size to reduce the bucket load from around 50 to 3.

This shaves about 2s off of objtool on vmlinux.o runtime, down to 16s.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
---
 tools/objtool/check.h |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

--- a/tools/objtool/check.h
+++ b/tools/objtool/check.h
@@ -50,7 +50,7 @@ struct instruction {
 struct objtool_file {
 	struct elf *elf;
 	struct list_head insn_list;
-	DECLARE_HASHTABLE(insn_hash, 16);
+	DECLARE_HASHTABLE(insn_hash, 20);
 	bool ignore_unreachables, c_file, hints, rodata;
 };
 



^ permalink raw reply	[flat|nested] 89+ messages in thread

* [PATCH v3 13/26] objtool: Optimize find_symbol_by_name()
  2020-03-24 15:31 [PATCH v3 00/26] objtool: vmlinux.o and noinstr validation Peter Zijlstra
                   ` (11 preceding siblings ...)
  2020-03-24 15:31 ` [PATCH v3 12/26] objtool: Resize insn_hash Peter Zijlstra
@ 2020-03-24 15:31 ` Peter Zijlstra
  2020-03-25 10:25   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  2020-03-24 15:31 ` [PATCH v3 14/26] objtool: Optimize read_sections() Peter Zijlstra
                   ` (12 subsequent siblings)
  25 siblings, 2 replies; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 15:31 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, peterz, mhiramat, mbenes, brgerst

Perf showed that find_symbol_by_name() takes time; add a symbol name
hash.

This shaves another second off of objtool on vmlinux.o runtime, down
to 15 seconds.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
---
 tools/objtool/elf.c |   10 +++++-----
 tools/objtool/elf.h |    2 ++
 2 files changed, 7 insertions(+), 5 deletions(-)

--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -203,13 +203,11 @@ struct symbol *find_func_containing(stru
 
 struct symbol *find_symbol_by_name(struct elf *elf, const char *name)
 {
-	struct section *sec;
 	struct symbol *sym;
 
-	list_for_each_entry(sec, &elf->sections, list)
-		list_for_each_entry(sym, &sec->symbol_list, list)
-			if (!strcmp(sym->name, name))
-				return sym;
+	hash_for_each_possible(elf->symbol_name_hash, sym, name_hash, str_hash(name))
+		if (!strcmp(sym->name, name))
+			return sym;
 
 	return NULL;
 }
@@ -386,6 +384,7 @@ static int read_symbols(struct elf *elf)
 			entry = &sym->sec->symbol_list;
 		list_add(&sym->list, entry);
 		hash_add(elf->symbol_hash, &sym->hash, sym->idx);
+		hash_add(elf->symbol_name_hash, &sym->name_hash, str_hash(sym->name));
 	}
 
 	if (stats)
@@ -524,6 +523,7 @@ struct elf *elf_read(const char *name, i
 	memset(elf, 0, sizeof(*elf));
 
 	hash_init(elf->symbol_hash);
+	hash_init(elf->symbol_name_hash);
 	hash_init(elf->section_hash);
 	hash_init(elf->section_name_hash);
 	INIT_LIST_HEAD(&elf->sections);
--- a/tools/objtool/elf.h
+++ b/tools/objtool/elf.h
@@ -47,6 +47,7 @@ struct symbol {
 	struct list_head list;
 	struct rb_node node;
 	struct hlist_node hash;
+	struct hlist_node name_hash;
 	GElf_Sym sym;
 	struct section *sec;
 	char *name;
@@ -77,6 +78,7 @@ struct elf {
 	char *name;
 	struct list_head sections;
 	DECLARE_HASHTABLE(symbol_hash, 20);
+	DECLARE_HASHTABLE(symbol_name_hash, 20);
 	DECLARE_HASHTABLE(section_hash, 16);
 	DECLARE_HASHTABLE(section_name_hash, 16);
 };



^ permalink raw reply	[flat|nested] 89+ messages in thread

* [PATCH v3 14/26] objtool: Optimize read_sections()
  2020-03-24 15:31 [PATCH v3 00/26] objtool: vmlinux.o and noinstr validation Peter Zijlstra
                   ` (12 preceding siblings ...)
  2020-03-24 15:31 ` [PATCH v3 13/26] objtool: Optimize find_symbol_by_name() Peter Zijlstra
@ 2020-03-24 15:31 ` Peter Zijlstra
  2020-03-25 12:10   ` Miroslav Benes
                     ` (2 more replies)
  2020-03-24 15:31 ` [PATCH v3 15/26] objtool: Delete cleanup() Peter Zijlstra
                   ` (11 subsequent siblings)
  25 siblings, 3 replies; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 15:31 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, peterz, mhiramat, mbenes, brgerst

Perf showed that __hash_init() is a significant portion of
read_sections(), so instead of doing a per section rela_hash, use an
elf-wide rela_hash.

Statistics show us there are about 1.1 million relas, so size it
accordingly.

This reduces the objtool on vmlinux.o runtime to a third, from 15 to 5
seconds.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
---
 tools/objtool/check.c   |   18 +++++++++---------
 tools/objtool/elf.c     |   24 ++++++++++++++----------
 tools/objtool/elf.h     |   21 +++++++++++++++++----
 tools/objtool/orc_gen.c |    9 +++++----
 tools/objtool/special.c |    4 ++--
 5 files changed, 47 insertions(+), 29 deletions(-)

--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -587,8 +587,8 @@ static int add_jump_destinations(struct
 		if (insn->ignore || insn->offset == FAKE_JUMP_OFFSET)
 			continue;
 
-		rela = find_rela_by_dest_range(insn->sec, insn->offset,
-					       insn->len);
+		rela = find_rela_by_dest_range(file->elf, insn->sec,
+					       insn->offset, insn->len);
 		if (!rela) {
 			dest_sec = insn->sec;
 			dest_off = insn->offset + insn->len + insn->immediate;
@@ -684,8 +684,8 @@ static int add_call_destinations(struct
 		if (insn->type != INSN_CALL)
 			continue;
 
-		rela = find_rela_by_dest_range(insn->sec, insn->offset,
-					       insn->len);
+		rela = find_rela_by_dest_range(file->elf, insn->sec,
+					       insn->offset, insn->len);
 		if (!rela) {
 			dest_off = insn->offset + insn->len + insn->immediate;
 			insn->call_dest = find_func_by_offset(insn->sec, dest_off);
@@ -814,7 +814,7 @@ static int handle_group_alt(struct objto
 		 */
 		if ((insn->offset != special_alt->new_off ||
 		    (insn->type != INSN_CALL && !is_static_jump(insn))) &&
-		    find_rela_by_dest_range(insn->sec, insn->offset, insn->len)) {
+		    find_rela_by_dest_range(file->elf, insn->sec, insn->offset, insn->len)) {
 
 			WARN_FUNC("unsupported relocation in alternatives section",
 				  insn->sec, insn->offset);
@@ -1084,8 +1084,8 @@ static struct rela *find_jump_table(stru
 		    break;
 
 		/* look for a relocation which references .rodata */
-		text_rela = find_rela_by_dest_range(insn->sec, insn->offset,
-						    insn->len);
+		text_rela = find_rela_by_dest_range(file->elf, insn->sec,
+						    insn->offset, insn->len);
 		if (!text_rela || text_rela->sym->type != STT_SECTION ||
 		    !text_rela->sym->sec->rodata)
 			continue;
@@ -1114,7 +1114,7 @@ static struct rela *find_jump_table(stru
 		 * should reference text in the same function as the original
 		 * instruction.
 		 */
-		table_rela = find_rela_by_dest(table_sec, table_offset);
+		table_rela = find_rela_by_dest(file->elf, table_sec, table_offset);
 		if (!table_rela)
 			continue;
 		dest_insn = find_insn(file, table_rela->sym->sec, table_rela->addend);
@@ -1250,7 +1250,7 @@ static int read_unwind_hints(struct objt
 	for (i = 0; i < sec->len / sizeof(struct unwind_hint); i++) {
 		hint = (struct unwind_hint *)sec->data->d_buf + i;
 
-		rela = find_rela_by_dest(sec, i * sizeof(*hint));
+		rela = find_rela_by_dest(file->elf, sec, i * sizeof(*hint));
 		if (!rela) {
 			WARN("can't find rela for unwind_hints[%d]", i);
 			return -1;
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -212,8 +212,8 @@ struct symbol *find_symbol_by_name(struc
 	return NULL;
 }
 
-struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset,
-				     unsigned int len)
+struct rela *find_rela_by_dest_range(struct elf *elf, struct section *sec,
+				     unsigned long offset, unsigned int len)
 {
 	struct rela *rela;
 	unsigned long o;
@@ -221,17 +221,22 @@ struct rela *find_rela_by_dest_range(str
 	if (!sec->rela)
 		return NULL;
 
-	for (o = offset; o < offset + len; o++)
-		hash_for_each_possible(sec->rela->rela_hash, rela, hash, o)
-			if (rela->offset == o)
+	sec = sec->rela;
+
+	for (o = offset; o < offset + len; o++) {
+		hash_for_each_possible(elf->rela_hash, rela, hash,
+				       sec_offset_hash(sec, o)) {
+			if (rela->sec == sec && rela->offset == o)
 				return rela;
+		}
+	}
 
 	return NULL;
 }
 
-struct rela *find_rela_by_dest(struct section *sec, unsigned long offset)
+struct rela *find_rela_by_dest(struct elf *elf, struct section *sec, unsigned long offset)
 {
-	return find_rela_by_dest_range(sec, offset, 1);
+	return find_rela_by_dest_range(elf, sec, offset, 1);
 }
 
 static int read_sections(struct elf *elf)
@@ -261,7 +266,6 @@ static int read_sections(struct elf *elf
 
 		INIT_LIST_HEAD(&sec->symbol_list);
 		INIT_LIST_HEAD(&sec->rela_list);
-		hash_init(sec->rela_hash);
 
 		s = elf_getscn(elf->elf, i);
 		if (!s) {
@@ -493,7 +497,7 @@ static int read_relas(struct elf *elf)
 			}
 
 			list_add_tail(&rela->list, &sec->rela_list);
-			hash_add(sec->rela_hash, &rela->hash, rela->offset);
+			hash_add(elf->rela_hash, &rela->hash, rela_hash(rela));
 			nr_rela++;
 		}
 		max_rela = max(max_rela, nr_rela);
@@ -526,6 +530,7 @@ struct elf *elf_read(const char *name, i
 	hash_init(elf->symbol_name_hash);
 	hash_init(elf->section_hash);
 	hash_init(elf->section_name_hash);
+	hash_init(elf->rela_hash);
 	INIT_LIST_HEAD(&elf->sections);
 
 	elf->fd = open(name, flags);
@@ -586,7 +591,6 @@ struct section *elf_create_section(struc
 
 	INIT_LIST_HEAD(&sec->symbol_list);
 	INIT_LIST_HEAD(&sec->rela_list);
-	hash_init(sec->rela_hash);
 
 	s = elf_newscn(elf->elf);
 	if (!s) {
--- a/tools/objtool/elf.h
+++ b/tools/objtool/elf.h
@@ -33,7 +33,6 @@ struct section {
 	struct rb_root symbol_tree;
 	struct list_head symbol_list;
 	struct list_head rela_list;
-	DECLARE_HASHTABLE(rela_hash, 16);
 	struct section *base, *rela;
 	struct symbol *sym;
 	Elf_Data *data;
@@ -81,8 +80,22 @@ struct elf {
 	DECLARE_HASHTABLE(symbol_name_hash, 20);
 	DECLARE_HASHTABLE(section_hash, 16);
 	DECLARE_HASHTABLE(section_name_hash, 16);
+	DECLARE_HASHTABLE(rela_hash, 20);
 };
 
+static inline u32 sec_offset_hash(struct section *sec, unsigned long offset)
+{
+	u32 ol = offset, oh = offset >> 32, idx = sec->idx;
+
+	__jhash_mix(ol, oh, idx);
+
+	return ol;
+}
+
+static inline u32 rela_hash(struct rela *rela)
+{
+	return sec_offset_hash(rela->sec, rela->offset);
+}
 
 struct elf *elf_read(const char *name, int flags);
 struct section *find_section_by_name(struct elf *elf, const char *name);
@@ -90,9 +103,9 @@ struct symbol *find_func_by_offset(struc
 struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset);
 struct symbol *find_symbol_by_name(struct elf *elf, const char *name);
 struct symbol *find_symbol_containing(struct section *sec, unsigned long offset);
-struct rela *find_rela_by_dest(struct section *sec, unsigned long offset);
-struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset,
-				     unsigned int len);
+struct rela *find_rela_by_dest(struct elf *elf, struct section *sec, unsigned long offset);
+struct rela *find_rela_by_dest_range(struct elf *elf, struct section *sec,
+				     unsigned long offset, unsigned int len);
 struct symbol *find_func_containing(struct section *sec, unsigned long offset);
 struct section *elf_create_section(struct elf *elf, const char *name, size_t
 				   entsize, int nr);
--- a/tools/objtool/orc_gen.c
+++ b/tools/objtool/orc_gen.c
@@ -81,7 +81,7 @@ int create_orc(struct objtool_file *file
 	return 0;
 }
 
-static int create_orc_entry(struct section *u_sec, struct section *ip_relasec,
+static int create_orc_entry(struct elf *elf, struct section *u_sec, struct section *ip_relasec,
 				unsigned int idx, struct section *insn_sec,
 				unsigned long insn_off, struct orc_entry *o)
 {
@@ -109,9 +109,10 @@ static int create_orc_entry(struct secti
 	rela->addend = insn_off;
 	rela->type = R_X86_64_PC32;
 	rela->offset = idx * sizeof(int);
+	rela->sec = ip_relasec;
 
 	list_add_tail(&rela->list, &ip_relasec->rela_list);
-	hash_add(ip_relasec->rela_hash, &rela->hash, rela->offset);
+	hash_add(elf->rela_hash, &rela->hash, rela_hash(rela));
 
 	return 0;
 }
@@ -182,7 +183,7 @@ int create_orc_sections(struct objtool_f
 			if (!prev_insn || memcmp(&insn->orc, &prev_insn->orc,
 						 sizeof(struct orc_entry))) {
 
-				if (create_orc_entry(u_sec, ip_relasec, idx,
+				if (create_orc_entry(file->elf, u_sec, ip_relasec, idx,
 						     insn->sec, insn->offset,
 						     &insn->orc))
 					return -1;
@@ -194,7 +195,7 @@ int create_orc_sections(struct objtool_f
 
 		/* section terminator */
 		if (prev_insn) {
-			if (create_orc_entry(u_sec, ip_relasec, idx,
+			if (create_orc_entry(file->elf, u_sec, ip_relasec, idx,
 					     prev_insn->sec,
 					     prev_insn->offset + prev_insn->len,
 					     &empty))
--- a/tools/objtool/special.c
+++ b/tools/objtool/special.c
@@ -118,7 +118,7 @@ static int get_alt_entry(struct elf *elf
 		}
 	}
 
-	orig_rela = find_rela_by_dest(sec, offset + entry->orig);
+	orig_rela = find_rela_by_dest(elf, sec, offset + entry->orig);
 	if (!orig_rela) {
 		WARN_FUNC("can't find orig rela", sec, offset + entry->orig);
 		return -1;
@@ -133,7 +133,7 @@ static int get_alt_entry(struct elf *elf
 	alt->orig_off = orig_rela->addend;
 
 	if (!entry->group || alt->new_len) {
-		new_rela = find_rela_by_dest(sec, offset + entry->new);
+		new_rela = find_rela_by_dest(elf, sec, offset + entry->new);
 		if (!new_rela) {
 			WARN_FUNC("can't find new rela",
 				  sec, offset + entry->new);



^ permalink raw reply	[flat|nested] 89+ messages in thread

* [PATCH v3 15/26] objtool: Delete cleanup()
  2020-03-24 15:31 [PATCH v3 00/26] objtool: vmlinux.o and noinstr validation Peter Zijlstra
                   ` (13 preceding siblings ...)
  2020-03-24 15:31 ` [PATCH v3 14/26] objtool: Optimize read_sections() Peter Zijlstra
@ 2020-03-24 15:31 ` Peter Zijlstra
  2020-03-25 12:11   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  2020-03-24 15:31 ` [PATCH v3 16/26] objtool: Optimize find_rela_by_dest_range() Peter Zijlstra
                   ` (10 subsequent siblings)
  25 siblings, 2 replies; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 15:31 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, peterz, mhiramat, mbenes, brgerst

Perf shows we spend a measurable amount of time spend cleaning up
right before we exit anyway. Avoid the needsless work and just
terminate.

This reduces objtool on vmlinux.o runtime from 5.4s to 4.8s

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
---
 tools/objtool/check.c |   19 -------------------
 1 file changed, 19 deletions(-)

--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -2476,23 +2476,6 @@ static int validate_reachable_instructio
 	return 0;
 }
 
-static void cleanup(struct objtool_file *file)
-{
-	struct instruction *insn, *tmpinsn;
-	struct alternative *alt, *tmpalt;
-
-	list_for_each_entry_safe(insn, tmpinsn, &file->insn_list, list) {
-		list_for_each_entry_safe(alt, tmpalt, &insn->alts, list) {
-			list_del(&alt->list);
-			free(alt);
-		}
-		list_del(&insn->list);
-		hash_del(&insn->hash);
-		free(insn);
-	}
-	elf_close(file->elf);
-}
-
 static struct objtool_file file;
 
 int check(const char *_objname, bool orc)
@@ -2560,8 +2543,6 @@ int check(const char *_objname, bool orc
 	}
 
 out:
-	cleanup(&file);
-
 	if (ret < 0) {
 		/*
 		 *  Fatal error.  The binary is corrupt or otherwise broken in



^ permalink raw reply	[flat|nested] 89+ messages in thread

* [PATCH v3 16/26] objtool: Optimize find_rela_by_dest_range()
  2020-03-24 15:31 [PATCH v3 00/26] objtool: vmlinux.o and noinstr validation Peter Zijlstra
                   ` (14 preceding siblings ...)
  2020-03-24 15:31 ` [PATCH v3 15/26] objtool: Delete cleanup() Peter Zijlstra
@ 2020-03-24 15:31 ` Peter Zijlstra
  2020-03-25 12:19   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  2020-03-24 15:31 ` [PATCH v3 17/26] objtool: Re-arrange validate_functions() Peter Zijlstra
                   ` (9 subsequent siblings)
  25 siblings, 2 replies; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 15:31 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, peterz, mhiramat, mbenes, brgerst

Perf shows there is significant time in find_rela_by_dest(); this is
because we have to iterate the address space per byte, looking for
relocation entries.

Optimize this by reducing the address space granularity.

This reduces objtool on vmlinux.o runtime from 4.8 to 4.4 seconds.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
---
 tools/objtool/elf.c |   15 +++++++++++----
 tools/objtool/elf.h |   16 +++++++++++++++-
 2 files changed, 26 insertions(+), 5 deletions(-)

--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -215,7 +215,7 @@ struct symbol *find_symbol_by_name(struc
 struct rela *find_rela_by_dest_range(struct elf *elf, struct section *sec,
 				     unsigned long offset, unsigned int len)
 {
-	struct rela *rela;
+	struct rela *rela, *r = NULL;
 	unsigned long o;
 
 	if (!sec->rela)
@@ -223,12 +223,19 @@ struct rela *find_rela_by_dest_range(str
 
 	sec = sec->rela;
 
-	for (o = offset; o < offset + len; o++) {
+	for_offset_range(o, offset, offset + len) {
 		hash_for_each_possible(elf->rela_hash, rela, hash,
 				       sec_offset_hash(sec, o)) {
-			if (rela->sec == sec && rela->offset == o)
-				return rela;
+			if (rela->sec != sec)
+				continue;
+
+			if (rela->offset >= offset && rela->offset < offset + len) {
+				if (!r || rela->offset < r->offset)
+					r = rela;
+			}
 		}
+		if (r)
+			return r;
 	}
 
 	return NULL;
--- a/tools/objtool/elf.h
+++ b/tools/objtool/elf.h
@@ -83,9 +83,23 @@ struct elf {
 	DECLARE_HASHTABLE(rela_hash, 20);
 };
 
+#define OFFSET_STRIDE_BITS	4
+#define OFFSET_STRIDE		(1UL << OFFSET_STRIDE_BITS)
+#define OFFSET_STRIDE_MASK	(~(OFFSET_STRIDE - 1))
+
+#define for_offset_range(_offset, _start, _end)		\
+	for (_offset = ((_start) & OFFSET_STRIDE_MASK);	\
+	     _offset <= ((_end) & OFFSET_STRIDE_MASK);	\
+	     _offset += OFFSET_STRIDE)
+
 static inline u32 sec_offset_hash(struct section *sec, unsigned long offset)
 {
-	u32 ol = offset, oh = offset >> 32, idx = sec->idx;
+	u32 ol, oh, idx = sec->idx;
+
+	offset &= OFFSET_STRIDE_MASK;
+
+	ol = offset;
+	oh = offset >> 32;
 
 	__jhash_mix(ol, oh, idx);
 



^ permalink raw reply	[flat|nested] 89+ messages in thread

* [PATCH v3 17/26] objtool: Re-arrange validate_functions()
  2020-03-24 15:31 [PATCH v3 00/26] objtool: vmlinux.o and noinstr validation Peter Zijlstra
                   ` (15 preceding siblings ...)
  2020-03-24 15:31 ` [PATCH v3 16/26] objtool: Optimize find_rela_by_dest_range() Peter Zijlstra
@ 2020-03-24 15:31 ` Peter Zijlstra
  2020-03-24 21:10   ` Josh Poimboeuf
                     ` (2 more replies)
  2020-03-24 15:31 ` [PATCH v3 18/26] objtool: Fix !CFI insn_state propagation Peter Zijlstra
                   ` (8 subsequent siblings)
  25 siblings, 3 replies; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 15:31 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, peterz, mhiramat, mbenes, brgerst

In preparation to adding a vmlinux.o specific pass, rearrange some
code. No functional changes intended.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
 tools/objtool/check.c |   60 ++++++++++++++++++++++++++++----------------------
 1 file changed, 34 insertions(+), 26 deletions(-)

--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -2413,9 +2413,8 @@ static bool ignore_unreachable_insn(stru
 	return false;
 }
 
-static int validate_functions(struct objtool_file *file)
+static int validate_section(struct objtool_file *file, struct section *sec)
 {
-	struct section *sec;
 	struct symbol *func;
 	struct instruction *insn;
 	struct insn_state state;
@@ -2428,35 +2427,44 @@ static int validate_functions(struct obj
 	       CFI_NUM_REGS * sizeof(struct cfi_reg));
 	state.stack_size = initial_func_cfi.cfa.offset;
 
-	for_each_sec(file, sec) {
-		list_for_each_entry(func, &sec->symbol_list, list) {
-			if (func->type != STT_FUNC)
-				continue;
-
-			if (!func->len) {
-				WARN("%s() is missing an ELF size annotation",
-				     func->name);
-				warnings++;
-			}
-
-			if (func->pfunc != func || func->alias != func)
-				continue;
-
-			insn = find_insn(file, sec, func->offset);
-			if (!insn || insn->ignore || insn->visited)
-				continue;
-
-			state.uaccess = func->uaccess_safe;
-
-			ret = validate_branch(file, func, insn, state);
-			if (ret && backtrace)
-				BT_FUNC("<=== (func)", insn);
-			warnings += ret;
+	list_for_each_entry(func, &sec->symbol_list, list) {
+		if (func->type != STT_FUNC)
+			continue;
+
+		if (!func->len) {
+			WARN("%s() is missing an ELF size annotation",
+					func->name);
+			warnings++;
 		}
+
+		if (func->pfunc != func || func->alias != func)
+			continue;
+
+		insn = find_insn(file, sec, func->offset);
+		if (!insn || insn->ignore || insn->visited)
+			continue;
+
+		state.uaccess = func->uaccess_safe;
+
+		ret = validate_branch(file, func, insn, state);
+		if (ret && backtrace)
+			BT_FUNC("<=== (func)", insn);
+		warnings += ret;
 	}
 
 	return warnings;
 }
+
+static int validate_functions(struct objtool_file *file)
+{
+	struct section *sec;
+	int warnings = 0;
+
+	for_each_sec(file, sec)
+		warnings += validate_section(file, sec);
+
+	return warnings;
+}
 
 static int validate_reachable_instructions(struct objtool_file *file)
 {



^ permalink raw reply	[flat|nested] 89+ messages in thread

* [PATCH v3 18/26] objtool: Fix !CFI insn_state propagation
  2020-03-24 15:31 [PATCH v3 00/26] objtool: vmlinux.o and noinstr validation Peter Zijlstra
                   ` (16 preceding siblings ...)
  2020-03-24 15:31 ` [PATCH v3 17/26] objtool: Re-arrange validate_functions() Peter Zijlstra
@ 2020-03-24 15:31 ` Peter Zijlstra
  2020-03-24 21:40   ` Josh Poimboeuf
                     ` (4 more replies)
  2020-03-24 15:31 ` [PATCH v3 19/26] objtool: Implement noinstr validation Peter Zijlstra
                   ` (7 subsequent siblings)
  25 siblings, 5 replies; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 15:31 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, peterz, mhiramat, mbenes, brgerst

Objtool keeps per instruction CFI state in struct insn_state and will
save/restore this where required. However, insn_state has grown some
!CFI state, and this must not be saved/restored (and thus lost).

Fix this by explicitly preserving the !CFI state and clarify by
restucturing the code and adding a comment.

XXX, the insn==first condition is not handled right.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
 tools/objtool/check.c |   95 +++++++++++++++++++++++++++++---------------------
 tools/objtool/check.h |    8 ++++
 2 files changed, 64 insertions(+), 39 deletions(-)

--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -2033,6 +2033,59 @@ static int validate_return(struct symbol
 	return 0;
 }
 
+static int apply_insn_hint(struct objtool_file *file, struct section *sec,
+			   struct symbol *func, struct instruction *first,
+			   struct instruction *insn, struct insn_state *state)
+{
+	struct insn_state old = *state;
+
+	if (insn->restore) {
+		struct instruction *save_insn, *i;
+
+		i = insn;
+		save_insn = NULL;
+		sym_for_each_insn_continue_reverse(file, func, i) {
+			if (i->save) {
+				save_insn = i;
+				break;
+			}
+		}
+
+		if (!save_insn) {
+			WARN_FUNC("no corresponding CFI save for CFI restore",
+					sec, insn->offset);
+			return 1;
+		}
+
+		if (!save_insn->visited) {
+			/*
+			 * Oops, no state to copy yet.
+			 * Hopefully we can reach this
+			 * instruction from another branch
+			 * after the save insn has been
+			 * visited.
+			 */
+			if (insn == first)
+				return 0; // XXX
+
+			WARN_FUNC("objtool isn't smart enough to handle this CFI save/restore combo",
+					sec, insn->offset);
+			return 1;
+		}
+
+		insn->state = save_insn->state;
+	}
+
+	*state = insn->state;
+
+	/* restore !CFI state */
+	state->df = old.df;
+	state->uaccess = old.uaccess;
+	state->uaccess_stack = old.uaccess_stack;
+
+	return 0;
+}
+
 /*
  * Follow the branch starting at the given instruction, and recursively follow
  * any other branches (jumps).  Meanwhile, track the frame pointer state at
@@ -2082,45 +2135,9 @@ static int validate_branch(struct objtoo
 		}
 
 		if (insn->hint) {
-			if (insn->restore) {
-				struct instruction *save_insn, *i;
-
-				i = insn;
-				save_insn = NULL;
-				sym_for_each_insn_continue_reverse(file, func, i) {
-					if (i->save) {
-						save_insn = i;
-						break;
-					}
-				}
-
-				if (!save_insn) {
-					WARN_FUNC("no corresponding CFI save for CFI restore",
-						  sec, insn->offset);
-					return 1;
-				}
-
-				if (!save_insn->visited) {
-					/*
-					 * Oops, no state to copy yet.
-					 * Hopefully we can reach this
-					 * instruction from another branch
-					 * after the save insn has been
-					 * visited.
-					 */
-					if (insn == first)
-						return 0;
-
-					WARN_FUNC("objtool isn't smart enough to handle this CFI save/restore combo",
-						  sec, insn->offset);
-					return 1;
-				}
-
-				insn->state = save_insn->state;
-			}
-
-			state = insn->state;
-
+			ret = apply_insn_hint(file, sec, func, first, insn, &state);
+			if (ret)
+				return ret;
 		} else
 			insn->state = state;
 
--- a/tools/objtool/check.h
+++ b/tools/objtool/check.h
@@ -13,6 +13,14 @@
 #include "orc.h"
 #include <linux/hashtable.h>
 
+/*
+ * This structure (mostly) contains the instruction level CFI (Call Frame
+ * Information) and is saved/restored where appropriate; see validate_branch().
+ *
+ * However it does also contain a limited amount of !CFI state; this state must
+ * not be saved/restored along with the CFI information and is manually
+ * preserved. See apply_insn_hint().
+ */
 struct insn_state {
 	struct cfi_reg cfa;
 	struct cfi_reg regs[CFI_NUM_REGS];



^ permalink raw reply	[flat|nested] 89+ messages in thread

* [PATCH v3 19/26] objtool: Implement noinstr validation
  2020-03-24 15:31 [PATCH v3 00/26] objtool: vmlinux.o and noinstr validation Peter Zijlstra
                   ` (17 preceding siblings ...)
  2020-03-24 15:31 ` [PATCH v3 18/26] objtool: Fix !CFI insn_state propagation Peter Zijlstra
@ 2020-03-24 15:31 ` Peter Zijlstra
  2020-03-24 21:41   ` Josh Poimboeuf
  2020-03-25 14:44   ` [PATCH v3.1 " Peter Zijlstra
  2020-03-24 15:31 ` [PATCH v3 20/26] objtool: Optimize !vmlinux.o again Peter Zijlstra
                   ` (6 subsequent siblings)
  25 siblings, 2 replies; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 15:31 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, peterz, mhiramat, mbenes, brgerst

Validate that any call out of .noinstr.text is in between
instr_begin() and instr_end() annotations.

This annotation is useful to ensure correct behaviour wrt tracing
sensitive code like entry/exit and idle code. When we run code in a
sensitive context we want a guarantee no unknown code is ran.

Since this validation relies on knowing the section of call
destination symbols, we must run it on vmlinux.o instead of on
individual object files.

Add two options:

 -d/--duplicate "duplicate validation for vmlinux"
 -l/--vmlinux "vmlinux.o validation"

Where the latter auto-detects when objname ends with "vmlinux.o" and
the former will force all validations, also those already done on
!vmlinux object files.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
 tools/objtool/builtin-check.c |   11 +++-
 tools/objtool/builtin.h       |    2 
 tools/objtool/check.c         |   97 ++++++++++++++++++++++++++++++++++++++++++
 tools/objtool/check.h         |    3 +
 tools/objtool/elf.h           |    2 
 5 files changed, 111 insertions(+), 4 deletions(-)

--- a/tools/objtool/builtin-check.c
+++ b/tools/objtool/builtin-check.c
@@ -14,10 +14,11 @@
  */
 
 #include <subcmd/parse-options.h>
+#include <string.h>
 #include "builtin.h"
 #include "check.h"
 
-bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats;
+bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats, validate_dup, vmlinux;
 
 static const char * const check_usage[] = {
 	"objtool check [<options>] file.o",
@@ -32,12 +33,14 @@ const struct option check_options[] = {
 	OPT_BOOLEAN('b', "backtrace", &backtrace, "unwind on error"),
 	OPT_BOOLEAN('a', "uaccess", &uaccess, "enable uaccess checking"),
 	OPT_BOOLEAN('s', "stats", &stats, "print statistics"),
+	OPT_BOOLEAN('d', "duplicate", &validate_dup, "duplicate validation for vmlinux.o"),
+	OPT_BOOLEAN('l', "vmlinux", &vmlinux, "vmlinux.o validation"),
 	OPT_END(),
 };
 
 int cmd_check(int argc, const char **argv)
 {
-	const char *objname;
+	const char *objname, *s;
 
 	argc = parse_options(argc, argv, check_options, check_usage, 0);
 
@@ -46,5 +49,9 @@ int cmd_check(int argc, const char **arg
 
 	objname = argv[0];
 
+	s = strstr(objname, "vmlinux.o");
+	if (s && !s[9])
+		vmlinux = true;
+
 	return check(objname, false);
 }
--- a/tools/objtool/builtin.h
+++ b/tools/objtool/builtin.h
@@ -8,7 +8,7 @@
 #include <subcmd/parse-options.h>
 
 extern const struct option check_options[];
-extern bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats;
+extern bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats, validate_dup, vmlinux;
 
 extern int cmd_check(int argc, const char **argv);
 extern int cmd_orc(int argc, const char **argv);
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -252,6 +252,9 @@ static int decode_instructions(struct ob
 		    strncmp(sec->name, ".discard.", 9))
 			sec->text = true;
 
+		if (!strcmp(sec->name, ".noinstr.text"))
+			sec->noinstr = true;
+
 		for (offset = 0; offset < sec->len; offset += insn->len) {
 			insn = malloc(sizeof(*insn));
 			if (!insn) {
@@ -1350,6 +1353,53 @@ static int read_retpoline_hints(struct o
 	return 0;
 }
 
+static int read_instr_hints(struct objtool_file *file)
+{
+	struct section *sec;
+	struct instruction *insn;
+	struct rela *rela;
+
+	sec = find_section_by_name(file->elf, ".rela.discard.instr_end");
+	if (!sec)
+		return 0;
+
+	list_for_each_entry(rela, &sec->rela_list, list) {
+		if (rela->sym->type != STT_SECTION) {
+			WARN("unexpected relocation symbol type in %s", sec->name);
+			return -1;
+		}
+
+		insn = find_insn(file, rela->sym->sec, rela->addend);
+		if (!insn) {
+			WARN("bad .discard.instr_end entry");
+			return -1;
+		}
+
+		insn->instr--;
+	}
+
+	sec = find_section_by_name(file->elf, ".rela.discard.instr_begin");
+	if (!sec)
+		return 0;
+
+	list_for_each_entry(rela, &sec->rela_list, list) {
+		if (rela->sym->type != STT_SECTION) {
+			WARN("unexpected relocation symbol type in %s", sec->name);
+			return -1;
+		}
+
+		insn = find_insn(file, rela->sym->sec, rela->addend);
+		if (!insn) {
+			WARN("bad .discard.instr_begin entry");
+			return -1;
+		}
+
+		insn->instr++;
+	}
+
+	return 0;
+}
+
 static void mark_rodata(struct objtool_file *file)
 {
 	struct section *sec;
@@ -1421,6 +1471,10 @@ static int decode_sections(struct objtoo
 	if (ret)
 		return ret;
 
+	ret = read_instr_hints(file);
+	if (ret)
+		return ret;
+
 	return 0;
 }
 
@@ -1972,6 +2026,13 @@ static inline const char *call_dest_name
 
 static int validate_call(struct instruction *insn, struct insn_state *state)
 {
+	if (state->noinstr && state->instr <= 0 &&
+	    (!insn->call_dest || insn->call_dest->sec != insn->sec)) {
+		WARN_FUNC("call to %s() leaves .noinstr.text section",
+				insn->sec, insn->offset, call_dest_name(insn));
+		return 1;
+	}
+
 	if (state->uaccess && !func_uaccess_safe(insn->call_dest)) {
 		WARN_FUNC("call to %s() with UACCESS enabled",
 				insn->sec, insn->offset, call_dest_name(insn));
@@ -2000,6 +2061,12 @@ static int validate_sibling_call(struct
 
 static int validate_return(struct symbol *func, struct instruction *insn, struct insn_state *state)
 {
+	if (state->noinstr && state->instr > 0) {
+		WARN_FUNC("return with instrumentation enabled",
+			  insn->sec, insn->offset);
+		return 1;
+	}
+
 	if (state->uaccess && !func_uaccess_safe(func)) {
 		WARN_FUNC("return with UACCESS enabled",
 			  insn->sec, insn->offset);
@@ -2082,6 +2149,8 @@ static int apply_insn_hint(struct objtoo
 	state->df = old.df;
 	state->uaccess = old.uaccess;
 	state->uaccess_stack = old.uaccess_stack;
+	state->noinstr = old.noinstr;
+	state->instr = old.instr;
 
 	return 0;
 }
@@ -2444,6 +2513,14 @@ static int validate_section(struct objto
 	       CFI_NUM_REGS * sizeof(struct cfi_reg));
 	state.stack_size = initial_func_cfi.cfa.offset;
 
+	/*
+	 * We need the full vmlinux for noinstr validation, otherwise we can
+	 * not correctly determine insn->call_dest->sec (external symbols do
+	 * not have a section).
+	 */
+	if (vmlinux)
+		state.noinstr = sec->noinstr;
+
 	list_for_each_entry(func, &sec->symbol_list, list) {
 		if (func->type != STT_FUNC)
 			continue;
@@ -2472,6 +2549,17 @@ static int validate_section(struct objto
 	return warnings;
 }
 
+static int validate_vmlinux_functions(struct objtool_file *file)
+{
+	struct section *sec;
+
+	sec = find_section_by_name(file->elf, ".noinstr.text");
+	if (!sec)
+		return 0;
+
+	return validate_section(file, sec);
+}
+
 static int validate_functions(struct objtool_file *file)
 {
 	struct section *sec;
@@ -2529,6 +2617,15 @@ int check(const char *_objname, bool orc
 	if (list_empty(&file.insn_list))
 		goto out;
 
+	if (vmlinux && !validate_dup) {
+		ret = validate_vmlinux_functions(&file);
+		if (ret < 0)
+			goto out;
+
+		warnings += ret;
+		goto out;
+	}
+
 	if (retpoline) {
 		ret = validate_retpoline(&file);
 		if (ret < 0)
--- a/tools/objtool/check.h
+++ b/tools/objtool/check.h
@@ -28,6 +28,8 @@ struct insn_state {
 	unsigned char type;
 	bool bp_scratch;
 	bool drap, end, uaccess, df;
+	bool noinstr;
+	s8 instr;
 	unsigned int uaccess_stack;
 	int drap_reg, drap_offset;
 	struct cfi_reg vals[CFI_NUM_REGS];
@@ -43,6 +45,7 @@ struct instruction {
 	unsigned long immediate;
 	bool alt_group, dead_end, ignore, hint, save, restore, ignore_alts;
 	bool retpoline_safe;
+	s8 instr;
 	u8 visited;
 	struct symbol *call_dest;
 	struct instruction *jump_dest;
--- a/tools/objtool/elf.h
+++ b/tools/objtool/elf.h
@@ -39,7 +39,7 @@ struct section {
 	char *name;
 	int idx;
 	unsigned int len;
-	bool changed, text, rodata;
+	bool changed, text, rodata, noinstr;
 };
 
 struct symbol {



^ permalink raw reply	[flat|nested] 89+ messages in thread

* [PATCH v3 20/26] objtool: Optimize !vmlinux.o again
  2020-03-24 15:31 [PATCH v3 00/26] objtool: vmlinux.o and noinstr validation Peter Zijlstra
                   ` (18 preceding siblings ...)
  2020-03-24 15:31 ` [PATCH v3 19/26] objtool: Implement noinstr validation Peter Zijlstra
@ 2020-03-24 15:31 ` Peter Zijlstra
  2020-03-24 15:31 ` [PATCH v3 21/26] objtool: Use sec_offset_hash() for insn_hash Peter Zijlstra
                   ` (5 subsequent siblings)
  25 siblings, 0 replies; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 15:31 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, peterz, mhiramat, mbenes, brgerst

When doing kbuild tests to see if the objtool changes affected those I
found that there was a measurable regression:

          pre		  post

  real    1m13.594        1m16.488s
  user    34m58.246s      35m23.947s
  sys     4m0.393s        4m27.312s

Perf showed that for small files the increased hash-table sizes were a
measurable difference. Since we already have -l "vmlinux" to
distinguish between the modes, make it also use a smaller portion of
the hash-tables.

This flips it into a small win:

  real    1m14.143s
  user    34m49.292s
  sys     3m44.746s

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
---
 tools/objtool/elf.c     |   62 +++++++++++++++++++++++++++++++++---------------
 tools/objtool/elf.h     |   13 ++++++----
 tools/objtool/orc_gen.c |    3 --
 3 files changed, 52 insertions(+), 26 deletions(-)

--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -27,6 +27,22 @@ static inline u32 str_hash(const char *s
 	return jhash(str, strlen(str), 0);
 }
 
+static inline int elf_hash_bits(void)
+{
+	return vmlinux ? ELF_HASH_BITS : 16;
+}
+
+#define elf_hash_add(hashtable, node, key) \
+	hlist_add_head(node, &hashtable[hash_min(key, elf_hash_bits())])
+
+static void elf_hash_init(struct hlist_head *table)
+{
+	__hash_init(table, 1U << elf_hash_bits());
+}
+
+#define elf_hash_for_each_possible(name, obj, member, key)			\
+	hlist_for_each_entry(obj, &name[hash_min(key, elf_hash_bits())], member)
+
 static void rb_add(struct rb_root *tree, struct rb_node *node,
 		   int (*cmp)(struct rb_node *, const struct rb_node *))
 {
@@ -115,7 +131,7 @@ struct section *find_section_by_name(str
 {
 	struct section *sec;
 
-	hash_for_each_possible(elf->section_name_hash, sec, name_hash, str_hash(name))
+	elf_hash_for_each_possible(elf->section_name_hash, sec, name_hash, str_hash(name))
 		if (!strcmp(sec->name, name))
 			return sec;
 
@@ -127,7 +143,7 @@ static struct section *find_section_by_i
 {
 	struct section *sec;
 
-	hash_for_each_possible(elf->section_hash, sec, hash, idx)
+	elf_hash_for_each_possible(elf->section_hash, sec, hash, idx)
 		if (sec->idx == idx)
 			return sec;
 
@@ -138,7 +154,7 @@ static struct symbol *find_symbol_by_ind
 {
 	struct symbol *sym;
 
-	hash_for_each_possible(elf->symbol_hash, sym, hash, idx)
+	elf_hash_for_each_possible(elf->symbol_hash, sym, hash, idx)
 		if (sym->idx == idx)
 			return sym;
 
@@ -205,7 +221,7 @@ struct symbol *find_symbol_by_name(struc
 {
 	struct symbol *sym;
 
-	hash_for_each_possible(elf->symbol_name_hash, sym, name_hash, str_hash(name))
+	elf_hash_for_each_possible(elf->symbol_name_hash, sym, name_hash, str_hash(name))
 		if (!strcmp(sym->name, name))
 			return sym;
 
@@ -224,7 +240,7 @@ struct rela *find_rela_by_dest_range(str
 	sec = sec->rela;
 
 	for_offset_range(o, offset, offset + len) {
-		hash_for_each_possible(elf->rela_hash, rela, hash,
+		elf_hash_for_each_possible(elf->rela_hash, rela, hash,
 				       sec_offset_hash(sec, o)) {
 			if (rela->sec != sec)
 				continue;
@@ -309,8 +325,8 @@ static int read_sections(struct elf *elf
 		sec->len = sec->sh.sh_size;
 
 		list_add_tail(&sec->list, &elf->sections);
-		hash_add(elf->section_hash, &sec->hash, sec->idx);
-		hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name));
+		elf_hash_add(elf->section_hash, &sec->hash, sec->idx);
+		elf_hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name));
 	}
 
 	if (stats)
@@ -394,8 +410,8 @@ static int read_symbols(struct elf *elf)
 		else
 			entry = &sym->sec->symbol_list;
 		list_add(&sym->list, entry);
-		hash_add(elf->symbol_hash, &sym->hash, sym->idx);
-		hash_add(elf->symbol_name_hash, &sym->name_hash, str_hash(sym->name));
+		elf_hash_add(elf->symbol_hash, &sym->hash, sym->idx);
+		elf_hash_add(elf->symbol_name_hash, &sym->name_hash, str_hash(sym->name));
 	}
 
 	if (stats)
@@ -456,6 +472,14 @@ static int read_symbols(struct elf *elf)
 	return -1;
 }
 
+void elf_add_rela(struct elf *elf, struct rela *rela)
+{
+	struct section *sec = rela->sec;
+
+	list_add_tail(&rela->list, &sec->rela_list);
+	elf_hash_add(elf->rela_hash, &rela->hash, rela_hash(rela));
+}
+
 static int read_relas(struct elf *elf)
 {
 	struct section *sec;
@@ -503,8 +527,7 @@ static int read_relas(struct elf *elf)
 				return -1;
 			}
 
-			list_add_tail(&rela->list, &sec->rela_list);
-			hash_add(elf->rela_hash, &rela->hash, rela_hash(rela));
+			elf_add_rela(elf, rela);
 			nr_rela++;
 		}
 		max_rela = max(max_rela, nr_rela);
@@ -531,15 +554,16 @@ struct elf *elf_read(const char *name, i
 		perror("malloc");
 		return NULL;
 	}
-	memset(elf, 0, sizeof(*elf));
+	memset(elf, 0, offsetof(struct elf, sections));
 
-	hash_init(elf->symbol_hash);
-	hash_init(elf->symbol_name_hash);
-	hash_init(elf->section_hash);
-	hash_init(elf->section_name_hash);
-	hash_init(elf->rela_hash);
 	INIT_LIST_HEAD(&elf->sections);
 
+	elf_hash_init(elf->symbol_hash);
+	elf_hash_init(elf->symbol_name_hash);
+	elf_hash_init(elf->section_hash);
+	elf_hash_init(elf->section_name_hash);
+	elf_hash_init(elf->rela_hash);
+
 	elf->fd = open(name, flags);
 	if (elf->fd == -1) {
 		fprintf(stderr, "objtool: Can't open '%s': %s\n",
@@ -676,8 +700,8 @@ struct section *elf_create_section(struc
 	shstrtab->changed = true;
 
 	list_add_tail(&sec->list, &elf->sections);
-	hash_add(elf->section_hash, &sec->hash, sec->idx);
-	hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name));
+	elf_hash_add(elf->section_hash, &sec->hash, sec->idx);
+	elf_hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name));
 
 	return sec;
 }
--- a/tools/objtool/elf.h
+++ b/tools/objtool/elf.h
@@ -70,17 +70,19 @@ struct rela {
 	bool jump_table_start;
 };
 
+#define ELF_HASH_BITS	20
+
 struct elf {
 	Elf *elf;
 	GElf_Ehdr ehdr;
 	int fd;
 	char *name;
 	struct list_head sections;
-	DECLARE_HASHTABLE(symbol_hash, 20);
-	DECLARE_HASHTABLE(symbol_name_hash, 20);
-	DECLARE_HASHTABLE(section_hash, 16);
-	DECLARE_HASHTABLE(section_name_hash, 16);
-	DECLARE_HASHTABLE(rela_hash, 20);
+	DECLARE_HASHTABLE(symbol_hash, ELF_HASH_BITS);
+	DECLARE_HASHTABLE(symbol_name_hash, ELF_HASH_BITS);
+	DECLARE_HASHTABLE(section_hash, ELF_HASH_BITS);
+	DECLARE_HASHTABLE(section_name_hash, ELF_HASH_BITS);
+	DECLARE_HASHTABLE(rela_hash, ELF_HASH_BITS);
 };
 
 #define OFFSET_STRIDE_BITS	4
@@ -127,6 +129,7 @@ struct section *elf_create_rela_section(
 int elf_rebuild_rela_section(struct section *sec);
 int elf_write(struct elf *elf);
 void elf_close(struct elf *elf);
+void elf_add_rela(struct elf *elf, struct rela *rela);
 
 #define for_each_sec(file, sec)						\
 	list_for_each_entry(sec, &file->elf->sections, list)
--- a/tools/objtool/orc_gen.c
+++ b/tools/objtool/orc_gen.c
@@ -111,8 +111,7 @@ static int create_orc_entry(struct elf *
 	rela->offset = idx * sizeof(int);
 	rela->sec = ip_relasec;
 
-	list_add_tail(&rela->list, &ip_relasec->rela_list);
-	hash_add(elf->rela_hash, &rela->hash, rela_hash(rela));
+	elf_add_rela(elf, rela);
 
 	return 0;
 }



^ permalink raw reply	[flat|nested] 89+ messages in thread

* [PATCH v3 21/26] objtool: Use sec_offset_hash() for insn_hash
  2020-03-24 15:31 [PATCH v3 00/26] objtool: vmlinux.o and noinstr validation Peter Zijlstra
                   ` (19 preceding siblings ...)
  2020-03-24 15:31 ` [PATCH v3 20/26] objtool: Optimize !vmlinux.o again Peter Zijlstra
@ 2020-03-24 15:31 ` Peter Zijlstra
  2020-03-24 15:31 ` [PATCH v3 22/26] objtool: Detect loading function pointers across noinstr Peter Zijlstra
                   ` (4 subsequent siblings)
  25 siblings, 0 replies; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 15:31 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, peterz, mhiramat, mbenes, brgerst

In preparation for find_insn_containing(), change insn_hash to use
sec_offset_hash().

This actually reduces runtime; probably because mixing in the section
index reduces the collisions due to text sections all starting their
instructions at offset 0.

Runtime on vmlinux.o from 3.1 to 2.5 seconds.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
---
 tools/objtool/check.c |    5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -34,9 +34,10 @@ struct instruction *find_insn(struct obj
 {
 	struct instruction *insn;
 
-	hash_for_each_possible(file->insn_hash, insn, hash, offset)
+	hash_for_each_possible(file->insn_hash, insn, hash, sec_offset_hash(sec, offset)) {
 		if (insn->sec == sec && insn->offset == offset)
 			return insn;
+	}
 
 	return NULL;
 }
@@ -276,7 +277,7 @@ static int decode_instructions(struct ob
 			if (ret)
 				goto err;
 
-			hash_add(file->insn_hash, &insn->hash, insn->offset);
+			hash_add(file->insn_hash, &insn->hash, sec_offset_hash(sec, insn->offset));
 			list_add_tail(&insn->list, &file->insn_list);
 			nr_insns++;
 		}



^ permalink raw reply	[flat|nested] 89+ messages in thread

* [PATCH v3 22/26] objtool: Detect loading function pointers across noinstr
  2020-03-24 15:31 [PATCH v3 00/26] objtool: vmlinux.o and noinstr validation Peter Zijlstra
                   ` (20 preceding siblings ...)
  2020-03-24 15:31 ` [PATCH v3 21/26] objtool: Use sec_offset_hash() for insn_hash Peter Zijlstra
@ 2020-03-24 15:31 ` Peter Zijlstra
  2020-03-24 15:31 ` [PATCH v3 23/26] kbuild/objtool: Add objtool-vmlinux.o pass Peter Zijlstra
                   ` (3 subsequent siblings)
  25 siblings, 0 replies; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 15:31 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, peterz, mhiramat, mbenes, brgerst

Detect if noinstr text loads functions pointers from regular text,
doing so is a definite sign that indirect function calls are unsafe.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
---
 tools/objtool/arch.h  |    2 +
 tools/objtool/check.c |   71 ++++++++++++++++++++++++++++++++++++++++++++++++++
 tools/objtool/check.h |    2 -
 3 files changed, 74 insertions(+), 1 deletion(-)

--- a/tools/objtool/arch.h
+++ b/tools/objtool/arch.h
@@ -75,4 +75,6 @@ int arch_decode_instruction(struct elf *
 
 bool arch_callee_saved_reg(unsigned char reg);
 
+#define MAX_INSN_SIZE 15
+
 #endif /* _ARCH_H */
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -42,6 +42,25 @@ struct instruction *find_insn(struct obj
 	return NULL;
 }
 
+static struct instruction *find_insn_containing(struct objtool_file *file, struct section *sec,
+					 unsigned long offset)
+{
+	struct instruction *insn;
+	unsigned long o;
+
+	for_offset_range(o, offset - MAX_INSN_SIZE - 1, offset) {
+		hash_for_each_possible(file->insn_hash, insn, hash, sec_offset_hash(sec, o)) {
+			if (insn->sec != sec)
+				continue;
+
+			if (insn->offset <= offset && insn->offset + insn->len > offset)
+				return insn;
+		}
+	}
+
+	return NULL;
+}
+
 static struct instruction *next_insn_same_sec(struct objtool_file *file,
 					      struct instruction *insn)
 {
@@ -2101,6 +2120,32 @@ static int validate_return(struct symbol
 	return 0;
 }
 
+static int validate_rela(struct instruction *insn, struct insn_state *state)
+{
+	/*
+	 * Assume that any text rela that's not a CALL or JMP is a load of a
+	 * function pointer.
+	 */
+
+	switch (insn->type) {
+	case INSN_CALL:
+	case INSN_CALL_DYNAMIC:
+	case INSN_JUMP_CONDITIONAL:
+	case INSN_JUMP_UNCONDITIONAL:
+		return 0;
+
+	default:
+		break;
+	}
+
+	if (state->noinstr && state->instr <= 0 && insn->has_text_rela) {
+		WARN_FUNC("loading non-noinstr function pointer", insn->sec, insn->offset);
+		return 1;
+	}
+
+	return 0;
+}
+
 /*
  * Follow the branch starting at the given instruction, and recursively follow
  * any other branches (jumps).  Meanwhile, track the frame pointer state at
@@ -2216,6 +2261,10 @@ static int validate_branch(struct objtoo
 				return 0;
 		}
 
+		ret = validate_rela(insn, &state);
+		if (ret)
+			return ret;
+
 		switch (insn->type) {
 
 		case INSN_RETURN:
@@ -2484,6 +2533,25 @@ static bool ignore_unreachable_insn(stru
 	return false;
 }
 
+static void prepare_insn_rela(struct objtool_file *file, struct section *sec)
+{
+	struct instruction *insn;
+	struct rela *rela;
+
+	if (!sec->rela)
+		return;
+
+	list_for_each_entry(rela, &sec->rela->rela_list, list) {
+		insn = find_insn_containing(file, sec, rela->offset);
+		if (!insn)
+			continue;
+
+		insn->has_text_rela = rela->sym && rela->sym->sec &&
+				      rela->sym->sec->text &&
+				      !rela->sym->sec->noinstr;
+	}
+}
+
 static int validate_section(struct objtool_file *file, struct section *sec)
 {
 	struct symbol *func;
@@ -2506,6 +2574,9 @@ static int validate_section(struct objto
 	if (vmlinux)
 		state.noinstr = sec->noinstr;
 
+	if (state.noinstr)
+		prepare_insn_rela(file, sec);
+
 	list_for_each_entry(func, &sec->symbol_list, list) {
 		if (func->type != STT_FUNC)
 			continue;
--- a/tools/objtool/check.h
+++ b/tools/objtool/check.h
@@ -36,7 +36,7 @@ struct instruction {
 	enum insn_type type;
 	unsigned long immediate;
 	bool alt_group, dead_end, ignore, hint, save, restore, ignore_alts;
-	bool retpoline_safe;
+	bool retpoline_safe, has_text_rela;
 	s8 instr;
 	u8 visited;
 	struct symbol *call_dest;



^ permalink raw reply	[flat|nested] 89+ messages in thread

* [PATCH v3 23/26] kbuild/objtool: Add objtool-vmlinux.o pass
  2020-03-24 15:31 [PATCH v3 00/26] objtool: vmlinux.o and noinstr validation Peter Zijlstra
                   ` (21 preceding siblings ...)
  2020-03-24 15:31 ` [PATCH v3 22/26] objtool: Detect loading function pointers across noinstr Peter Zijlstra
@ 2020-03-24 15:31 ` Peter Zijlstra
  2020-03-24 22:03   ` Josh Poimboeuf
  2020-03-24 15:31 ` [PATCH v3 24/26] objtool: Avoid iterating !text section symbols Peter Zijlstra
                   ` (2 subsequent siblings)
  25 siblings, 1 reply; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 15:31 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, peterz, mhiramat, mbenes, brgerst

Now that objtool is capable of processing vmlinux.o and actually has
something useful to do there, (conditionally) add it to the final link
pass.

This will increase build time by a few seconds.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
 lib/Kconfig.debug       |    5 +++++
 scripts/link-vmlinux.sh |   24 ++++++++++++++++++++++++
 2 files changed, 29 insertions(+)

--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -379,6 +379,11 @@ config STACK_VALIDATION
 	  For more information, see
 	  tools/objtool/Documentation/stack-validation.txt.
 
+config VMLINUX_VALIDATION
+	bool
+	depends on STACK_VALIDATION && DEBUG_ENTRY && !PARAVIRT
+	default y
+
 config DEBUG_FORCE_WEAK_PER_CPU
 	bool "Force weak per-cpu definitions"
 	depends on DEBUG_KERNEL
--- a/scripts/link-vmlinux.sh
+++ b/scripts/link-vmlinux.sh
@@ -55,6 +55,29 @@ modpost_link()
 	${LD} ${KBUILD_LDFLAGS} -r -o ${1} ${objects}
 }
 
+objtool_link()
+{
+	local objtoolopt;
+
+	if [ -n "${CONFIG_VMLINUX_VALIDATION}" ]; then
+		objtoolopt="check"
+		if [ -z "${CONFIG_FRAME_POINTER}" ]; then
+			objtoolopt="${objtoolopt} --no-fp"
+		fi
+		if [ -n "${CONFIG_GCOV_KERNEL}" ]; then
+			objtoolopt="${objtoolopt} --no-unreachable"
+		fi
+		if [ -n "${CONFIG_RETPOLINE}" ]; then
+			objtoolopt="${objtoolopt} --retpoline"
+		fi
+		if [ -n "${CONFIG_X86_SMAP}" ]; then
+			objtoolopt="${objtoolopt} --uaccess"
+		fi
+		info OBJTOOL ${1}
+		tools/objtool/objtool ${objtoolopt} ${1}
+	fi
+}
+
 # Link of vmlinux
 # ${1} - output file
 # ${2}, ${3}, ... - optional extra .o files
@@ -244,6 +267,7 @@ ${MAKE} -f "${srctree}/scripts/Makefile.
 #link vmlinux.o
 info LD vmlinux.o
 modpost_link vmlinux.o
+objtool_link vmlinux.o
 
 # modpost vmlinux.o to check for section mismatches
 ${MAKE} -f "${srctree}/scripts/Makefile.modpost" MODPOST_VMLINUX=1



^ permalink raw reply	[flat|nested] 89+ messages in thread

* [PATCH v3 24/26] objtool: Avoid iterating !text section symbols
  2020-03-24 15:31 [PATCH v3 00/26] objtool: vmlinux.o and noinstr validation Peter Zijlstra
                   ` (22 preceding siblings ...)
  2020-03-24 15:31 ` [PATCH v3 23/26] kbuild/objtool: Add objtool-vmlinux.o pass Peter Zijlstra
@ 2020-03-24 15:31 ` Peter Zijlstra
  2020-03-24 22:09   ` Josh Poimboeuf
  2020-03-24 15:31 ` [PATCH v3 25/26] objtool: Rearrange validate_section() Peter Zijlstra
  2020-03-24 15:31 ` [PATCH v3 26/26] objtool: Add STT_NOTYPE noinstr validation Peter Zijlstra
  25 siblings, 1 reply; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 15:31 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, peterz, mhiramat, mbenes, brgerst

validate_functions() iterates all sections their symbols; this is
pointless to do for !text sections as they won't have instructions
anyway.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
 tools/objtool/check.c |    6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -2621,8 +2621,12 @@ static int validate_functions(struct obj
 	struct section *sec;
 	int warnings = 0;
 
-	for_each_sec(file, sec)
+	for_each_sec(file, sec) {
+		if (!(sec->sh.sh_flags & SHF_EXECINSTR))
+			continue;
+
 		warnings += validate_section(file, sec);
+	}
 
 	return warnings;
 }



^ permalink raw reply	[flat|nested] 89+ messages in thread

* [PATCH v3 25/26] objtool: Rearrange validate_section()
  2020-03-24 15:31 [PATCH v3 00/26] objtool: vmlinux.o and noinstr validation Peter Zijlstra
                   ` (23 preceding siblings ...)
  2020-03-24 15:31 ` [PATCH v3 24/26] objtool: Avoid iterating !text section symbols Peter Zijlstra
@ 2020-03-24 15:31 ` Peter Zijlstra
  2020-03-24 22:10   ` Josh Poimboeuf
  2020-03-24 15:31 ` [PATCH v3 26/26] objtool: Add STT_NOTYPE noinstr validation Peter Zijlstra
  25 siblings, 1 reply; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 15:31 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, peterz, mhiramat, mbenes, brgerst

In preparation of further changes, once again break out the loop body.
No functional changes intended.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
 tools/objtool/check.c |   51 ++++++++++++++++++++++++++++----------------------
 1 file changed, 29 insertions(+), 22 deletions(-)

--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -2552,12 +2552,37 @@ static void prepare_insn_rela(struct obj
 	}
 }
 
-static int validate_section(struct objtool_file *file, struct section *sec)
+static int validate_symbol(struct objtool_file *file, struct section *sec,
+			   struct symbol *sym, struct insn_state *state)
 {
-	struct symbol *func;
 	struct instruction *insn;
+	int ret;
+
+	if (!sym->len) {
+		WARN("%s() is missing an ELF size annotation", sym->name);
+		return 1;
+	}
+
+	if (sym->pfunc != sym || sym->alias != sym)
+		return 0;
+
+	insn = find_insn(file, sec, sym->offset);
+	if (!insn || insn->ignore || insn->visited)
+		return 0;
+
+	state->uaccess = sym->uaccess_safe;
+
+	ret = validate_branch(file, insn->func, insn, *state);
+	if (ret && backtrace)
+		BT_FUNC("<=== (sym)", insn);
+	return ret;
+}
+
+static int validate_section(struct objtool_file *file, struct section *sec)
+{
 	struct insn_state state;
-	int ret, warnings = 0;
+	struct symbol *func;
+	int warnings = 0;
 
 	clear_insn_state(&state);
 
@@ -2581,25 +2606,7 @@ static int validate_section(struct objto
 		if (func->type != STT_FUNC)
 			continue;
 
-		if (!func->len) {
-			WARN("%s() is missing an ELF size annotation",
-					func->name);
-			warnings++;
-		}
-
-		if (func->pfunc != func || func->alias != func)
-			continue;
-
-		insn = find_insn(file, sec, func->offset);
-		if (!insn || insn->ignore || insn->visited)
-			continue;
-
-		state.uaccess = func->uaccess_safe;
-
-		ret = validate_branch(file, func, insn, state);
-		if (ret && backtrace)
-			BT_FUNC("<=== (func)", insn);
-		warnings += ret;
+		warnings += validate_symbol(file, sec, func, &state);
 	}
 
 	return warnings;



^ permalink raw reply	[flat|nested] 89+ messages in thread

* [PATCH v3 26/26] objtool: Add STT_NOTYPE noinstr validation
  2020-03-24 15:31 [PATCH v3 00/26] objtool: vmlinux.o and noinstr validation Peter Zijlstra
                   ` (24 preceding siblings ...)
  2020-03-24 15:31 ` [PATCH v3 25/26] objtool: Rearrange validate_section() Peter Zijlstra
@ 2020-03-24 15:31 ` Peter Zijlstra
  2020-03-24 22:16   ` Josh Poimboeuf
  25 siblings, 1 reply; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 15:31 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, peterz, mhiramat, mbenes, brgerst

Make sure to also check STT_NOTYPE symbols for noinstr violations.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
 tools/objtool/check.c |   19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)

--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -2563,7 +2563,7 @@ static int validate_symbol(struct objtoo
 		return 1;
 	}
 
-	if (sym->pfunc != sym || sym->alias != sym)
+	if ((sym->type == STT_FUNC && sym->pfunc != sym) || sym->alias != sym)
 		return 0;
 
 	insn = find_insn(file, sec, sym->offset);
@@ -2610,6 +2610,23 @@ static int validate_section(struct objto
 		warnings += validate_symbol(file, sec, func, &state);
 	}
 
+	if (state.noinstr) {
+		/*
+		 * In vmlinux mode we will not run validate_unwind_hints() by
+		 * default which means we'll not otherwise visit STT_NOTYPE
+		 * symbols.
+		 *
+		 * In case of --duplicate mode, insn->visited will avoid actual
+		 * duplicate work being done.
+		 */
+		list_for_each_entry(func, &sec->symbol_list, list) {
+			if (func->type != STT_NOTYPE)
+				continue;
+
+			warnings += validate_symbol(file, sec, func, &state);
+		}
+	}
+
 	return warnings;
 }
 



^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 05/26] x86/kexec: Make relocate_kernel_64.S objtool clean
  2020-03-24 15:31 ` [PATCH v3 05/26] x86/kexec: Make relocate_kernel_64.S objtool clean Peter Zijlstra
@ 2020-03-24 20:55   ` Josh Poimboeuf
  2020-03-25  9:56   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  2 siblings, 0 replies; 89+ messages in thread
From: Josh Poimboeuf @ 2020-03-24 20:55 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: tglx, linux-kernel, x86, mhiramat, mbenes, brgerst

On Tue, Mar 24, 2020 at 04:31:18PM +0100, Peter Zijlstra wrote:
> Having fixed the biggest objtool issue in this file; fix up the rest
> and remove the exception.
> 
> Suggested-by: Josh Poimboeuf <jpoimboe@redhat.com>
> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>

Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>

-- 
Josh


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 17/26] objtool: Re-arrange validate_functions()
  2020-03-24 15:31 ` [PATCH v3 17/26] objtool: Re-arrange validate_functions() Peter Zijlstra
@ 2020-03-24 21:10   ` Josh Poimboeuf
  2020-03-24 21:15     ` Peter Zijlstra
  2020-03-25 12:22   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  2 siblings, 1 reply; 89+ messages in thread
From: Josh Poimboeuf @ 2020-03-24 21:10 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: tglx, linux-kernel, x86, mhiramat, mbenes, brgerst

On Tue, Mar 24, 2020 at 04:31:30PM +0100, Peter Zijlstra wrote:
> In preparation to adding a vmlinux.o specific pass, rearrange some
> code. No functional changes intended.
> 
> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> ---
>  tools/objtool/check.c |   60 ++++++++++++++++++++++++++++----------------------
>  1 file changed, 34 insertions(+), 26 deletions(-)
> 
> --- a/tools/objtool/check.c
> +++ b/tools/objtool/check.c
> @@ -2413,9 +2413,8 @@ static bool ignore_unreachable_insn(stru
>  	return false;
>  }
>  
> -static int validate_functions(struct objtool_file *file)
> +static int validate_section(struct objtool_file *file, struct section *sec)
>  {
> -	struct section *sec;
>  	struct symbol *func;
>  	struct instruction *insn;
>  	struct insn_state state;
> @@ -2428,35 +2427,44 @@ static int validate_functions(struct obj
>  	       CFI_NUM_REGS * sizeof(struct cfi_reg));
>  	state.stack_size = initial_func_cfi.cfa.offset;
>  
> -	for_each_sec(file, sec) {
> -		list_for_each_entry(func, &sec->symbol_list, list) {
> -			if (func->type != STT_FUNC)
> -				continue;
> -
> -			if (!func->len) {
> -				WARN("%s() is missing an ELF size annotation",
> -				     func->name);
> -				warnings++;
> -			}
> -
> -			if (func->pfunc != func || func->alias != func)
> -				continue;
> -
> -			insn = find_insn(file, sec, func->offset);
> -			if (!insn || insn->ignore || insn->visited)
> -				continue;
> -
> -			state.uaccess = func->uaccess_safe;
> -
> -			ret = validate_branch(file, func, insn, state);
> -			if (ret && backtrace)
> -				BT_FUNC("<=== (func)", insn);
> -			warnings += ret;
> +	list_for_each_entry(func, &sec->symbol_list, list) {
> +		if (func->type != STT_FUNC)
> +			continue;
> +
> +		if (!func->len) {
> +			WARN("%s() is missing an ELF size annotation",
> +					func->name);

wonky indentation

Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>

-- 
Josh


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 17/26] objtool: Re-arrange validate_functions()
  2020-03-24 21:10   ` Josh Poimboeuf
@ 2020-03-24 21:15     ` Peter Zijlstra
  0 siblings, 0 replies; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 21:15 UTC (permalink / raw)
  To: Josh Poimboeuf; +Cc: tglx, linux-kernel, x86, mhiramat, mbenes, brgerst

On Tue, Mar 24, 2020 at 04:10:06PM -0500, Josh Poimboeuf wrote:
> On Tue, Mar 24, 2020 at 04:31:30PM +0100, Peter Zijlstra wrote:
> > +		if (!func->len) {
> > +			WARN("%s() is missing an ELF size annotation",
> > +					func->name);
> 
> wonky indentation

Argh, clearly I need to update my .vimrc on that machine. That's the
default C indenting per Vim and I'm pretty sure I reflowed that function
with =%.



^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 18/26] objtool: Fix !CFI insn_state propagation
  2020-03-24 15:31 ` [PATCH v3 18/26] objtool: Fix !CFI insn_state propagation Peter Zijlstra
@ 2020-03-24 21:40   ` Josh Poimboeuf
  2020-03-24 22:11     ` Peter Zijlstra
  2020-03-25 14:41   ` [PATCH v3.1 18a/26] objtool: Remove CFI save/restore special case Peter Zijlstra
                     ` (3 subsequent siblings)
  4 siblings, 1 reply; 89+ messages in thread
From: Josh Poimboeuf @ 2020-03-24 21:40 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: tglx, linux-kernel, x86, mhiramat, mbenes, brgerst

On Tue, Mar 24, 2020 at 04:31:31PM +0100, Peter Zijlstra wrote:
> Objtool keeps per instruction CFI state in struct insn_state and will
> save/restore this where required. However, insn_state has grown some
> !CFI state, and this must not be saved/restored (and thus lost).
> 
> Fix this by explicitly preserving the !CFI state and clarify by
> restucturing the code and adding a comment.
> 
> XXX, the insn==first condition is not handled right.
> 
> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> ---
>  tools/objtool/check.c |   95 +++++++++++++++++++++++++++++---------------------
>  tools/objtool/check.h |    8 ++++
>  2 files changed, 64 insertions(+), 39 deletions(-)
> 
> --- a/tools/objtool/check.c
> +++ b/tools/objtool/check.c
> @@ -2033,6 +2033,59 @@ static int validate_return(struct symbol
>  	return 0;
>  }
>  
> +static int apply_insn_hint(struct objtool_file *file, struct section *sec,
> +			   struct symbol *func, struct instruction *first,
> +			   struct instruction *insn, struct insn_state *state)
> +{
> +	struct insn_state old = *state;
> +
> +	if (insn->restore) {
> +		struct instruction *save_insn, *i;
> +
> +		i = insn;
> +		save_insn = NULL;
> +		sym_for_each_insn_continue_reverse(file, func, i) {
> +			if (i->save) {
> +				save_insn = i;
> +				break;
> +			}
> +		}
> +
> +		if (!save_insn) {
> +			WARN_FUNC("no corresponding CFI save for CFI restore",
> +					sec, insn->offset);
> +			return 1;
> +		}
> +
> +		if (!save_insn->visited) {
> +			/*
> +			 * Oops, no state to copy yet.
> +			 * Hopefully we can reach this
> +			 * instruction from another branch
> +			 * after the save insn has been
> +			 * visited.
> +			 */
> +			if (insn == first)
> +				return 0; // XXX

Yeah, moving this code out to apply_insn_hint() seems like a nice idea,
but it wouldn't be worth it if it breaks this case.  TBH I don't
remember if this check was for a real-world case.  Might be worth
looking at...  If this case doesn't exist in reality then we could just
remove this check altogether.

> +
> +			WARN_FUNC("objtool isn't smart enough to handle this CFI save/restore combo",
> +					sec, insn->offset);
> +			return 1;
> +		}
> +
> +		insn->state = save_insn->state;
> +	}
> +
> +	*state = insn->state;

This would have been easier to review if apply_insn_hint() were added in
a separate patch.

> +
> +	/* restore !CFI state */
> +	state->df = old.df;
> +	state->uaccess = old.uaccess;
> +	state->uaccess_stack = old.uaccess_stack;

Maybe we should just move the CFI stuff into a state->cfi substruct.
That would remove the need for these bits and probably also the comment
above the insn_state declaration.

-- 
Josh


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 19/26] objtool: Implement noinstr validation
  2020-03-24 15:31 ` [PATCH v3 19/26] objtool: Implement noinstr validation Peter Zijlstra
@ 2020-03-24 21:41   ` Josh Poimboeuf
  2020-03-25 14:44   ` [PATCH v3.1 " Peter Zijlstra
  1 sibling, 0 replies; 89+ messages in thread
From: Josh Poimboeuf @ 2020-03-24 21:41 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: tglx, linux-kernel, x86, mhiramat, mbenes, brgerst

On Tue, Mar 24, 2020 at 04:31:32PM +0100, Peter Zijlstra wrote:
> Validate that any call out of .noinstr.text is in between
> instr_begin() and instr_end() annotations.
> 
> This annotation is useful to ensure correct behaviour wrt tracing
> sensitive code like entry/exit and idle code. When we run code in a
> sensitive context we want a guarantee no unknown code is ran.
> 
> Since this validation relies on knowing the section of call
> destination symbols, we must run it on vmlinux.o instead of on
> individual object files.
> 
> Add two options:
> 
>  -d/--duplicate "duplicate validation for vmlinux"
>  -l/--vmlinux "vmlinux.o validation"
> 
> Where the latter auto-detects when objname ends with "vmlinux.o" and
> the former will force all validations, also those already done on
> !vmlinux object files.
> 
> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>

Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>

-- 
Josh


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 23/26] kbuild/objtool: Add objtool-vmlinux.o pass
  2020-03-24 15:31 ` [PATCH v3 23/26] kbuild/objtool: Add objtool-vmlinux.o pass Peter Zijlstra
@ 2020-03-24 22:03   ` Josh Poimboeuf
  2020-03-24 22:05     ` Peter Zijlstra
  0 siblings, 1 reply; 89+ messages in thread
From: Josh Poimboeuf @ 2020-03-24 22:03 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: tglx, linux-kernel, x86, mhiramat, mbenes, brgerst

On Tue, Mar 24, 2020 at 04:31:36PM +0100, Peter Zijlstra wrote:
> Now that objtool is capable of processing vmlinux.o and actually has
> something useful to do there, (conditionally) add it to the final link
> pass.
> 
> This will increase build time by a few seconds.
> 
> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> ---
>  lib/Kconfig.debug       |    5 +++++
>  scripts/link-vmlinux.sh |   24 ++++++++++++++++++++++++
>  2 files changed, 29 insertions(+)
> 
> --- a/lib/Kconfig.debug
> +++ b/lib/Kconfig.debug
> @@ -379,6 +379,11 @@ config STACK_VALIDATION
>  	  For more information, see
>  	  tools/objtool/Documentation/stack-validation.txt.
>  
> +config VMLINUX_VALIDATION
> +	bool
> +	depends on STACK_VALIDATION && DEBUG_ENTRY && !PARAVIRT
> +	default y
> +

So I'm assuming this is incompatible with PARAVIRT because of all the
indirect pvops calls?

I'm thinking it should be easy to detect those and whitelist them
because they always have a pv_ops relocation associated with the call
instruction.

-- 
Josh


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 23/26] kbuild/objtool: Add objtool-vmlinux.o pass
  2020-03-24 22:03   ` Josh Poimboeuf
@ 2020-03-24 22:05     ` Peter Zijlstra
  0 siblings, 0 replies; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 22:05 UTC (permalink / raw)
  To: Josh Poimboeuf; +Cc: tglx, linux-kernel, x86, mhiramat, mbenes, brgerst

On Tue, Mar 24, 2020 at 05:03:21PM -0500, Josh Poimboeuf wrote:

> > +config VMLINUX_VALIDATION
> > +	bool
> > +	depends on STACK_VALIDATION && DEBUG_ENTRY && !PARAVIRT
> > +	default y
> > +
> 
> So I'm assuming this is incompatible with PARAVIRT because of all the
> indirect pvops calls?

Yep.

> I'm thinking it should be easy to detect those and whitelist them
> because they always have a pv_ops relocation associated with the call
> instruction.

I did consider that, but have been too lazy to actually make that work.
Also, the series is large enough as it is.. :-)

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 24/26] objtool: Avoid iterating !text section symbols
  2020-03-24 15:31 ` [PATCH v3 24/26] objtool: Avoid iterating !text section symbols Peter Zijlstra
@ 2020-03-24 22:09   ` Josh Poimboeuf
  0 siblings, 0 replies; 89+ messages in thread
From: Josh Poimboeuf @ 2020-03-24 22:09 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: tglx, linux-kernel, x86, mhiramat, mbenes, brgerst

On Tue, Mar 24, 2020 at 04:31:37PM +0100, Peter Zijlstra wrote:
> validate_functions() iterates all sections their symbols; this is
> pointless to do for !text sections as they won't have instructions
> anyway.
> 
> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>

Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>

-- 
Josh


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 25/26] objtool: Rearrange validate_section()
  2020-03-24 15:31 ` [PATCH v3 25/26] objtool: Rearrange validate_section() Peter Zijlstra
@ 2020-03-24 22:10   ` Josh Poimboeuf
  0 siblings, 0 replies; 89+ messages in thread
From: Josh Poimboeuf @ 2020-03-24 22:10 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: tglx, linux-kernel, x86, mhiramat, mbenes, brgerst

On Tue, Mar 24, 2020 at 04:31:38PM +0100, Peter Zijlstra wrote:
> In preparation of further changes, once again break out the loop body.
> No functional changes intended.
> 
> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>

Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>

-- 
Josh


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 18/26] objtool: Fix !CFI insn_state propagation
  2020-03-24 21:40   ` Josh Poimboeuf
@ 2020-03-24 22:11     ` Peter Zijlstra
  2020-03-24 23:00       ` Peter Zijlstra
  0 siblings, 1 reply; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 22:11 UTC (permalink / raw)
  To: Josh Poimboeuf; +Cc: tglx, linux-kernel, x86, mhiramat, mbenes, brgerst

On Tue, Mar 24, 2020 at 04:40:06PM -0500, Josh Poimboeuf wrote:
> On Tue, Mar 24, 2020 at 04:31:31PM +0100, Peter Zijlstra wrote:

> > +		if (!save_insn->visited) {
> > +			/*
> > +			 * Oops, no state to copy yet.
> > +			 * Hopefully we can reach this
> > +			 * instruction from another branch
> > +			 * after the save insn has been
> > +			 * visited.
> > +			 */
> > +			if (insn == first)
> > +				return 0; // XXX
> 
> Yeah, moving this code out to apply_insn_hint() seems like a nice idea,
> but it wouldn't be worth it if it breaks this case.  TBH I don't
> remember if this check was for a real-world case.  Might be worth
> looking at...  If this case doesn't exist in reality then we could just
> remove this check altogether.

I'll go run a bunch of builds with a print on it, that should tell us I
suppose.

> > +
> > +			WARN_FUNC("objtool isn't smart enough to handle this CFI save/restore combo",
> > +					sec, insn->offset);
> > +			return 1;
> > +		}
> > +
> > +		insn->state = save_insn->state;
> > +	}
> > +
> > +	*state = insn->state;
> 
> This would have been easier to review if apply_insn_hint() were added in
> a separate patch.

27 it will be!

> > +
> > +	/* restore !CFI state */
> > +	state->df = old.df;
> > +	state->uaccess = old.uaccess;
> > +	state->uaccess_stack = old.uaccess_stack;
> 
> Maybe we should just move the CFI stuff into a state->cfi substruct.
> That would remove the need for these bits and probably also the comment
> above the insn_state declaration.

Indeed!

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 26/26] objtool: Add STT_NOTYPE noinstr validation
  2020-03-24 15:31 ` [PATCH v3 26/26] objtool: Add STT_NOTYPE noinstr validation Peter Zijlstra
@ 2020-03-24 22:16   ` Josh Poimboeuf
  2020-03-24 22:34     ` Peter Zijlstra
  0 siblings, 1 reply; 89+ messages in thread
From: Josh Poimboeuf @ 2020-03-24 22:16 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: tglx, linux-kernel, x86, mhiramat, mbenes, brgerst

On Tue, Mar 24, 2020 at 04:31:39PM +0100, Peter Zijlstra wrote:
> Make sure to also check STT_NOTYPE symbols for noinstr violations.
> 
> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> ---
>  tools/objtool/check.c |   19 ++++++++++++++++++-
>  1 file changed, 18 insertions(+), 1 deletion(-)
> 
> --- a/tools/objtool/check.c
> +++ b/tools/objtool/check.c
> @@ -2563,7 +2563,7 @@ static int validate_symbol(struct objtoo
>  		return 1;
>  	}
>  
> -	if (sym->pfunc != sym || sym->alias != sym)
> +	if ((sym->type == STT_FUNC && sym->pfunc != sym) || sym->alias != sym)
>  		return 0;
>  
>  	insn = find_insn(file, sec, sym->offset);
> @@ -2610,6 +2610,23 @@ static int validate_section(struct objto
>  		warnings += validate_symbol(file, sec, func, &state);
>  	}
>  
> +	if (state.noinstr) {
> +		/*
> +		 * In vmlinux mode we will not run validate_unwind_hints() by
> +		 * default which means we'll not otherwise visit STT_NOTYPE
> +		 * symbols.
> +		 *
> +		 * In case of --duplicate mode, insn->visited will avoid actual
> +		 * duplicate work being done.
> +		 */
> +		list_for_each_entry(func, &sec->symbol_list, list) {
> +			if (func->type != STT_NOTYPE)
> +				continue;
> +
> +			warnings += validate_symbol(file, sec, func, &state);
> +		}
> +	}
> +

I guess this is ok, but is there a valid reason why we don't just call
validate_unwind_hints()?

It's also slightly concerning that validate_reachable_instructions()
isn't called, I'm not 100% convinced all the code will get checked.

-- 
Josh


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 26/26] objtool: Add STT_NOTYPE noinstr validation
  2020-03-24 22:16   ` Josh Poimboeuf
@ 2020-03-24 22:34     ` Peter Zijlstra
  2020-03-25 14:42       ` Josh Poimboeuf
  0 siblings, 1 reply; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 22:34 UTC (permalink / raw)
  To: Josh Poimboeuf; +Cc: tglx, linux-kernel, x86, mhiramat, mbenes, brgerst

On Tue, Mar 24, 2020 at 05:16:16PM -0500, Josh Poimboeuf wrote:
> On Tue, Mar 24, 2020 at 04:31:39PM +0100, Peter Zijlstra wrote:

> > +	if (state.noinstr) {
> > +		/*
> > +		 * In vmlinux mode we will not run validate_unwind_hints() by
> > +		 * default which means we'll not otherwise visit STT_NOTYPE
> > +		 * symbols.
> > +		 *
> > +		 * In case of --duplicate mode, insn->visited will avoid actual
> > +		 * duplicate work being done.
> > +		 */
> > +		list_for_each_entry(func, &sec->symbol_list, list) {
> > +			if (func->type != STT_NOTYPE)
> > +				continue;
> > +
> > +			warnings += validate_symbol(file, sec, func, &state);
> > +		}
> > +	}
> > +
> 
> I guess this is ok, but is there a valid reason why we don't just call
> validate_unwind_hints()?
> 
> It's also slightly concerning that validate_reachable_instructions()
> isn't called, I'm not 100% convinced all the code will get checked.

This will only end up running on .noinstr.text, while
validate_unwind_hints() will run on *everything*. That is, we're
purposely not checking everything.

It very much relies on the !vmlinux mode to do the unreachable things.

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 18/26] objtool: Fix !CFI insn_state propagation
  2020-03-24 22:11     ` Peter Zijlstra
@ 2020-03-24 23:00       ` Peter Zijlstra
  2020-03-25 14:39         ` Josh Poimboeuf
  0 siblings, 1 reply; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-24 23:00 UTC (permalink / raw)
  To: Josh Poimboeuf; +Cc: tglx, linux-kernel, x86, mhiramat, mbenes, brgerst

On Tue, Mar 24, 2020 at 11:11:09PM +0100, Peter Zijlstra wrote:
> On Tue, Mar 24, 2020 at 04:40:06PM -0500, Josh Poimboeuf wrote:
> > On Tue, Mar 24, 2020 at 04:31:31PM +0100, Peter Zijlstra wrote:
> 
> > > +		if (!save_insn->visited) {
> > > +			/*
> > > +			 * Oops, no state to copy yet.
> > > +			 * Hopefully we can reach this
> > > +			 * instruction from another branch
> > > +			 * after the save insn has been
> > > +			 * visited.
> > > +			 */
> > > +			if (insn == first)
> > > +				return 0; // XXX
> > 
> > Yeah, moving this code out to apply_insn_hint() seems like a nice idea,
> > but it wouldn't be worth it if it breaks this case.  TBH I don't
> > remember if this check was for a real-world case.  Might be worth
> > looking at...  If this case doesn't exist in reality then we could just
> > remove this check altogether.
> 
> I'll go run a bunch of builds with a print on it, that should tell us I
> suppose.

I can a bunch of builds, including an allmodconfig with the below on top
and it 'works'.

So I suppose we can remove this special case.

---
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -2134,11 +2134,13 @@ static int apply_insn_hint(struct objtoo
 			 * after the save insn has been
 			 * visited.
 			 */
-			if (insn == first)
-				return 0; // XXX

 			WARN_FUNC("objtool isn't smart enough to handle this CFI save/restore combo",
 					sec, insn->offset);
+
+			if (insn == first)
+				return -1;
+
 			return 1;
 		}



^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 01/26] objtool: Introduce validate_return()
  2020-03-24 15:31 ` [PATCH v3 01/26] objtool: Introduce validate_return() Peter Zijlstra
@ 2020-03-25  8:39   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  1 sibling, 0 replies; 89+ messages in thread
From: Miroslav Benes @ 2020-03-25  8:39 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: tglx, jpoimboe, linux-kernel, x86, mhiramat, brgerst

On Tue, 24 Mar 2020, Peter Zijlstra wrote:

> Trivial 'cleanup' to save one indentation level and match
> validate_call().
> 
> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>

Reviewed-by: Miroslav Benes <mbenes@suse.cz>

M

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 02/26] objtool: Rename func_for_each_insn()
  2020-03-24 15:31 ` [PATCH v3 02/26] objtool: Rename func_for_each_insn() Peter Zijlstra
@ 2020-03-25  8:43   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  1 sibling, 0 replies; 89+ messages in thread
From: Miroslav Benes @ 2020-03-25  8:43 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: tglx, jpoimboe, linux-kernel, x86, mhiramat, brgerst

On Tue, 24 Mar 2020, Peter Zijlstra wrote:

> There is func_for_each_insn() and func_for_each_insn_all(), the both
> iterate the instructions, but the first uses symbol offset/length
> while the second uses insn->func.
> 
> Rename func_for_each_insn() to sym_for_eac_insn() because it iterates

I did not notice before, but there is a typo

s/sym_for_eac_insn/sym_for_each_insn/

> on symbol information.
> 
> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>

Reviewed-by: Miroslav Benes <mbenes@suse.cz>

M

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 03/26] objtool: Rename func_for_each_insn_all()
  2020-03-24 15:31 ` [PATCH v3 03/26] objtool: Rename func_for_each_insn_all() Peter Zijlstra
@ 2020-03-25  8:44   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  1 sibling, 0 replies; 89+ messages in thread
From: Miroslav Benes @ 2020-03-25  8:44 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: tglx, jpoimboe, linux-kernel, x86, mhiramat, brgerst

On Tue, 24 Mar 2020, Peter Zijlstra wrote:

> Now that func_for_each_insn() is available, rename
> func_for_each_insn_all(). This gets us:
> 
>   sym_for_each_insn()  - iterate on symbol offset/len
>   func_for_each_insn() - iterate on insn->func
> 
> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>

Reviewed-by: Miroslav Benes <mbenes@suse.cz>

M

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 04/26] x86/kexec: Use RIP relative addressing
  2020-03-24 15:31 ` [PATCH v3 04/26] x86/kexec: Use RIP relative addressing Peter Zijlstra
@ 2020-03-25  9:34   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  1 sibling, 0 replies; 89+ messages in thread
From: Miroslav Benes @ 2020-03-25  9:34 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: tglx, jpoimboe, linux-kernel, x86, mhiramat, brgerst

On Tue, 24 Mar 2020, Peter Zijlstra wrote:

> Normally identity_mapped is not visible to objtool, due to:
> 
>   arch/x86/kernel/Makefile:OBJECT_FILES_NON_STANDARD_relocate_kernel_$(BITS).o := y
> 
> However, when we want to run objtool on vmlinux.o there is no hiding
> it:
> 
>   vmlinux.o: warning: objtool: .text+0x4c0f1: unsupported intra-function call
> 
> Replace the (i386 inspired) pattern:
> 
> 	call 1f
>   1:	popq %r8
> 	subq $(1b - relocate_kernel), %r8
> 
> With a x86_64 RIP-relative LEA:
> 
> 	leaq relocate_kernel(%rip), %r8
> 
> Suggested-by: Brian Gerst <brgerst@gmail.com>
> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>

Reviewed-by: Miroslav Benes <mbenes@suse.cz>

M

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 05/26] x86/kexec: Make relocate_kernel_64.S objtool clean
  2020-03-24 15:31 ` [PATCH v3 05/26] x86/kexec: Make relocate_kernel_64.S objtool clean Peter Zijlstra
  2020-03-24 20:55   ` Josh Poimboeuf
@ 2020-03-25  9:56   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  2 siblings, 0 replies; 89+ messages in thread
From: Miroslav Benes @ 2020-03-25  9:56 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: tglx, jpoimboe, linux-kernel, x86, mhiramat, brgerst

On Tue, 24 Mar 2020, Peter Zijlstra wrote:

> Having fixed the biggest objtool issue in this file; fix up the rest
> and remove the exception.
> 
> Suggested-by: Josh Poimboeuf <jpoimboe@redhat.com>
> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>

Reviewed-by: Miroslav Benes <mbenes@suse.cz>

M

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 06/26] objtool: Optimize find_symbol_by_index()
  2020-03-24 15:31 ` [PATCH v3 06/26] objtool: Optimize find_symbol_by_index() Peter Zijlstra
@ 2020-03-25 10:01   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  1 sibling, 0 replies; 89+ messages in thread
From: Miroslav Benes @ 2020-03-25 10:01 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: tglx, jpoimboe, linux-kernel, x86, mhiramat, brgerst

On Tue, 24 Mar 2020, Peter Zijlstra wrote:

> The symbol index is object wide, not per section, so it makes no sense
> to have the symbol_hash be part of the section object. By moving it to
> the elf object we avoid the linear sections iteration.
> 
> This reduces the runtime of objtool on vmlinux.o from over 3 hours (I
> gave up) to a few minutes. The defconfig vmlinux.o has around 20k
> sections.
> 
> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>

Reviewed-by: Miroslav Benes <mbenes@suse.cz>

M

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 07/26] objtool: Add a statistics mode
  2020-03-24 15:31 ` [PATCH v3 07/26] objtool: Add a statistics mode Peter Zijlstra
@ 2020-03-25 10:10   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  1 sibling, 0 replies; 89+ messages in thread
From: Miroslav Benes @ 2020-03-25 10:10 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: tglx, jpoimboe, linux-kernel, x86, mhiramat, brgerst

On Tue, 24 Mar 2020, Peter Zijlstra wrote:

> Have it print a few numbers which can be used to size the hashtables.
> 
> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>

Reviewed-by: Miroslav Benes <mbenes@suse.cz>

M

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 08/26] objtool: Optimize find_section_by_index()
  2020-03-24 15:31 ` [PATCH v3 08/26] objtool: Optimize find_section_by_index() Peter Zijlstra
@ 2020-03-25 10:12   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  1 sibling, 0 replies; 89+ messages in thread
From: Miroslav Benes @ 2020-03-25 10:12 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: tglx, jpoimboe, linux-kernel, x86, mhiramat, brgerst

On Tue, 24 Mar 2020, Peter Zijlstra wrote:

> In order to avoid a linear search (over 20k entries), add an
> section_hash to the elf object.
> 
> This reduces objtool on vmlinux.o from a few minutes to around 45
> seconds.
> 
> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>

Reviewed-by: Miroslav Benes <mbenes@suse.cz>

M

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 09/26] objtool: Optimize find_section_by_name()
  2020-03-24 15:31 ` [PATCH v3 09/26] objtool: Optimize find_section_by_name() Peter Zijlstra
@ 2020-03-25 10:18   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  1 sibling, 0 replies; 89+ messages in thread
From: Miroslav Benes @ 2020-03-25 10:18 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: tglx, jpoimboe, linux-kernel, x86, mhiramat, brgerst

On Tue, 24 Mar 2020, Peter Zijlstra wrote:

> In order to avoid yet another linear search of (20k) sections, add a
> name based hash.
> 
> This reduces objtool runtime on vmlinux.o by some 10s to around 35s.
> 
> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>

Reviewed-by: Miroslav Benes <mbenes@suse.cz>

M

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 10/26] objtool: Optimize find_symbol_*() and read_symbols()
  2020-03-24 15:31 ` [PATCH v3 10/26] objtool: Optimize find_symbol_*() and read_symbols() Peter Zijlstra
@ 2020-03-25 10:20   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  1 sibling, 0 replies; 89+ messages in thread
From: Miroslav Benes @ 2020-03-25 10:20 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: tglx, jpoimboe, linux-kernel, x86, mhiramat, brgerst

On Tue, 24 Mar 2020, Peter Zijlstra wrote:

> All of:
> 
>   read_symbols(), find_symbol_by_offset(), find_symbol_containing(),
>   find_containing_func()
> 
> do a linear search of the symbols. Add an RB tree to make it go
> faster.
> 
> This about halves objtool runtime on vmlinux.o, from 34s to 18s.
> 
> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>

Reviewed-by: Miroslav Benes <mbenes@suse.cz>

M

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 11/26] objtool: Rename find_containing_func()
  2020-03-24 15:31 ` [PATCH v3 11/26] objtool: Rename find_containing_func() Peter Zijlstra
@ 2020-03-25 10:21   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  1 sibling, 0 replies; 89+ messages in thread
From: Miroslav Benes @ 2020-03-25 10:21 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: tglx, jpoimboe, linux-kernel, x86, mhiramat, brgerst

On Tue, 24 Mar 2020, Peter Zijlstra wrote:

> For consistency; we have:
> 
>   find_symbol_by_offset() / find_symbol_containing()
>   find_func_by_offset()   / find_containing_func()
> 
> fix that.
> 
> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>

Reviewed-by: Miroslav Benes <mbenes@suse.cz>

M

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 12/26] objtool: Resize insn_hash
  2020-03-24 15:31 ` [PATCH v3 12/26] objtool: Resize insn_hash Peter Zijlstra
@ 2020-03-25 10:21   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  1 sibling, 0 replies; 89+ messages in thread
From: Miroslav Benes @ 2020-03-25 10:21 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: tglx, jpoimboe, linux-kernel, x86, mhiramat, brgerst

On Tue, 24 Mar 2020, Peter Zijlstra wrote:

> Perf shows we're spending a lot of time in find_insn() and the
> statistics show we have around 3.2 million instruction. Increase the
> hash table size to reduce the bucket load from around 50 to 3.
> 
> This shaves about 2s off of objtool on vmlinux.o runtime, down to 16s.
> 
> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>

Reviewed-by: Miroslav Benes <mbenes@suse.cz>

M

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 13/26] objtool: Optimize find_symbol_by_name()
  2020-03-24 15:31 ` [PATCH v3 13/26] objtool: Optimize find_symbol_by_name() Peter Zijlstra
@ 2020-03-25 10:25   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  1 sibling, 0 replies; 89+ messages in thread
From: Miroslav Benes @ 2020-03-25 10:25 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: tglx, jpoimboe, linux-kernel, x86, mhiramat, brgerst

On Tue, 24 Mar 2020, Peter Zijlstra wrote:

> Perf showed that find_symbol_by_name() takes time; add a symbol name
> hash.
> 
> This shaves another second off of objtool on vmlinux.o runtime, down
> to 15 seconds.
> 
> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>

Reviewed-by: Miroslav Benes <mbenes@suse.cz>

M

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 14/26] objtool: Optimize read_sections()
  2020-03-24 15:31 ` [PATCH v3 14/26] objtool: Optimize read_sections() Peter Zijlstra
@ 2020-03-25 12:10   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  2020-04-21 14:47   ` [PATCH v3 14/26] " youling257
  2 siblings, 0 replies; 89+ messages in thread
From: Miroslav Benes @ 2020-03-25 12:10 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: tglx, jpoimboe, linux-kernel, x86, mhiramat, brgerst

On Tue, 24 Mar 2020, Peter Zijlstra wrote:

> Perf showed that __hash_init() is a significant portion of
> read_sections(), so instead of doing a per section rela_hash, use an
> elf-wide rela_hash.
> 
> Statistics show us there are about 1.1 million relas, so size it
> accordingly.
> 
> This reduces the objtool on vmlinux.o runtime to a third, from 15 to 5
> seconds.
> 
> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>

Reviewed-by: Miroslav Benes <mbenes@suse.cz>

M

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 15/26] objtool: Delete cleanup()
  2020-03-24 15:31 ` [PATCH v3 15/26] objtool: Delete cleanup() Peter Zijlstra
@ 2020-03-25 12:11   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  1 sibling, 0 replies; 89+ messages in thread
From: Miroslav Benes @ 2020-03-25 12:11 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: tglx, jpoimboe, linux-kernel, x86, mhiramat, brgerst

On Tue, 24 Mar 2020, Peter Zijlstra wrote:

> Perf shows we spend a measurable amount of time spend cleaning up
> right before we exit anyway. Avoid the needsless work and just
> terminate.
> 
> This reduces objtool on vmlinux.o runtime from 5.4s to 4.8s
> 
> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>

Reviewed-by: Miroslav Benes <mbenes@suse.cz>

M

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 16/26] objtool: Optimize find_rela_by_dest_range()
  2020-03-24 15:31 ` [PATCH v3 16/26] objtool: Optimize find_rela_by_dest_range() Peter Zijlstra
@ 2020-03-25 12:19   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  1 sibling, 0 replies; 89+ messages in thread
From: Miroslav Benes @ 2020-03-25 12:19 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: tglx, jpoimboe, linux-kernel, x86, mhiramat, brgerst

On Tue, 24 Mar 2020, Peter Zijlstra wrote:

> Perf shows there is significant time in find_rela_by_dest(); this is
> because we have to iterate the address space per byte, looking for
> relocation entries.
> 
> Optimize this by reducing the address space granularity.
> 
> This reduces objtool on vmlinux.o runtime from 4.8 to 4.4 seconds.
> 
> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>

Reviewed-by: Miroslav Benes <mbenes@suse.cz>

M

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 17/26] objtool: Re-arrange validate_functions()
  2020-03-24 15:31 ` [PATCH v3 17/26] objtool: Re-arrange validate_functions() Peter Zijlstra
  2020-03-24 21:10   ` Josh Poimboeuf
@ 2020-03-25 12:22   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
  2 siblings, 0 replies; 89+ messages in thread
From: Miroslav Benes @ 2020-03-25 12:22 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: tglx, jpoimboe, linux-kernel, x86, mhiramat, brgerst

On Tue, 24 Mar 2020, Peter Zijlstra wrote:

> In preparation to adding a vmlinux.o specific pass, rearrange some
> code. No functional changes intended.
> 
> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>

Reviewed-by: Miroslav Benes <mbenes@suse.cz>

M

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 18/26] objtool: Fix !CFI insn_state propagation
  2020-03-24 23:00       ` Peter Zijlstra
@ 2020-03-25 14:39         ` Josh Poimboeuf
  0 siblings, 0 replies; 89+ messages in thread
From: Josh Poimboeuf @ 2020-03-25 14:39 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: tglx, linux-kernel, x86, mhiramat, mbenes, brgerst

On Wed, Mar 25, 2020 at 12:00:10AM +0100, Peter Zijlstra wrote:
> On Tue, Mar 24, 2020 at 11:11:09PM +0100, Peter Zijlstra wrote:
> > On Tue, Mar 24, 2020 at 04:40:06PM -0500, Josh Poimboeuf wrote:
> > > On Tue, Mar 24, 2020 at 04:31:31PM +0100, Peter Zijlstra wrote:
> > 
> > > > +		if (!save_insn->visited) {
> > > > +			/*
> > > > +			 * Oops, no state to copy yet.
> > > > +			 * Hopefully we can reach this
> > > > +			 * instruction from another branch
> > > > +			 * after the save insn has been
> > > > +			 * visited.
> > > > +			 */
> > > > +			if (insn == first)
> > > > +				return 0; // XXX
> > > 
> > > Yeah, moving this code out to apply_insn_hint() seems like a nice idea,
> > > but it wouldn't be worth it if it breaks this case.  TBH I don't
> > > remember if this check was for a real-world case.  Might be worth
> > > looking at...  If this case doesn't exist in reality then we could just
> > > remove this check altogether.
> > 
> > I'll go run a bunch of builds with a print on it, that should tell us I
> > suppose.
> 
> I can a bunch of builds, including an allmodconfig with the below on top
> and it 'works'.
> 
> So I suppose we can remove this special case.
> 
> ---
> --- a/tools/objtool/check.c
> +++ b/tools/objtool/check.c
> @@ -2134,11 +2134,13 @@ static int apply_insn_hint(struct objtoo
>  			 * after the save insn has been
>  			 * visited.
>  			 */
> -			if (insn == first)
> -				return 0; // XXX
> 
>  			WARN_FUNC("objtool isn't smart enough to handle this CFI save/restore combo",
>  					sec, insn->offset);
> +
> +			if (insn == first)
> +				return -1;
> +

I think all the validate_branch() callers aren't prepared to handle a -1
return code.

We can just be lazy and remove this 'insn == first' check and consider
it a non-fatal warning.

-- 
Josh


^ permalink raw reply	[flat|nested] 89+ messages in thread

* [PATCH v3.1 18a/26] objtool: Remove CFI save/restore special case
  2020-03-24 15:31 ` [PATCH v3 18/26] objtool: Fix !CFI insn_state propagation Peter Zijlstra
  2020-03-24 21:40   ` Josh Poimboeuf
@ 2020-03-25 14:41   ` Peter Zijlstra
  2020-03-25 14:42   ` [PATCH v3.1 18b/26] objtool: Factor out CFI hints Peter Zijlstra
                     ` (2 subsequent siblings)
  4 siblings, 0 replies; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-25 14:41 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, mhiramat, mbenes, brgerst

Subject: objtool: Remove CFI save/restore special case
From: Peter Zijlstra <peterz@infradead.org>
Date: Wed Mar 25 12:58:16 CET 2020

There is a special case in the UNWIND_HINT_RESTORE code. When, upon
looking for the UNWIND_HINT_SAVE instruction to restore from, it finds
the instruction hasn't been visited yet, it normally issues a WARN,
except when this HINT_SAVE instruction is the first instruction of
this branch.

This special case is of dubious correctness and is certainly unused
(verified with an allmodconfig build), the two sites that employ
UNWIND_HINT_SAVE/RESTORE (sync_core() and ftrace_regs_caller()) have
the SAVE on unconditional instructions at the start of the function.
It is therefore impossible for the save_insn not to have been visited
when we do hit the RESTORE.

Remove this.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
 tools/objtool/check.c |   15 ++-------------
 1 file changed, 2 insertions(+), 13 deletions(-)

--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -2040,15 +2040,14 @@ static int validate_return(struct symbol
  * tools/objtool/Documentation/stack-validation.txt.
  */
 static int validate_branch(struct objtool_file *file, struct symbol *func,
-			   struct instruction *first, struct insn_state state)
+			   struct instruction *insn, struct insn_state state)
 {
+	struct instruction *next_insn;
 	struct alternative *alt;
-	struct instruction *insn, *next_insn;
 	struct section *sec;
 	u8 visited;
 	int ret;
 
-	insn = first;
 	sec = insn->sec;
 
 	if (insn->alt_group && list_empty(&insn->alts)) {
@@ -2101,16 +2100,6 @@ static int validate_branch(struct objtoo
 				}
 
 				if (!save_insn->visited) {
-					/*
-					 * Oops, no state to copy yet.
-					 * Hopefully we can reach this
-					 * instruction from another branch
-					 * after the save insn has been
-					 * visited.
-					 */
-					if (insn == first)
-						return 0;
-
 					WARN_FUNC("objtool isn't smart enough to handle this CFI save/restore combo",
 						  sec, insn->offset);
 					return 1;

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 26/26] objtool: Add STT_NOTYPE noinstr validation
  2020-03-24 22:34     ` Peter Zijlstra
@ 2020-03-25 14:42       ` Josh Poimboeuf
  2020-03-25 15:53         ` Peter Zijlstra
  0 siblings, 1 reply; 89+ messages in thread
From: Josh Poimboeuf @ 2020-03-25 14:42 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: tglx, linux-kernel, x86, mhiramat, mbenes, brgerst

On Tue, Mar 24, 2020 at 11:34:55PM +0100, Peter Zijlstra wrote:
> On Tue, Mar 24, 2020 at 05:16:16PM -0500, Josh Poimboeuf wrote:
> > On Tue, Mar 24, 2020 at 04:31:39PM +0100, Peter Zijlstra wrote:
> 
> > > +	if (state.noinstr) {
> > > +		/*
> > > +		 * In vmlinux mode we will not run validate_unwind_hints() by
> > > +		 * default which means we'll not otherwise visit STT_NOTYPE
> > > +		 * symbols.
> > > +		 *
> > > +		 * In case of --duplicate mode, insn->visited will avoid actual
> > > +		 * duplicate work being done.
> > > +		 */
> > > +		list_for_each_entry(func, &sec->symbol_list, list) {
> > > +			if (func->type != STT_NOTYPE)
> > > +				continue;
> > > +
> > > +			warnings += validate_symbol(file, sec, func, &state);
> > > +		}
> > > +	}
> > > +
> > 
> > I guess this is ok, but is there a valid reason why we don't just call
> > validate_unwind_hints()?
> > 
> > It's also slightly concerning that validate_reachable_instructions()
> > isn't called, I'm not 100% convinced all the code will get checked.
> 
> This will only end up running on .noinstr.text, while
> validate_unwind_hints() will run on *everything*. That is, we're
> purposely not checking everything.
> 
> It very much relies on the !vmlinux mode to do the unreachable things.

Sure, but couldn't validate_unwind_hints() and
validate_reachable_instructions() be changed to *only* run on
.noinstr.text, for the vmlinux case?  That might help converge the
vmlinux and !vmlinux paths.

-- 
Josh


^ permalink raw reply	[flat|nested] 89+ messages in thread

* [PATCH v3.1 18b/26] objtool: Factor out CFI hints
  2020-03-24 15:31 ` [PATCH v3 18/26] objtool: Fix !CFI insn_state propagation Peter Zijlstra
  2020-03-24 21:40   ` Josh Poimboeuf
  2020-03-25 14:41   ` [PATCH v3.1 18a/26] objtool: Remove CFI save/restore special case Peter Zijlstra
@ 2020-03-25 14:42   ` Peter Zijlstra
  2020-03-25 14:43   ` [PATCH v3.1 18c/26] objtool: Rename struct cfi_state Peter Zijlstra
  2020-03-25 14:43   ` [PATCH v3.1 18d/26] objtool: Fix !CFI insn_state propagation Peter Zijlstra
  4 siblings, 0 replies; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-25 14:42 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, mhiramat, mbenes, brgerst

Subject: objtool: Factor out CFI hints
From: Peter Zijlstra <peterz@infradead.org>
Date: Wed Mar 25 13:08:17 CET 2020

Move the application of CFI hints into it's own function.
No functional changes intended.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
 tools/objtool/check.c |   66 ++++++++++++++++++++++++++++----------------------
 1 file changed, 37 insertions(+), 29 deletions(-)

--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -2033,6 +2033,40 @@ static int validate_return(struct symbol
 	return 0;
 }
 
+static int apply_insn_hint(struct objtool_file *file, struct section *sec,
+			   struct symbol *func, struct instruction *insn,
+			   struct insn_state *state)
+{
+	if (insn->restore) {
+		struct instruction *save_insn, *i;
+
+		i = insn;
+		save_insn = NULL;
+		sym_for_each_insn_continue_reverse(file, func, i) {
+			if (i->save) {
+				save_insn = i;
+				break;
+			}
+		}
+
+		if (!save_insn) {
+			WARN_FUNC("no corresponding CFI save for CFI restore",
+				  sec, insn->offset);
+			return 1;
+		}
+
+		if (!save_insn->visited) {
+			WARN_FUNC("objtool isn't smart enough to handle this CFI save/restore combo",
+				  sec, insn->offset);
+			return 1;
+		}
+
+		insn->state = save_insn->state;
+	}
+
+	state = insn->state;
+}
+
 /*
  * Follow the branch starting at the given instruction, and recursively follow
  * any other branches (jumps).  Meanwhile, track the frame pointer state at
@@ -2081,35 +2115,9 @@ static int validate_branch(struct objtoo
 		}
 
 		if (insn->hint) {
-			if (insn->restore) {
-				struct instruction *save_insn, *i;
-
-				i = insn;
-				save_insn = NULL;
-				sym_for_each_insn_continue_reverse(file, func, i) {
-					if (i->save) {
-						save_insn = i;
-						break;
-					}
-				}
-
-				if (!save_insn) {
-					WARN_FUNC("no corresponding CFI save for CFI restore",
-						  sec, insn->offset);
-					return 1;
-				}
-
-				if (!save_insn->visited) {
-					WARN_FUNC("objtool isn't smart enough to handle this CFI save/restore combo",
-						  sec, insn->offset);
-					return 1;
-				}
-
-				insn->state = save_insn->state;
-			}
-
-			state = insn->state;
-
+			ret = apply_insn_hint(file, sec, func, insn, &state);
+			if (ret)
+				return ret;
 		} else
 			insn->state = state;
 

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [PATCH v3.1 18c/26] objtool: Rename struct cfi_state
  2020-03-24 15:31 ` [PATCH v3 18/26] objtool: Fix !CFI insn_state propagation Peter Zijlstra
                     ` (2 preceding siblings ...)
  2020-03-25 14:42   ` [PATCH v3.1 18b/26] objtool: Factor out CFI hints Peter Zijlstra
@ 2020-03-25 14:43   ` Peter Zijlstra
  2020-03-25 14:43   ` [PATCH v3.1 18d/26] objtool: Fix !CFI insn_state propagation Peter Zijlstra
  4 siblings, 0 replies; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-25 14:43 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, mhiramat, mbenes, brgerst

Subject: objtool: Rename struct cfi_state
From: Peter Zijlstra <peterz@infradead.org>
Date: Wed Mar 25 15:34:50 CET 2020

There's going to be a new struct cfi_state, rename this one to make
place.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
 tools/objtool/arch.h            |    2 +-
 tools/objtool/arch/x86/decode.c |    2 +-
 tools/objtool/cfi.h             |    2 +-
 tools/objtool/check.c           |    2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

--- a/tools/objtool/arch.h
+++ b/tools/objtool/arch.h
@@ -66,7 +66,7 @@ struct stack_op {
 	struct op_src src;
 };
 
-void arch_initial_func_cfi_state(struct cfi_state *state);
+void arch_initial_func_cfi_state(struct cfi_init_state *state);
 
 int arch_decode_instruction(struct elf *elf, struct section *sec,
 			    unsigned long offset, unsigned int maxlen,
--- a/tools/objtool/arch/x86/decode.c
+++ b/tools/objtool/arch/x86/decode.c
@@ -480,7 +480,7 @@ int arch_decode_instruction(struct elf *
 	return 0;
 }
 
-void arch_initial_func_cfi_state(struct cfi_state *state)
+void arch_initial_func_cfi_state(struct cfi_init_state *state)
 {
 	int i;
 
--- a/tools/objtool/cfi.h
+++ b/tools/objtool/cfi.h
@@ -35,7 +35,7 @@ struct cfi_reg {
 	int offset;
 };
 
-struct cfi_state {
+struct cfi_init_state {
 	struct cfi_reg cfa;
 	struct cfi_reg regs[CFI_NUM_REGS];
 };
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -27,7 +27,7 @@ struct alternative {
 };
 
 const char *objname;
-struct cfi_state initial_func_cfi;
+struct cfi_init_state initial_func_cfi;
 
 struct instruction *find_insn(struct objtool_file *file,
 			      struct section *sec, unsigned long offset)

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [PATCH v3.1 18d/26] objtool: Fix !CFI insn_state propagation
  2020-03-24 15:31 ` [PATCH v3 18/26] objtool: Fix !CFI insn_state propagation Peter Zijlstra
                     ` (3 preceding siblings ...)
  2020-03-25 14:43   ` [PATCH v3.1 18c/26] objtool: Rename struct cfi_state Peter Zijlstra
@ 2020-03-25 14:43   ` Peter Zijlstra
  4 siblings, 0 replies; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-25 14:43 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, mhiramat, mbenes, brgerst

Subject: objtool: Fix !CFI insn_state propagation
From: Peter Zijlstra <peterz@infradead.org>
Date: Wed Mar 25 14:04:45 CET 2020

Objtool keeps per instruction CFI state in struct insn_state and will
save/restore this where required. However, insn_state has grown some
!CFI state, and this must not be saved/restored (that would
loose/destroy state).

Fix this by moving the CFI specific parts of insn_state into struct
cfi_state.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
 tools/objtool/cfi.h     |   12 ++
 tools/objtool/check.c   |  270 ++++++++++++++++++++++++------------------------
 tools/objtool/check.h   |   13 --
 tools/objtool/orc_gen.c |    8 -
 4 files changed, 160 insertions(+), 143 deletions(-)

--- a/tools/objtool/cfi.h
+++ b/tools/objtool/cfi.h
@@ -36,8 +36,20 @@ struct cfi_reg {
 };
 
 struct cfi_init_state {
+	struct cfi_reg regs[CFI_NUM_REGS];
 	struct cfi_reg cfa;
+};
+
+struct cfi_state {
 	struct cfi_reg regs[CFI_NUM_REGS];
+	struct cfi_reg vals[CFI_NUM_REGS];
+	struct cfi_reg cfa;
+	int stack_size;
+	int drap_reg, drap_offset;
+	unsigned char type;
+	bool bp_scratch;
+	bool drap;
+	bool end;
 };
 
 #endif /* _OBJTOOL_CFI_H */
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -215,18 +215,23 @@ static bool dead_end_function(struct obj
 	return __dead_end_function(file, func, 0);
 }
 
-static void clear_insn_state(struct insn_state *state)
+static void init_cfi_state(struct cfi_state *cfi)
 {
 	int i;
 
-	memset(state, 0, sizeof(*state));
-	state->cfa.base = CFI_UNDEFINED;
 	for (i = 0; i < CFI_NUM_REGS; i++) {
-		state->regs[i].base = CFI_UNDEFINED;
-		state->vals[i].base = CFI_UNDEFINED;
+		cfi->regs[i].base = CFI_UNDEFINED;
+		cfi->vals[i].base = CFI_UNDEFINED;
 	}
-	state->drap_reg = CFI_UNDEFINED;
-	state->drap_offset = -1;
+	cfi->cfa.base = CFI_UNDEFINED;
+	cfi->drap_reg = CFI_UNDEFINED;
+	cfi->drap_offset = -1;
+}
+
+static void clear_insn_state(struct insn_state *state)
+{
+	memset(state, 0, sizeof(*state));
+	init_cfi_state(&state->cfi);
 }
 
 /*
@@ -260,7 +265,7 @@ static int decode_instructions(struct ob
 			}
 			memset(insn, 0, sizeof(*insn));
 			INIT_LIST_HEAD(&insn->alts);
-			clear_insn_state(&insn->state);
+			init_cfi_state(&insn->cfi);
 
 			insn->sec = sec;
 			insn->offset = offset;
@@ -769,7 +774,7 @@ static int handle_group_alt(struct objto
 		}
 		memset(fake_jump, 0, sizeof(*fake_jump));
 		INIT_LIST_HEAD(&fake_jump->alts);
-		clear_insn_state(&fake_jump->state);
+		init_cfi_state(&fake_jump->cfi);
 
 		fake_jump->sec = special_alt->new_sec;
 		fake_jump->offset = FAKE_JUMP_OFFSET;
@@ -1262,7 +1267,7 @@ static int read_unwind_hints(struct objt
 			return -1;
 		}
 
-		cfa = &insn->state.cfa;
+		cfa = &insn->cfi.cfa;
 
 		if (hint->type == UNWIND_HINT_TYPE_SAVE) {
 			insn->save = true;
@@ -1308,8 +1313,8 @@ static int read_unwind_hints(struct objt
 		}
 
 		cfa->offset = hint->sp_offset;
-		insn->state.type = hint->type;
-		insn->state.end = hint->end;
+		insn->cfi.type = hint->type;
+		insn->cfi.end = hint->end;
 	}
 
 	return 0;
@@ -1436,17 +1441,18 @@ static bool is_fentry_call(struct instru
 
 static bool has_modified_stack_frame(struct insn_state *state)
 {
+	struct cfi_state *cfi = &state->cfi;
 	int i;
 
-	if (state->cfa.base != initial_func_cfi.cfa.base ||
-	    state->cfa.offset != initial_func_cfi.cfa.offset ||
-	    state->stack_size != initial_func_cfi.cfa.offset ||
-	    state->drap)
+	if (cfi->cfa.base != initial_func_cfi.cfa.base ||
+	    cfi->cfa.offset != initial_func_cfi.cfa.offset ||
+	    cfi->stack_size != initial_func_cfi.cfa.offset ||
+	    cfi->drap)
 		return true;
 
 	for (i = 0; i < CFI_NUM_REGS; i++)
-		if (state->regs[i].base != initial_func_cfi.regs[i].base ||
-		    state->regs[i].offset != initial_func_cfi.regs[i].offset)
+		if (cfi->regs[i].base != initial_func_cfi.regs[i].base ||
+		    cfi->regs[i].offset != initial_func_cfi.regs[i].offset)
 			return true;
 
 	return false;
@@ -1454,19 +1460,21 @@ static bool has_modified_stack_frame(str
 
 static bool has_valid_stack_frame(struct insn_state *state)
 {
-	if (state->cfa.base == CFI_BP && state->regs[CFI_BP].base == CFI_CFA &&
-	    state->regs[CFI_BP].offset == -16)
+	struct cfi_state *cfi = &state->cfi;
+
+	if (cfi->cfa.base == CFI_BP && cfi->regs[CFI_BP].base == CFI_CFA &&
+	    cfi->regs[CFI_BP].offset == -16)
 		return true;
 
-	if (state->drap && state->regs[CFI_BP].base == CFI_BP)
+	if (cfi->drap && cfi->regs[CFI_BP].base == CFI_BP)
 		return true;
 
 	return false;
 }
 
-static int update_insn_state_regs(struct instruction *insn, struct insn_state *state)
+static int update_cfi_state_regs(struct instruction *insn, struct cfi_state *cfi)
 {
-	struct cfi_reg *cfa = &state->cfa;
+	struct cfi_reg *cfa = &cfi->cfa;
 	struct stack_op *op = &insn->stack_op;
 
 	if (cfa->base != CFI_SP)
@@ -1488,20 +1496,19 @@ static int update_insn_state_regs(struct
 	return 0;
 }
 
-static void save_reg(struct insn_state *state, unsigned char reg, int base,
-		     int offset)
+static void save_reg(struct cfi_state *cfi, unsigned char reg, int base, int offset)
 {
 	if (arch_callee_saved_reg(reg) &&
-	    state->regs[reg].base == CFI_UNDEFINED) {
-		state->regs[reg].base = base;
-		state->regs[reg].offset = offset;
+	    cfi->regs[reg].base == CFI_UNDEFINED) {
+		cfi->regs[reg].base = base;
+		cfi->regs[reg].offset = offset;
 	}
 }
 
-static void restore_reg(struct insn_state *state, unsigned char reg)
+static void restore_reg(struct cfi_state *cfi, unsigned char reg)
 {
-	state->regs[reg].base = CFI_UNDEFINED;
-	state->regs[reg].offset = 0;
+	cfi->regs[reg].base = CFI_UNDEFINED;
+	cfi->regs[reg].offset = 0;
 }
 
 /*
@@ -1557,11 +1564,11 @@ static void restore_reg(struct insn_stat
  *   41 5d			pop    %r13
  *   c3				retq
  */
-static int update_insn_state(struct instruction *insn, struct insn_state *state)
+static int update_cfi_state(struct instruction *insn, struct cfi_state *cfi)
 {
 	struct stack_op *op = &insn->stack_op;
-	struct cfi_reg *cfa = &state->cfa;
-	struct cfi_reg *regs = state->regs;
+	struct cfi_reg *cfa = &cfi->cfa;
+	struct cfi_reg *regs = cfi->regs;
 
 	/* stack operations don't make sense with an undefined CFA */
 	if (cfa->base == CFI_UNDEFINED) {
@@ -1572,8 +1579,8 @@ static int update_insn_state(struct inst
 		return 0;
 	}
 
-	if (state->type == ORC_TYPE_REGS || state->type == ORC_TYPE_REGS_IRET)
-		return update_insn_state_regs(insn, state);
+	if (cfi->type == ORC_TYPE_REGS || cfi->type == ORC_TYPE_REGS_IRET)
+		return update_cfi_state_regs(insn, cfi);
 
 	switch (op->dest.type) {
 
@@ -1588,16 +1595,16 @@ static int update_insn_state(struct inst
 
 				/* mov %rsp, %rbp */
 				cfa->base = op->dest.reg;
-				state->bp_scratch = false;
+				cfi->bp_scratch = false;
 			}
 
 			else if (op->src.reg == CFI_SP &&
-				 op->dest.reg == CFI_BP && state->drap) {
+				 op->dest.reg == CFI_BP && cfi->drap) {
 
 				/* drap: mov %rsp, %rbp */
 				regs[CFI_BP].base = CFI_BP;
-				regs[CFI_BP].offset = -state->stack_size;
-				state->bp_scratch = false;
+				regs[CFI_BP].offset = -cfi->stack_size;
+				cfi->bp_scratch = false;
 			}
 
 			else if (op->src.reg == CFI_SP && cfa->base == CFI_SP) {
@@ -1612,8 +1619,8 @@ static int update_insn_state(struct inst
 				 *   ...
 				 *   mov    %rax, %rsp
 				 */
-				state->vals[op->dest.reg].base = CFI_CFA;
-				state->vals[op->dest.reg].offset = -state->stack_size;
+				cfi->vals[op->dest.reg].base = CFI_CFA;
+				cfi->vals[op->dest.reg].offset = -cfi->stack_size;
 			}
 
 			else if (op->src.reg == CFI_BP && op->dest.reg == CFI_SP &&
@@ -1624,14 +1631,14 @@ static int update_insn_state(struct inst
 				 *
 				 * Restore the original stack pointer (Clang).
 				 */
-				state->stack_size = -state->regs[CFI_BP].offset;
+				cfi->stack_size = -cfi->regs[CFI_BP].offset;
 			}
 
 			else if (op->dest.reg == cfa->base) {
 
 				/* mov %reg, %rsp */
 				if (cfa->base == CFI_SP &&
-				    state->vals[op->src.reg].base == CFI_CFA) {
+				    cfi->vals[op->src.reg].base == CFI_CFA) {
 
 					/*
 					 * This is needed for the rare case
@@ -1641,8 +1648,8 @@ static int update_insn_state(struct inst
 					 *   ...
 					 *   mov    %rcx, %rsp
 					 */
-					cfa->offset = -state->vals[op->src.reg].offset;
-					state->stack_size = cfa->offset;
+					cfa->offset = -cfi->vals[op->src.reg].offset;
+					cfi->stack_size = cfa->offset;
 
 				} else {
 					cfa->base = CFI_UNDEFINED;
@@ -1656,7 +1663,7 @@ static int update_insn_state(struct inst
 			if (op->dest.reg == CFI_SP && op->src.reg == CFI_SP) {
 
 				/* add imm, %rsp */
-				state->stack_size -= op->src.offset;
+				cfi->stack_size -= op->src.offset;
 				if (cfa->base == CFI_SP)
 					cfa->offset -= op->src.offset;
 				break;
@@ -1665,14 +1672,14 @@ static int update_insn_state(struct inst
 			if (op->dest.reg == CFI_SP && op->src.reg == CFI_BP) {
 
 				/* lea disp(%rbp), %rsp */
-				state->stack_size = -(op->src.offset + regs[CFI_BP].offset);
+				cfi->stack_size = -(op->src.offset + regs[CFI_BP].offset);
 				break;
 			}
 
 			if (op->src.reg == CFI_SP && cfa->base == CFI_SP) {
 
 				/* drap: lea disp(%rsp), %drap */
-				state->drap_reg = op->dest.reg;
+				cfi->drap_reg = op->dest.reg;
 
 				/*
 				 * lea disp(%rsp), %reg
@@ -1684,25 +1691,25 @@ static int update_insn_state(struct inst
 				 *   ...
 				 *   mov    %rcx, %rsp
 				 */
-				state->vals[op->dest.reg].base = CFI_CFA;
-				state->vals[op->dest.reg].offset = \
-					-state->stack_size + op->src.offset;
+				cfi->vals[op->dest.reg].base = CFI_CFA;
+				cfi->vals[op->dest.reg].offset = \
+					-cfi->stack_size + op->src.offset;
 
 				break;
 			}
 
-			if (state->drap && op->dest.reg == CFI_SP &&
-			    op->src.reg == state->drap_reg) {
+			if (cfi->drap && op->dest.reg == CFI_SP &&
+			    op->src.reg == cfi->drap_reg) {
 
 				 /* drap: lea disp(%drap), %rsp */
 				cfa->base = CFI_SP;
-				cfa->offset = state->stack_size = -op->src.offset;
-				state->drap_reg = CFI_UNDEFINED;
-				state->drap = false;
+				cfa->offset = cfi->stack_size = -op->src.offset;
+				cfi->drap_reg = CFI_UNDEFINED;
+				cfi->drap = false;
 				break;
 			}
 
-			if (op->dest.reg == state->cfa.base) {
+			if (op->dest.reg == cfi->cfa.base) {
 				WARN_FUNC("unsupported stack register modification",
 					  insn->sec, insn->offset);
 				return -1;
@@ -1712,18 +1719,18 @@ static int update_insn_state(struct inst
 
 		case OP_SRC_AND:
 			if (op->dest.reg != CFI_SP ||
-			    (state->drap_reg != CFI_UNDEFINED && cfa->base != CFI_SP) ||
-			    (state->drap_reg == CFI_UNDEFINED && cfa->base != CFI_BP)) {
+			    (cfi->drap_reg != CFI_UNDEFINED && cfa->base != CFI_SP) ||
+			    (cfi->drap_reg == CFI_UNDEFINED && cfa->base != CFI_BP)) {
 				WARN_FUNC("unsupported stack pointer realignment",
 					  insn->sec, insn->offset);
 				return -1;
 			}
 
-			if (state->drap_reg != CFI_UNDEFINED) {
+			if (cfi->drap_reg != CFI_UNDEFINED) {
 				/* drap: and imm, %rsp */
-				cfa->base = state->drap_reg;
-				cfa->offset = state->stack_size = 0;
-				state->drap = true;
+				cfa->base = cfi->drap_reg;
+				cfa->offset = cfi->stack_size = 0;
+				cfi->drap = true;
 			}
 
 			/*
@@ -1735,57 +1742,57 @@ static int update_insn_state(struct inst
 
 		case OP_SRC_POP:
 		case OP_SRC_POPF:
-			if (!state->drap && op->dest.type == OP_DEST_REG &&
+			if (!cfi->drap && op->dest.type == OP_DEST_REG &&
 			    op->dest.reg == cfa->base) {
 
 				/* pop %rbp */
 				cfa->base = CFI_SP;
 			}
 
-			if (state->drap && cfa->base == CFI_BP_INDIRECT &&
+			if (cfi->drap && cfa->base == CFI_BP_INDIRECT &&
 			    op->dest.type == OP_DEST_REG &&
-			    op->dest.reg == state->drap_reg &&
-			    state->drap_offset == -state->stack_size) {
+			    op->dest.reg == cfi->drap_reg &&
+			    cfi->drap_offset == -cfi->stack_size) {
 
 				/* drap: pop %drap */
-				cfa->base = state->drap_reg;
+				cfa->base = cfi->drap_reg;
 				cfa->offset = 0;
-				state->drap_offset = -1;
+				cfi->drap_offset = -1;
 
-			} else if (regs[op->dest.reg].offset == -state->stack_size) {
+			} else if (regs[op->dest.reg].offset == -cfi->stack_size) {
 
 				/* pop %reg */
-				restore_reg(state, op->dest.reg);
+				restore_reg(cfi, op->dest.reg);
 			}
 
-			state->stack_size -= 8;
+			cfi->stack_size -= 8;
 			if (cfa->base == CFI_SP)
 				cfa->offset -= 8;
 
 			break;
 
 		case OP_SRC_REG_INDIRECT:
-			if (state->drap && op->src.reg == CFI_BP &&
-			    op->src.offset == state->drap_offset) {
+			if (cfi->drap && op->src.reg == CFI_BP &&
+			    op->src.offset == cfi->drap_offset) {
 
 				/* drap: mov disp(%rbp), %drap */
-				cfa->base = state->drap_reg;
+				cfa->base = cfi->drap_reg;
 				cfa->offset = 0;
-				state->drap_offset = -1;
+				cfi->drap_offset = -1;
 			}
 
-			if (state->drap && op->src.reg == CFI_BP &&
+			if (cfi->drap && op->src.reg == CFI_BP &&
 			    op->src.offset == regs[op->dest.reg].offset) {
 
 				/* drap: mov disp(%rbp), %reg */
-				restore_reg(state, op->dest.reg);
+				restore_reg(cfi, op->dest.reg);
 
 			} else if (op->src.reg == cfa->base &&
 			    op->src.offset == regs[op->dest.reg].offset + cfa->offset) {
 
 				/* mov disp(%rbp), %reg */
 				/* mov disp(%rsp), %reg */
-				restore_reg(state, op->dest.reg);
+				restore_reg(cfi, op->dest.reg);
 			}
 
 			break;
@@ -1800,78 +1807,78 @@ static int update_insn_state(struct inst
 
 	case OP_DEST_PUSH:
 	case OP_DEST_PUSHF:
-		state->stack_size += 8;
+		cfi->stack_size += 8;
 		if (cfa->base == CFI_SP)
 			cfa->offset += 8;
 
 		if (op->src.type != OP_SRC_REG)
 			break;
 
-		if (state->drap) {
-			if (op->src.reg == cfa->base && op->src.reg == state->drap_reg) {
+		if (cfi->drap) {
+			if (op->src.reg == cfa->base && op->src.reg == cfi->drap_reg) {
 
 				/* drap: push %drap */
 				cfa->base = CFI_BP_INDIRECT;
-				cfa->offset = -state->stack_size;
+				cfa->offset = -cfi->stack_size;
 
 				/* save drap so we know when to restore it */
-				state->drap_offset = -state->stack_size;
+				cfi->drap_offset = -cfi->stack_size;
 
-			} else if (op->src.reg == CFI_BP && cfa->base == state->drap_reg) {
+			} else if (op->src.reg == CFI_BP && cfa->base == cfi->drap_reg) {
 
 				/* drap: push %rbp */
-				state->stack_size = 0;
+				cfi->stack_size = 0;
 
 			} else if (regs[op->src.reg].base == CFI_UNDEFINED) {
 
 				/* drap: push %reg */
-				save_reg(state, op->src.reg, CFI_BP, -state->stack_size);
+				save_reg(cfi, op->src.reg, CFI_BP, -cfi->stack_size);
 			}
 
 		} else {
 
 			/* push %reg */
-			save_reg(state, op->src.reg, CFI_CFA, -state->stack_size);
+			save_reg(cfi, op->src.reg, CFI_CFA, -cfi->stack_size);
 		}
 
 		/* detect when asm code uses rbp as a scratch register */
 		if (!no_fp && insn->func && op->src.reg == CFI_BP &&
 		    cfa->base != CFI_BP)
-			state->bp_scratch = true;
+			cfi->bp_scratch = true;
 		break;
 
 	case OP_DEST_REG_INDIRECT:
 
-		if (state->drap) {
-			if (op->src.reg == cfa->base && op->src.reg == state->drap_reg) {
+		if (cfi->drap) {
+			if (op->src.reg == cfa->base && op->src.reg == cfi->drap_reg) {
 
 				/* drap: mov %drap, disp(%rbp) */
 				cfa->base = CFI_BP_INDIRECT;
 				cfa->offset = op->dest.offset;
 
 				/* save drap offset so we know when to restore it */
-				state->drap_offset = op->dest.offset;
+				cfi->drap_offset = op->dest.offset;
 			}
 
 			else if (regs[op->src.reg].base == CFI_UNDEFINED) {
 
 				/* drap: mov reg, disp(%rbp) */
-				save_reg(state, op->src.reg, CFI_BP, op->dest.offset);
+				save_reg(cfi, op->src.reg, CFI_BP, op->dest.offset);
 			}
 
 		} else if (op->dest.reg == cfa->base) {
 
 			/* mov reg, disp(%rbp) */
 			/* mov reg, disp(%rsp) */
-			save_reg(state, op->src.reg, CFI_CFA,
-				 op->dest.offset - state->cfa.offset);
+			save_reg(cfi, op->src.reg, CFI_CFA,
+				 op->dest.offset - cfi->cfa.offset);
 		}
 
 		break;
 
 	case OP_DEST_LEAVE:
-		if ((!state->drap && cfa->base != CFI_BP) ||
-		    (state->drap && cfa->base != state->drap_reg)) {
+		if ((!cfi->drap && cfa->base != CFI_BP) ||
+		    (cfi->drap && cfa->base != cfi->drap_reg)) {
 			WARN_FUNC("leave instruction with modified stack frame",
 				  insn->sec, insn->offset);
 			return -1;
@@ -1879,10 +1886,10 @@ static int update_insn_state(struct inst
 
 		/* leave (mov %rbp, %rsp; pop %rbp) */
 
-		state->stack_size = -state->regs[CFI_BP].offset - 8;
-		restore_reg(state, CFI_BP);
+		cfi->stack_size = -cfi->regs[CFI_BP].offset - 8;
+		restore_reg(cfi, CFI_BP);
 
-		if (!state->drap) {
+		if (!cfi->drap) {
 			cfa->base = CFI_SP;
 			cfa->offset -= 8;
 		}
@@ -1897,7 +1904,7 @@ static int update_insn_state(struct inst
 		}
 
 		/* pop mem */
-		state->stack_size -= 8;
+		cfi->stack_size -= 8;
 		if (cfa->base == CFI_SP)
 			cfa->offset -= 8;
 
@@ -1912,41 +1919,44 @@ static int update_insn_state(struct inst
 	return 0;
 }
 
-static bool insn_state_match(struct instruction *insn, struct insn_state *state)
+static bool insn_cfi_match(struct instruction *insn, struct cfi_state *cfi2)
 {
-	struct insn_state *state1 = &insn->state, *state2 = state;
+	struct cfi_state *cfi1 = &insn->cfi;
 	int i;
 
-	if (memcmp(&state1->cfa, &state2->cfa, sizeof(state1->cfa))) {
+	if (memcmp(&cfi1->cfa, &cfi2->cfa, sizeof(cfi1->cfa))) {
+
 		WARN_FUNC("stack state mismatch: cfa1=%d%+d cfa2=%d%+d",
 			  insn->sec, insn->offset,
-			  state1->cfa.base, state1->cfa.offset,
-			  state2->cfa.base, state2->cfa.offset);
+			  cfi1->cfa.base, cfi1->cfa.offset,
+			  cfi2->cfa.base, cfi2->cfa.offset);
 
-	} else if (memcmp(&state1->regs, &state2->regs, sizeof(state1->regs))) {
+	} else if (memcmp(&cfi1->regs, &cfi2->regs, sizeof(cfi1->regs))) {
 		for (i = 0; i < CFI_NUM_REGS; i++) {
-			if (!memcmp(&state1->regs[i], &state2->regs[i],
+			if (!memcmp(&cfi1->regs[i], &cfi2->regs[i],
 				    sizeof(struct cfi_reg)))
 				continue;
 
 			WARN_FUNC("stack state mismatch: reg1[%d]=%d%+d reg2[%d]=%d%+d",
 				  insn->sec, insn->offset,
-				  i, state1->regs[i].base, state1->regs[i].offset,
-				  i, state2->regs[i].base, state2->regs[i].offset);
+				  i, cfi1->regs[i].base, cfi1->regs[i].offset,
+				  i, cfi2->regs[i].base, cfi2->regs[i].offset);
 			break;
 		}
 
-	} else if (state1->type != state2->type) {
+	} else if (cfi1->type != cfi2->type) {
+
 		WARN_FUNC("stack state mismatch: type1=%d type2=%d",
-			  insn->sec, insn->offset, state1->type, state2->type);
+			  insn->sec, insn->offset, cfi1->type, cfi2->type);
+
+	} else if (cfi1->drap != cfi2->drap ||
+		   (cfi1->drap && cfi1->drap_reg != cfi2->drap_reg) ||
+		   (cfi1->drap && cfi1->drap_offset != cfi2->drap_offset)) {
 
-	} else if (state1->drap != state2->drap ||
-		 (state1->drap && state1->drap_reg != state2->drap_reg) ||
-		 (state1->drap && state1->drap_offset != state2->drap_offset)) {
 		WARN_FUNC("stack state mismatch: drap1=%d(%d,%d) drap2=%d(%d,%d)",
 			  insn->sec, insn->offset,
-			  state1->drap, state1->drap_reg, state1->drap_offset,
-			  state2->drap, state2->drap_reg, state2->drap_offset);
+			  cfi1->drap, cfi1->drap_reg, cfi1->drap_offset,
+			  cfi2->drap, cfi2->drap_reg, cfi2->drap_offset);
 
 	} else
 		return true;
@@ -2024,7 +2034,7 @@ static int validate_return(struct symbol
 		return 1;
 	}
 
-	if (state->bp_scratch) {
+	if (state->cfi.bp_scratch) {
 		WARN("%s uses BP as a scratch register",
 		     func->name);
 		return 1;
@@ -2035,7 +2045,7 @@ static int validate_return(struct symbol
 
 static int apply_insn_hint(struct objtool_file *file, struct section *sec,
 			   struct symbol *func, struct instruction *insn,
-			   struct insn_state *state)
+			   struct cfi_state *cfi)
 {
 	if (insn->restore) {
 		struct instruction *save_insn, *i;
@@ -2061,10 +2071,10 @@ static int apply_insn_hint(struct objtoo
 			return 1;
 		}
 
-		insn->state = save_insn->state;
+		insn->cfi = save_insn->cfi;
 	}
 
-	state = insn->state;
+	*cfi = insn->cfi;
 }
 
 /*
@@ -2107,7 +2117,7 @@ static int validate_branch(struct objtoo
 
 		visited = 1 << state.uaccess;
 		if (insn->visited) {
-			if (!insn->hint && !insn_state_match(insn, &state))
+			if (!insn->hint && !insn_cfi_match(insn, &state.cfi))
 				return 1;
 
 			if (insn->visited & visited)
@@ -2115,11 +2125,11 @@ static int validate_branch(struct objtoo
 		}
 
 		if (insn->hint) {
-			ret = apply_insn_hint(file, sec, func, insn, &state);
+			ret = apply_insn_hint(file, sec, func, insn, &state.cfi);
 			if (ret)
 				return ret;
 		} else
-			insn->state = state;
+			insn->cfi = state.cfi;
 
 		insn->visited |= visited;
 
@@ -2209,7 +2219,7 @@ static int validate_branch(struct objtoo
 			return 0;
 
 		case INSN_STACK:
-			if (update_insn_state(insn, &state))
+			if (update_cfi_state(insn, &state.cfi))
 				return 1;
 
 			if (insn->stack_op.dest.type == OP_DEST_PUSHF) {
@@ -2279,7 +2289,7 @@ static int validate_branch(struct objtoo
 			return 0;
 
 		if (!next_insn) {
-			if (state.cfa.base == CFI_UNDEFINED)
+			if (state.cfi.cfa.base == CFI_UNDEFINED)
 				return 0;
 			WARN("%s: unexpected end of section", sec->name);
 			return 1;
@@ -2419,10 +2429,10 @@ static int validate_section(struct objto
 
 	clear_insn_state(&state);
 
-	state.cfa = initial_func_cfi.cfa;
-	memcpy(&state.regs, &initial_func_cfi.regs,
+	state.cfi.cfa = initial_func_cfi.cfa;
+	memcpy(&state.cfi.regs, &initial_func_cfi.regs,
 	       CFI_NUM_REGS * sizeof(struct cfi_reg));
-	state.stack_size = initial_func_cfi.cfa.offset;
+	state.cfi.stack_size = initial_func_cfi.cfa.offset;
 
 	list_for_each_entry(func, &sec->symbol_list, list) {
 		if (func->type != STT_FUNC)
--- a/tools/objtool/check.h
+++ b/tools/objtool/check.h
@@ -14,15 +14,10 @@
 #include <linux/hashtable.h>
 
 struct insn_state {
-	struct cfi_reg cfa;
-	struct cfi_reg regs[CFI_NUM_REGS];
-	int stack_size;
-	unsigned char type;
-	bool bp_scratch;
-	bool drap, end, uaccess, df;
+	struct cfi_state cfi;
 	unsigned int uaccess_stack;
-	int drap_reg, drap_offset;
-	struct cfi_reg vals[CFI_NUM_REGS];
+	bool uaccess;
+	bool df;
 };
 
 struct instruction {
@@ -43,7 +38,7 @@ struct instruction {
 	struct list_head alts;
 	struct symbol *func;
 	struct stack_op stack_op;
-	struct insn_state state;
+	struct cfi_state cfi;
 	struct orc_entry orc;
 };
 
--- a/tools/objtool/orc_gen.c
+++ b/tools/objtool/orc_gen.c
@@ -16,10 +16,10 @@ int create_orc(struct objtool_file *file
 
 	for_each_insn(file, insn) {
 		struct orc_entry *orc = &insn->orc;
-		struct cfi_reg *cfa = &insn->state.cfa;
-		struct cfi_reg *bp = &insn->state.regs[CFI_BP];
+		struct cfi_reg *cfa = &insn->cfi.cfa;
+		struct cfi_reg *bp = &insn->cfi.regs[CFI_BP];
 
-		orc->end = insn->state.end;
+		orc->end = insn->cfi.end;
 
 		if (cfa->base == CFI_UNDEFINED) {
 			orc->sp_reg = ORC_REG_UNDEFINED;
@@ -75,7 +75,7 @@ int create_orc(struct objtool_file *file
 
 		orc->sp_offset = cfa->offset;
 		orc->bp_offset = bp->offset;
-		orc->type = insn->state.type;
+		orc->type = insn->cfi.type;
 	}
 
 	return 0;

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [PATCH v3.1 19/26] objtool: Implement noinstr validation
  2020-03-24 15:31 ` [PATCH v3 19/26] objtool: Implement noinstr validation Peter Zijlstra
  2020-03-24 21:41   ` Josh Poimboeuf
@ 2020-03-25 14:44   ` Peter Zijlstra
  1 sibling, 0 replies; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-25 14:44 UTC (permalink / raw)
  To: tglx, jpoimboe; +Cc: linux-kernel, x86, mhiramat, mbenes, brgerst

Subject: objtool: Implement noinstr validation
From: Peter Zijlstra <peterz@infradead.org>
Date: Tue Mar 10 18:57:41 CET 2020

Validate that any call out of .noinstr.text is in between
instr_begin() and instr_end() annotations.

This annotation is useful to ensure correct behaviour wrt tracing
sensitive code like entry/exit and idle code. When we run code in a
sensitive context we want a guarantee no unknown code is ran.

Since this validation relies on knowing the section of call
destination symbols, we must run it on vmlinux.o instead of on
individual object files.

Add two options:

 -d/--duplicate "duplicate validation for vmlinux"
 -l/--vmlinux "vmlinux.o validation"

Where the latter auto-detects when objname ends with "vmlinux.o" and
the former will force all validations, also those already done on
!vmlinux object files.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
 tools/objtool/builtin-check.c |   11 +++-
 tools/objtool/builtin.h       |    2 
 tools/objtool/check.c         |   98 ++++++++++++++++++++++++++++++++++++++++++
 tools/objtool/check.h         |    3 +
 tools/objtool/elf.h           |    2 
 5 files changed, 112 insertions(+), 4 deletions(-)

--- a/tools/objtool/builtin-check.c
+++ b/tools/objtool/builtin-check.c
@@ -14,10 +14,11 @@
  */
 
 #include <subcmd/parse-options.h>
+#include <string.h>
 #include "builtin.h"
 #include "check.h"
 
-bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats;
+bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats, validate_dup, vmlinux;
 
 static const char * const check_usage[] = {
 	"objtool check [<options>] file.o",
@@ -32,12 +33,14 @@ const struct option check_options[] = {
 	OPT_BOOLEAN('b', "backtrace", &backtrace, "unwind on error"),
 	OPT_BOOLEAN('a', "uaccess", &uaccess, "enable uaccess checking"),
 	OPT_BOOLEAN('s', "stats", &stats, "print statistics"),
+	OPT_BOOLEAN('d', "duplicate", &validate_dup, "duplicate validation for vmlinux.o"),
+	OPT_BOOLEAN('l', "vmlinux", &vmlinux, "vmlinux.o validation"),
 	OPT_END(),
 };
 
 int cmd_check(int argc, const char **argv)
 {
-	const char *objname;
+	const char *objname, *s;
 
 	argc = parse_options(argc, argv, check_options, check_usage, 0);
 
@@ -46,5 +49,9 @@ int cmd_check(int argc, const char **arg
 
 	objname = argv[0];
 
+	s = strstr(objname, "vmlinux.o");
+	if (s && !s[9])
+		vmlinux = true;
+
 	return check(objname, false);
 }
--- a/tools/objtool/builtin.h
+++ b/tools/objtool/builtin.h
@@ -8,7 +8,7 @@
 #include <subcmd/parse-options.h>
 
 extern const struct option check_options[];
-extern bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats;
+extern bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats, validate_dup, vmlinux;
 
 extern int cmd_check(int argc, const char **argv);
 extern int cmd_orc(int argc, const char **argv);
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -257,6 +257,9 @@ static int decode_instructions(struct ob
 		    strncmp(sec->name, ".discard.", 9))
 			sec->text = true;
 
+		if (!strcmp(sec->name, ".noinstr.text"))
+			sec->noinstr = true;
+
 		for (offset = 0; offset < sec->len; offset += insn->len) {
 			insn = malloc(sizeof(*insn));
 			if (!insn) {
@@ -1355,6 +1358,53 @@ static int read_retpoline_hints(struct o
 	return 0;
 }
 
+static int read_instr_hints(struct objtool_file *file)
+{
+	struct section *sec;
+	struct instruction *insn;
+	struct rela *rela;
+
+	sec = find_section_by_name(file->elf, ".rela.discard.instr_end");
+	if (!sec)
+		return 0;
+
+	list_for_each_entry(rela, &sec->rela_list, list) {
+		if (rela->sym->type != STT_SECTION) {
+			WARN("unexpected relocation symbol type in %s", sec->name);
+			return -1;
+		}
+
+		insn = find_insn(file, rela->sym->sec, rela->addend);
+		if (!insn) {
+			WARN("bad .discard.instr_end entry");
+			return -1;
+		}
+
+		insn->instr--;
+	}
+
+	sec = find_section_by_name(file->elf, ".rela.discard.instr_begin");
+	if (!sec)
+		return 0;
+
+	list_for_each_entry(rela, &sec->rela_list, list) {
+		if (rela->sym->type != STT_SECTION) {
+			WARN("unexpected relocation symbol type in %s", sec->name);
+			return -1;
+		}
+
+		insn = find_insn(file, rela->sym->sec, rela->addend);
+		if (!insn) {
+			WARN("bad .discard.instr_begin entry");
+			return -1;
+		}
+
+		insn->instr++;
+	}
+
+	return 0;
+}
+
 static void mark_rodata(struct objtool_file *file)
 {
 	struct section *sec;
@@ -1426,6 +1476,10 @@ static int decode_sections(struct objtoo
 	if (ret)
 		return ret;
 
+	ret = read_instr_hints(file);
+	if (ret)
+		return ret;
+
 	return 0;
 }
 
@@ -1982,6 +2036,13 @@ static inline const char *call_dest_name
 
 static int validate_call(struct instruction *insn, struct insn_state *state)
 {
+	if (state->noinstr && state->instr <= 0 &&
+	    (!insn->call_dest || insn->call_dest->sec != insn->sec)) {
+		WARN_FUNC("call to %s() leaves .noinstr.text section",
+				insn->sec, insn->offset, call_dest_name(insn));
+		return 1;
+	}
+
 	if (state->uaccess && !func_uaccess_safe(insn->call_dest)) {
 		WARN_FUNC("call to %s() with UACCESS enabled",
 				insn->sec, insn->offset, call_dest_name(insn));
@@ -2010,6 +2071,12 @@ static int validate_sibling_call(struct
 
 static int validate_return(struct symbol *func, struct instruction *insn, struct insn_state *state)
 {
+	if (state->noinstr && state->instr > 0) {
+		WARN_FUNC("return with instrumentation enabled",
+			  insn->sec, insn->offset);
+		return 1;
+	}
+
 	if (state->uaccess && !func_uaccess_safe(func)) {
 		WARN_FUNC("return with UACCESS enabled",
 			  insn->sec, insn->offset);
@@ -2124,6 +2191,9 @@ static int validate_branch(struct objtoo
 				return 0;
 		}
 
+		if (state.noinstr)
+			state.instr += insn->instr;
+
 		if (insn->hint) {
 			ret = apply_insn_hint(file, sec, func, insn, &state.cfi);
 			if (ret)
@@ -2434,6 +2504,14 @@ static int validate_section(struct objto
 	       CFI_NUM_REGS * sizeof(struct cfi_reg));
 	state.cfi.stack_size = initial_func_cfi.cfa.offset;
 
+	/*
+	 * We need the full vmlinux for noinstr validation, otherwise we can
+	 * not correctly determine insn->call_dest->sec (external symbols do
+	 * not have a section).
+	 */
+	if (vmlinux)
+		state.noinstr = sec->noinstr;
+
 	list_for_each_entry(func, &sec->symbol_list, list) {
 		if (func->type != STT_FUNC)
 			continue;
@@ -2462,6 +2540,17 @@ static int validate_section(struct objto
 	return warnings;
 }
 
+static int validate_vmlinux_functions(struct objtool_file *file)
+{
+	struct section *sec;
+
+	sec = find_section_by_name(file->elf, ".noinstr.text");
+	if (!sec)
+		return 0;
+
+	return validate_section(file, sec);
+}
+
 static int validate_functions(struct objtool_file *file)
 {
 	struct section *sec;
@@ -2519,6 +2608,15 @@ int check(const char *_objname, bool orc
 	if (list_empty(&file.insn_list))
 		goto out;
 
+	if (vmlinux && !validate_dup) {
+		ret = validate_vmlinux_functions(&file);
+		if (ret < 0)
+			goto out;
+
+		warnings += ret;
+		goto out;
+	}
+
 	if (retpoline) {
 		ret = validate_retpoline(&file);
 		if (ret < 0)
--- a/tools/objtool/check.h
+++ b/tools/objtool/check.h
@@ -18,6 +18,8 @@ struct insn_state {
 	unsigned int uaccess_stack;
 	bool uaccess;
 	bool df;
+	bool noinstr;
+	s8 instr;
 };
 
 struct instruction {
@@ -30,6 +32,7 @@ struct instruction {
 	unsigned long immediate;
 	bool alt_group, dead_end, ignore, hint, save, restore, ignore_alts;
 	bool retpoline_safe;
+	s8 instr;
 	u8 visited;
 	struct symbol *call_dest;
 	struct instruction *jump_dest;
--- a/tools/objtool/elf.h
+++ b/tools/objtool/elf.h
@@ -39,7 +39,7 @@ struct section {
 	char *name;
 	int idx;
 	unsigned int len;
-	bool changed, text, rodata;
+	bool changed, text, rodata, noinstr;
 };
 
 struct symbol {

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 26/26] objtool: Add STT_NOTYPE noinstr validation
  2020-03-25 14:42       ` Josh Poimboeuf
@ 2020-03-25 15:53         ` Peter Zijlstra
  2020-03-25 16:40           ` Josh Poimboeuf
  0 siblings, 1 reply; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-25 15:53 UTC (permalink / raw)
  To: Josh Poimboeuf; +Cc: tglx, linux-kernel, x86, mhiramat, mbenes, brgerst

On Wed, Mar 25, 2020 at 09:42:11AM -0500, Josh Poimboeuf wrote:
> Sure, but couldn't validate_unwind_hints() and
> validate_reachable_instructions() be changed to *only* run on
> .noinstr.text, for the vmlinux case?  That might help converge the
> vmlinux and !vmlinux paths.

You're thinking something like so then?

--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -2421,24 +2421,34 @@ static int validate_branch(struct objtoo
 	return 0;
 }
 
-static int validate_unwind_hints(struct objtool_file *file)
+static int validate_unwind_hints(struct objtool_file *file, struct section *sec)
 {
 	struct instruction *insn;
-	int ret, warnings = 0;
 	struct insn_state state;
+	int ret, warnings = 0;
 
 	if (!file->hints)
 		return 0;
 
 	clear_insn_state(&state);
 
-	for_each_insn(file, insn) {
+	if (sec) {
+		insn = find_insn(file, sec, 0);
+		if (!insn)
+			return 0;
+	} else {
+		insn = list_first_entry(&file->insn_list, typeof(*insn), list);
+	}
+
+	while (&insn->list != &file->insn_list && (!sec || insn->sec == sec)) {
 		if (insn->hint && !insn->visited) {
 			ret = validate_branch(file, insn->func, insn, state);
 			if (ret && backtrace)
 				BT_FUNC("<=== (hint)", insn);
 			warnings += ret;
 		}
+
+		insn = list_next_entry(insn, list);
 	}
 
 	return warnings;
@@ -2622,12 +2632,16 @@ static int validate_section(struct objto
 static int validate_vmlinux_functions(struct objtool_file *file)
 {
 	struct section *sec;
+	int warnings = 0;
 
 	sec = find_section_by_name(file->elf, ".noinstr.text");
 	if (!sec)
 		return 0;
 
-	return validate_section(file, sec);
+	warnings += validate_section(file, sec);
+	warnings += validate_unwind_hints(file, sec);
+
+	return warnings;
 }
 
 static int validate_functions(struct objtool_file *file)
@@ -2712,7 +2726,7 @@ int check(const char *_objname, bool orc
 		goto out;
 	warnings += ret;
 
-	ret = validate_unwind_hints(&file);
+	ret = validate_unwind_hints(&file, NULL);
 	if (ret < 0)
 		goto out;
 	warnings += ret;

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 26/26] objtool: Add STT_NOTYPE noinstr validation
  2020-03-25 15:53         ` Peter Zijlstra
@ 2020-03-25 16:40           ` Josh Poimboeuf
  2020-03-25 16:50             ` Peter Zijlstra
  0 siblings, 1 reply; 89+ messages in thread
From: Josh Poimboeuf @ 2020-03-25 16:40 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: tglx, linux-kernel, x86, mhiramat, mbenes, brgerst

On Wed, Mar 25, 2020 at 04:53:48PM +0100, Peter Zijlstra wrote:
> On Wed, Mar 25, 2020 at 09:42:11AM -0500, Josh Poimboeuf wrote:
> > Sure, but couldn't validate_unwind_hints() and
> > validate_reachable_instructions() be changed to *only* run on
> > .noinstr.text, for the vmlinux case?  That might help converge the
> > vmlinux and !vmlinux paths.
> 
> You're thinking something like so then?

Not exactly.  But I don't want to keep churning this patch set.  I can
add more patches later, so don't worry about it.

But I was thinking it would eventually be good to have the top-level
check() be like

	sec = NULL;
	if (!validate_all)
		sec = find_section_by_name(file->elf, ".noinstr.text");

	ret = validate_functions(&file, sec);
	if (ret < 0)
		goto out;
	warnings += ret;

	ret = validate_unwind_hints(&file, sec);
	if (ret < 0)
		goto out;
	warnings += ret;

	if (!warnings) {
		ret = validate_reachable_instructions(&file, sec);
		if (ret < 0)
			goto out;
		warnings += ret;
	}

	if (!validate_all)
		goto out;

	ret = validate_retpoline(&file);
	....

that way the general flow is the same regardless...

-- 
Josh


^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 26/26] objtool: Add STT_NOTYPE noinstr validation
  2020-03-25 16:40           ` Josh Poimboeuf
@ 2020-03-25 16:50             ` Peter Zijlstra
  2020-03-26  8:01               ` Julien Thierry
  0 siblings, 1 reply; 89+ messages in thread
From: Peter Zijlstra @ 2020-03-25 16:50 UTC (permalink / raw)
  To: Josh Poimboeuf
  Cc: tglx, linux-kernel, x86, mhiramat, mbenes, brgerst, jthierry

On Wed, Mar 25, 2020 at 11:40:46AM -0500, Josh Poimboeuf wrote:
> On Wed, Mar 25, 2020 at 04:53:48PM +0100, Peter Zijlstra wrote:
> > On Wed, Mar 25, 2020 at 09:42:11AM -0500, Josh Poimboeuf wrote:
> > > Sure, but couldn't validate_unwind_hints() and
> > > validate_reachable_instructions() be changed to *only* run on
> > > .noinstr.text, for the vmlinux case?  That might help converge the
> > > vmlinux and !vmlinux paths.
> > 
> > You're thinking something like so then?
> 
> Not exactly.  But I don't want to keep churning this patch set.  I can
> add more patches later, so don't worry about it.
> 
> But I was thinking it would eventually be good to have the top-level
> check() be like
> 
> 	sec = NULL;
> 	if (!validate_all)
> 		sec = find_section_by_name(file->elf, ".noinstr.text");
> 
> 	ret = validate_functions(&file, sec);
> 	if (ret < 0)
> 		goto out;
> 	warnings += ret;
> 
> 	ret = validate_unwind_hints(&file, sec);
> 	if (ret < 0)
> 		goto out;
> 	warnings += ret;
> 
> 	if (!warnings) {
> 		ret = validate_reachable_instructions(&file, sec);
> 		if (ret < 0)
> 			goto out;
> 		warnings += ret;
> 	}
> 
> 	if (!validate_all)
> 		goto out;
> 
> 	ret = validate_retpoline(&file);
> 	....
> 
> that way the general flow is the same regardless...

Ah,... I see what you mean, there's two little wrinkles with that:

 - validate_reachable_instructions() is strictly superluous, it does no
   additional validation between the !vmlinux and vmlinux mode, so I'd
   put that if (!validate_all) goto out, one up.

 - tglx has a patch adding .entry.text to be considered as noinstr as a
   whole, which has:


@@ -2636,11 +2637,16 @@ static int validate_vmlinux_functions(st
 	int warnings = 0;
 
 	sec = find_section_by_name(file->elf, ".noinstr.text");
-	if (!sec)
-		return 0;
+	if (sec) {
+		warnings += validate_section(file, sec);
+		warnings += validate_unwind_hints(file, sec);
+	}
 
-	warnings += validate_section(file, sec);
-	warnings += validate_unwind_hints(file, sec);
+	sec = find_section_by_name(file->elf, ".entry.text");
+	if (sec) {
+		warnings += validate_section(file, sec);
+		warnings += validate_unwind_hints(file, sec);
+	}
 
 	return warnings;
 }

Anyway, yes, we can do this on top.

I was going to commit the first 17 patches to tip/core/objtool and
repost the remaining 13 once more. And then see how much pain I did to
Julien's patches :-)

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 26/26] objtool: Add STT_NOTYPE noinstr validation
  2020-03-25 16:50             ` Peter Zijlstra
@ 2020-03-26  8:01               ` Julien Thierry
  0 siblings, 0 replies; 89+ messages in thread
From: Julien Thierry @ 2020-03-26  8:01 UTC (permalink / raw)
  To: Peter Zijlstra, Josh Poimboeuf
  Cc: tglx, linux-kernel, x86, mhiramat, mbenes, brgerst



On 3/25/20 4:50 PM, Peter Zijlstra wrote:
> On Wed, Mar 25, 2020 at 11:40:46AM -0500, Josh Poimboeuf wrote:
>> On Wed, Mar 25, 2020 at 04:53:48PM +0100, Peter Zijlstra wrote:
>>> On Wed, Mar 25, 2020 at 09:42:11AM -0500, Josh Poimboeuf wrote:
>>>> Sure, but couldn't validate_unwind_hints() and
>>>> validate_reachable_instructions() be changed to *only* run on
>>>> .noinstr.text, for the vmlinux case?  That might help converge the
>>>> vmlinux and !vmlinux paths.
>>>
>>> You're thinking something like so then?
>>
>> Not exactly.  But I don't want to keep churning this patch set.  I can
>> add more patches later, so don't worry about it.
>>
>> But I was thinking it would eventually be good to have the top-level
>> check() be like
>>
>> 	sec = NULL;
>> 	if (!validate_all)
>> 		sec = find_section_by_name(file->elf, ".noinstr.text");
>>
>> 	ret = validate_functions(&file, sec);
>> 	if (ret < 0)
>> 		goto out;
>> 	warnings += ret;
>>
>> 	ret = validate_unwind_hints(&file, sec);
>> 	if (ret < 0)
>> 		goto out;
>> 	warnings += ret;
>>
>> 	if (!warnings) {
>> 		ret = validate_reachable_instructions(&file, sec);
>> 		if (ret < 0)
>> 			goto out;
>> 		warnings += ret;
>> 	}
>>
>> 	if (!validate_all)
>> 		goto out;
>>
>> 	ret = validate_retpoline(&file);
>> 	....
>>
>> that way the general flow is the same regardless...
> 
> Ah,... I see what you mean, there's two little wrinkles with that:
> 
>   - validate_reachable_instructions() is strictly superluous, it does no
>     additional validation between the !vmlinux and vmlinux mode, so I'd
>     put that if (!validate_all) goto out, one up.
> 
>   - tglx has a patch adding .entry.text to be considered as noinstr as a
>     whole, which has:
> 
> 
> @@ -2636,11 +2637,16 @@ static int validate_vmlinux_functions(st
>   	int warnings = 0;
>   
>   	sec = find_section_by_name(file->elf, ".noinstr.text");
> -	if (!sec)
> -		return 0;
> +	if (sec) {
> +		warnings += validate_section(file, sec);
> +		warnings += validate_unwind_hints(file, sec);
> +	}
>   
> -	warnings += validate_section(file, sec);
> -	warnings += validate_unwind_hints(file, sec);
> +	sec = find_section_by_name(file->elf, ".entry.text");
> +	if (sec) {
> +		warnings += validate_section(file, sec);
> +		warnings += validate_unwind_hints(file, sec);
> +	}
>   
>   	return warnings;
>   }
> 
> Anyway, yes, we can do this on top.
> 
> I was going to commit the first 17 patches to tip/core/objtool and
> repost the remaining 13 once more. And then see how much pain I did to
> Julien's patches :-)
> 

I appreciate the concideration for my patches but, since your patches 
have been posted a few times already and were already reviewed, you 
might as well commit them. I'll rebase my patches on top and see the 
state of things (I'll need to do that with the whole arm64 series anyway).

I'll try to post the new version fast enough so I'm not behind some 
other major objtool changes :) .

In the mean time I'll have a look at this series and see what I might 
have to change for the rest of the arm64-objtool set!

Thanks,

-- 
Julien Thierry


^ permalink raw reply	[flat|nested] 89+ messages in thread

* [tip: core/objtool] objtool: Re-arrange validate_functions()
  2020-03-24 15:31 ` [PATCH v3 17/26] objtool: Re-arrange validate_functions() Peter Zijlstra
  2020-03-24 21:10   ` Josh Poimboeuf
  2020-03-25 12:22   ` Miroslav Benes
@ 2020-03-26 10:08   ` tip-bot2 for Peter Zijlstra
  2 siblings, 0 replies; 89+ messages in thread
From: tip-bot2 for Peter Zijlstra @ 2020-03-26 10:08 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Peter Zijlstra (Intel), Miroslav Benes, Josh Poimboeuf, x86, LKML

The following commit has been merged into the core/objtool branch of tip:

Commit-ID:     350994bf95414d6da67a72f27d7ac3832ce3725d
Gitweb:        https://git.kernel.org/tip/350994bf95414d6da67a72f27d7ac3832ce3725d
Author:        Peter Zijlstra <peterz@infradead.org>
AuthorDate:    Mon, 23 Mar 2020 20:57:13 +01:00
Committer:     Peter Zijlstra <peterz@infradead.org>
CommitterDate: Wed, 25 Mar 2020 18:28:31 +01:00

objtool: Re-arrange validate_functions()

In preparation to adding a vmlinux.o specific pass, rearrange some
code. No functional changes intended.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lkml.kernel.org/r/20200324160924.924304616@infradead.org
---
 tools/objtool/check.c | 52 ++++++++++++++++++++++++------------------
 1 file changed, 30 insertions(+), 22 deletions(-)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 0c9c9ad..0bfcb39 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -2395,9 +2395,8 @@ static bool ignore_unreachable_insn(struct instruction *insn)
 	return false;
 }
 
-static int validate_functions(struct objtool_file *file)
+static int validate_section(struct objtool_file *file, struct section *sec)
 {
-	struct section *sec;
 	struct symbol *func;
 	struct instruction *insn;
 	struct insn_state state;
@@ -2410,36 +2409,45 @@ static int validate_functions(struct objtool_file *file)
 	       CFI_NUM_REGS * sizeof(struct cfi_reg));
 	state.stack_size = initial_func_cfi.cfa.offset;
 
-	for_each_sec(file, sec) {
-		list_for_each_entry(func, &sec->symbol_list, list) {
-			if (func->type != STT_FUNC)
-				continue;
+	list_for_each_entry(func, &sec->symbol_list, list) {
+		if (func->type != STT_FUNC)
+			continue;
 
-			if (!func->len) {
-				WARN("%s() is missing an ELF size annotation",
-				     func->name);
-				warnings++;
-			}
+		if (!func->len) {
+			WARN("%s() is missing an ELF size annotation",
+			     func->name);
+			warnings++;
+		}
 
-			if (func->pfunc != func || func->alias != func)
-				continue;
+		if (func->pfunc != func || func->alias != func)
+			continue;
 
-			insn = find_insn(file, sec, func->offset);
-			if (!insn || insn->ignore || insn->visited)
-				continue;
+		insn = find_insn(file, sec, func->offset);
+		if (!insn || insn->ignore || insn->visited)
+			continue;
 
-			state.uaccess = func->uaccess_safe;
+		state.uaccess = func->uaccess_safe;
 
-			ret = validate_branch(file, func, insn, state);
-			if (ret && backtrace)
-				BT_FUNC("<=== (func)", insn);
-			warnings += ret;
-		}
+		ret = validate_branch(file, func, insn, state);
+		if (ret && backtrace)
+			BT_FUNC("<=== (func)", insn);
+		warnings += ret;
 	}
 
 	return warnings;
 }
 
+static int validate_functions(struct objtool_file *file)
+{
+	struct section *sec;
+	int warnings = 0;
+
+	for_each_sec(file, sec)
+		warnings += validate_section(file, sec);
+
+	return warnings;
+}
+
 static int validate_reachable_instructions(struct objtool_file *file)
 {
 	struct instruction *insn;

^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [tip: core/objtool] objtool: Delete cleanup()
  2020-03-24 15:31 ` [PATCH v3 15/26] objtool: Delete cleanup() Peter Zijlstra
  2020-03-25 12:11   ` Miroslav Benes
@ 2020-03-26 10:08   ` tip-bot2 for Peter Zijlstra
  1 sibling, 0 replies; 89+ messages in thread
From: tip-bot2 for Peter Zijlstra @ 2020-03-26 10:08 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Peter Zijlstra (Intel), Miroslav Benes, Josh Poimboeuf, x86, LKML

The following commit has been merged into the core/objtool branch of tip:

Commit-ID:     8887a86eddd93ca396ca35f7b41fb14ed412f85d
Gitweb:        https://git.kernel.org/tip/8887a86eddd93ca396ca35f7b41fb14ed412f85d
Author:        Peter Zijlstra <peterz@infradead.org>
AuthorDate:    Wed, 11 Mar 2020 23:07:42 +01:00
Committer:     Peter Zijlstra <peterz@infradead.org>
CommitterDate: Wed, 25 Mar 2020 18:28:30 +01:00

objtool: Delete cleanup()

Perf shows we spend a measurable amount of time spend cleaning up
right before we exit anyway. Avoid the needsless work and just
terminate.

This reduces objtool on vmlinux.o runtime from 5.4s to 4.8s

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lkml.kernel.org/r/20200324160924.800720170@infradead.org
---
 tools/objtool/check.c | 19 -------------------
 1 file changed, 19 deletions(-)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 54a6043..0c9c9ad 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -2458,23 +2458,6 @@ static int validate_reachable_instructions(struct objtool_file *file)
 	return 0;
 }
 
-static void cleanup(struct objtool_file *file)
-{
-	struct instruction *insn, *tmpinsn;
-	struct alternative *alt, *tmpalt;
-
-	list_for_each_entry_safe(insn, tmpinsn, &file->insn_list, list) {
-		list_for_each_entry_safe(alt, tmpalt, &insn->alts, list) {
-			list_del(&alt->list);
-			free(alt);
-		}
-		list_del(&insn->list);
-		hash_del(&insn->hash);
-		free(insn);
-	}
-	elf_close(file->elf);
-}
-
 static struct objtool_file file;
 
 int check(const char *_objname, bool orc)
@@ -2542,8 +2525,6 @@ int check(const char *_objname, bool orc)
 	}
 
 out:
-	cleanup(&file);
-
 	if (ret < 0) {
 		/*
 		 *  Fatal error.  The binary is corrupt or otherwise broken in

^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [tip: core/objtool] objtool: Optimize find_rela_by_dest_range()
  2020-03-24 15:31 ` [PATCH v3 16/26] objtool: Optimize find_rela_by_dest_range() Peter Zijlstra
  2020-03-25 12:19   ` Miroslav Benes
@ 2020-03-26 10:08   ` tip-bot2 for Peter Zijlstra
  1 sibling, 0 replies; 89+ messages in thread
From: tip-bot2 for Peter Zijlstra @ 2020-03-26 10:08 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Peter Zijlstra (Intel), Miroslav Benes, Josh Poimboeuf, x86, LKML

The following commit has been merged into the core/objtool branch of tip:

Commit-ID:     74b873e49d92f90deb41d1a2a8fbb70328aebd67
Gitweb:        https://git.kernel.org/tip/74b873e49d92f90deb41d1a2a8fbb70328aebd67
Author:        Peter Zijlstra <peterz@infradead.org>
AuthorDate:    Thu, 12 Mar 2020 11:30:50 +01:00
Committer:     Peter Zijlstra <peterz@infradead.org>
CommitterDate: Wed, 25 Mar 2020 18:28:31 +01:00

objtool: Optimize find_rela_by_dest_range()

Perf shows there is significant time in find_rela_by_dest(); this is
because we have to iterate the address space per byte, looking for
relocation entries.

Optimize this by reducing the address space granularity.

This reduces objtool on vmlinux.o runtime from 4.8 to 4.4 seconds.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lkml.kernel.org/r/20200324160924.861321325@infradead.org
---
 tools/objtool/elf.c | 15 +++++++++++----
 tools/objtool/elf.h | 16 +++++++++++++++-
 2 files changed, 26 insertions(+), 5 deletions(-)

diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index 8a0a1bc..09ddc8f 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -215,7 +215,7 @@ struct symbol *find_symbol_by_name(struct elf *elf, const char *name)
 struct rela *find_rela_by_dest_range(struct elf *elf, struct section *sec,
 				     unsigned long offset, unsigned int len)
 {
-	struct rela *rela;
+	struct rela *rela, *r = NULL;
 	unsigned long o;
 
 	if (!sec->rela)
@@ -223,12 +223,19 @@ struct rela *find_rela_by_dest_range(struct elf *elf, struct section *sec,
 
 	sec = sec->rela;
 
-	for (o = offset; o < offset + len; o++) {
+	for_offset_range(o, offset, offset + len) {
 		hash_for_each_possible(elf->rela_hash, rela, hash,
 				       sec_offset_hash(sec, o)) {
-			if (rela->sec == sec && rela->offset == o)
-				return rela;
+			if (rela->sec != sec)
+				continue;
+
+			if (rela->offset >= offset && rela->offset < offset + len) {
+				if (!r || rela->offset < r->offset)
+					r = rela;
+			}
 		}
+		if (r)
+			return r;
 	}
 
 	return NULL;
diff --git a/tools/objtool/elf.h b/tools/objtool/elf.h
index dfd2431..ebbb10c 100644
--- a/tools/objtool/elf.h
+++ b/tools/objtool/elf.h
@@ -83,9 +83,23 @@ struct elf {
 	DECLARE_HASHTABLE(rela_hash, 20);
 };
 
+#define OFFSET_STRIDE_BITS	4
+#define OFFSET_STRIDE		(1UL << OFFSET_STRIDE_BITS)
+#define OFFSET_STRIDE_MASK	(~(OFFSET_STRIDE - 1))
+
+#define for_offset_range(_offset, _start, _end)		\
+	for (_offset = ((_start) & OFFSET_STRIDE_MASK);	\
+	     _offset <= ((_end) & OFFSET_STRIDE_MASK);	\
+	     _offset += OFFSET_STRIDE)
+
 static inline u32 sec_offset_hash(struct section *sec, unsigned long offset)
 {
-	u32 ol = offset, oh = offset >> 32, idx = sec->idx;
+	u32 ol, oh, idx = sec->idx;
+
+	offset &= OFFSET_STRIDE_MASK;
+
+	ol = offset;
+	oh = offset >> 32;
 
 	__jhash_mix(ol, oh, idx);
 

^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [tip: core/objtool] objtool: Optimize read_sections()
  2020-03-24 15:31 ` [PATCH v3 14/26] objtool: Optimize read_sections() Peter Zijlstra
  2020-03-25 12:10   ` Miroslav Benes
@ 2020-03-26 10:08   ` tip-bot2 for Peter Zijlstra
  2020-04-21 14:47   ` [PATCH v3 14/26] " youling257
  2 siblings, 0 replies; 89+ messages in thread
From: tip-bot2 for Peter Zijlstra @ 2020-03-26 10:08 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Peter Zijlstra (Intel), Miroslav Benes, Josh Poimboeuf, x86, LKML

The following commit has been merged into the core/objtool branch of tip:

Commit-ID:     8b5fa6bc326bf02f293b5a39a8f5b3de816265d3
Gitweb:        https://git.kernel.org/tip/8b5fa6bc326bf02f293b5a39a8f5b3de816265d3
Author:        Peter Zijlstra <peterz@infradead.org>
AuthorDate:    Thu, 12 Mar 2020 11:23:36 +01:00
Committer:     Peter Zijlstra <peterz@infradead.org>
CommitterDate: Wed, 25 Mar 2020 18:28:30 +01:00

objtool: Optimize read_sections()

Perf showed that __hash_init() is a significant portion of
read_sections(), so instead of doing a per section rela_hash, use an
elf-wide rela_hash.

Statistics show us there are about 1.1 million relas, so size it
accordingly.

This reduces the objtool on vmlinux.o runtime to a third, from 15 to 5
seconds.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lkml.kernel.org/r/20200324160924.739153726@infradead.org
---
 tools/objtool/check.c   | 18 +++++++++---------
 tools/objtool/elf.c     | 24 ++++++++++++++----------
 tools/objtool/elf.h     | 21 +++++++++++++++++----
 tools/objtool/orc_gen.c |  9 +++++----
 tools/objtool/special.c |  4 ++--
 5 files changed, 47 insertions(+), 29 deletions(-)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 6df1bae..54a6043 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -569,8 +569,8 @@ static int add_jump_destinations(struct objtool_file *file)
 		if (insn->ignore || insn->offset == FAKE_JUMP_OFFSET)
 			continue;
 
-		rela = find_rela_by_dest_range(insn->sec, insn->offset,
-					       insn->len);
+		rela = find_rela_by_dest_range(file->elf, insn->sec,
+					       insn->offset, insn->len);
 		if (!rela) {
 			dest_sec = insn->sec;
 			dest_off = insn->offset + insn->len + insn->immediate;
@@ -666,8 +666,8 @@ static int add_call_destinations(struct objtool_file *file)
 		if (insn->type != INSN_CALL)
 			continue;
 
-		rela = find_rela_by_dest_range(insn->sec, insn->offset,
-					       insn->len);
+		rela = find_rela_by_dest_range(file->elf, insn->sec,
+					       insn->offset, insn->len);
 		if (!rela) {
 			dest_off = insn->offset + insn->len + insn->immediate;
 			insn->call_dest = find_func_by_offset(insn->sec, dest_off);
@@ -796,7 +796,7 @@ static int handle_group_alt(struct objtool_file *file,
 		 */
 		if ((insn->offset != special_alt->new_off ||
 		    (insn->type != INSN_CALL && !is_static_jump(insn))) &&
-		    find_rela_by_dest_range(insn->sec, insn->offset, insn->len)) {
+		    find_rela_by_dest_range(file->elf, insn->sec, insn->offset, insn->len)) {
 
 			WARN_FUNC("unsupported relocation in alternatives section",
 				  insn->sec, insn->offset);
@@ -1066,8 +1066,8 @@ static struct rela *find_jump_table(struct objtool_file *file,
 		    break;
 
 		/* look for a relocation which references .rodata */
-		text_rela = find_rela_by_dest_range(insn->sec, insn->offset,
-						    insn->len);
+		text_rela = find_rela_by_dest_range(file->elf, insn->sec,
+						    insn->offset, insn->len);
 		if (!text_rela || text_rela->sym->type != STT_SECTION ||
 		    !text_rela->sym->sec->rodata)
 			continue;
@@ -1096,7 +1096,7 @@ static struct rela *find_jump_table(struct objtool_file *file,
 		 * should reference text in the same function as the original
 		 * instruction.
 		 */
-		table_rela = find_rela_by_dest(table_sec, table_offset);
+		table_rela = find_rela_by_dest(file->elf, table_sec, table_offset);
 		if (!table_rela)
 			continue;
 		dest_insn = find_insn(file, table_rela->sym->sec, table_rela->addend);
@@ -1232,7 +1232,7 @@ static int read_unwind_hints(struct objtool_file *file)
 	for (i = 0; i < sec->len / sizeof(struct unwind_hint); i++) {
 		hint = (struct unwind_hint *)sec->data->d_buf + i;
 
-		rela = find_rela_by_dest(sec, i * sizeof(*hint));
+		rela = find_rela_by_dest(file->elf, sec, i * sizeof(*hint));
 		if (!rela) {
 			WARN("can't find rela for unwind_hints[%d]", i);
 			return -1;
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index 43abae7..8a0a1bc 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -212,8 +212,8 @@ struct symbol *find_symbol_by_name(struct elf *elf, const char *name)
 	return NULL;
 }
 
-struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset,
-				     unsigned int len)
+struct rela *find_rela_by_dest_range(struct elf *elf, struct section *sec,
+				     unsigned long offset, unsigned int len)
 {
 	struct rela *rela;
 	unsigned long o;
@@ -221,17 +221,22 @@ struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset,
 	if (!sec->rela)
 		return NULL;
 
-	for (o = offset; o < offset + len; o++)
-		hash_for_each_possible(sec->rela->rela_hash, rela, hash, o)
-			if (rela->offset == o)
+	sec = sec->rela;
+
+	for (o = offset; o < offset + len; o++) {
+		hash_for_each_possible(elf->rela_hash, rela, hash,
+				       sec_offset_hash(sec, o)) {
+			if (rela->sec == sec && rela->offset == o)
 				return rela;
+		}
+	}
 
 	return NULL;
 }
 
-struct rela *find_rela_by_dest(struct section *sec, unsigned long offset)
+struct rela *find_rela_by_dest(struct elf *elf, struct section *sec, unsigned long offset)
 {
-	return find_rela_by_dest_range(sec, offset, 1);
+	return find_rela_by_dest_range(elf, sec, offset, 1);
 }
 
 static int read_sections(struct elf *elf)
@@ -261,7 +266,6 @@ static int read_sections(struct elf *elf)
 
 		INIT_LIST_HEAD(&sec->symbol_list);
 		INIT_LIST_HEAD(&sec->rela_list);
-		hash_init(sec->rela_hash);
 
 		s = elf_getscn(elf->elf, i);
 		if (!s) {
@@ -493,7 +497,7 @@ static int read_relas(struct elf *elf)
 			}
 
 			list_add_tail(&rela->list, &sec->rela_list);
-			hash_add(sec->rela_hash, &rela->hash, rela->offset);
+			hash_add(elf->rela_hash, &rela->hash, rela_hash(rela));
 			nr_rela++;
 		}
 		max_rela = max(max_rela, nr_rela);
@@ -526,6 +530,7 @@ struct elf *elf_read(const char *name, int flags)
 	hash_init(elf->symbol_name_hash);
 	hash_init(elf->section_hash);
 	hash_init(elf->section_name_hash);
+	hash_init(elf->rela_hash);
 	INIT_LIST_HEAD(&elf->sections);
 
 	elf->fd = open(name, flags);
@@ -586,7 +591,6 @@ struct section *elf_create_section(struct elf *elf, const char *name,
 
 	INIT_LIST_HEAD(&sec->symbol_list);
 	INIT_LIST_HEAD(&sec->rela_list);
-	hash_init(sec->rela_hash);
 
 	s = elf_newscn(elf->elf);
 	if (!s) {
diff --git a/tools/objtool/elf.h b/tools/objtool/elf.h
index 3088d92..dfd2431 100644
--- a/tools/objtool/elf.h
+++ b/tools/objtool/elf.h
@@ -33,7 +33,6 @@ struct section {
 	struct rb_root symbol_tree;
 	struct list_head symbol_list;
 	struct list_head rela_list;
-	DECLARE_HASHTABLE(rela_hash, 16);
 	struct section *base, *rela;
 	struct symbol *sym;
 	Elf_Data *data;
@@ -81,8 +80,22 @@ struct elf {
 	DECLARE_HASHTABLE(symbol_name_hash, 20);
 	DECLARE_HASHTABLE(section_hash, 16);
 	DECLARE_HASHTABLE(section_name_hash, 16);
+	DECLARE_HASHTABLE(rela_hash, 20);
 };
 
+static inline u32 sec_offset_hash(struct section *sec, unsigned long offset)
+{
+	u32 ol = offset, oh = offset >> 32, idx = sec->idx;
+
+	__jhash_mix(ol, oh, idx);
+
+	return ol;
+}
+
+static inline u32 rela_hash(struct rela *rela)
+{
+	return sec_offset_hash(rela->sec, rela->offset);
+}
 
 struct elf *elf_read(const char *name, int flags);
 struct section *find_section_by_name(struct elf *elf, const char *name);
@@ -90,9 +103,9 @@ struct symbol *find_func_by_offset(struct section *sec, unsigned long offset);
 struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset);
 struct symbol *find_symbol_by_name(struct elf *elf, const char *name);
 struct symbol *find_symbol_containing(struct section *sec, unsigned long offset);
-struct rela *find_rela_by_dest(struct section *sec, unsigned long offset);
-struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset,
-				     unsigned int len);
+struct rela *find_rela_by_dest(struct elf *elf, struct section *sec, unsigned long offset);
+struct rela *find_rela_by_dest_range(struct elf *elf, struct section *sec,
+				     unsigned long offset, unsigned int len);
 struct symbol *find_func_containing(struct section *sec, unsigned long offset);
 struct section *elf_create_section(struct elf *elf, const char *name, size_t
 				   entsize, int nr);
diff --git a/tools/objtool/orc_gen.c b/tools/objtool/orc_gen.c
index 27a4112..41e4a27 100644
--- a/tools/objtool/orc_gen.c
+++ b/tools/objtool/orc_gen.c
@@ -81,7 +81,7 @@ int create_orc(struct objtool_file *file)
 	return 0;
 }
 
-static int create_orc_entry(struct section *u_sec, struct section *ip_relasec,
+static int create_orc_entry(struct elf *elf, struct section *u_sec, struct section *ip_relasec,
 				unsigned int idx, struct section *insn_sec,
 				unsigned long insn_off, struct orc_entry *o)
 {
@@ -109,9 +109,10 @@ static int create_orc_entry(struct section *u_sec, struct section *ip_relasec,
 	rela->addend = insn_off;
 	rela->type = R_X86_64_PC32;
 	rela->offset = idx * sizeof(int);
+	rela->sec = ip_relasec;
 
 	list_add_tail(&rela->list, &ip_relasec->rela_list);
-	hash_add(ip_relasec->rela_hash, &rela->hash, rela->offset);
+	hash_add(elf->rela_hash, &rela->hash, rela_hash(rela));
 
 	return 0;
 }
@@ -182,7 +183,7 @@ int create_orc_sections(struct objtool_file *file)
 			if (!prev_insn || memcmp(&insn->orc, &prev_insn->orc,
 						 sizeof(struct orc_entry))) {
 
-				if (create_orc_entry(u_sec, ip_relasec, idx,
+				if (create_orc_entry(file->elf, u_sec, ip_relasec, idx,
 						     insn->sec, insn->offset,
 						     &insn->orc))
 					return -1;
@@ -194,7 +195,7 @@ int create_orc_sections(struct objtool_file *file)
 
 		/* section terminator */
 		if (prev_insn) {
-			if (create_orc_entry(u_sec, ip_relasec, idx,
+			if (create_orc_entry(file->elf, u_sec, ip_relasec, idx,
 					     prev_insn->sec,
 					     prev_insn->offset + prev_insn->len,
 					     &empty))
diff --git a/tools/objtool/special.c b/tools/objtool/special.c
index fdbaa61..e74e018 100644
--- a/tools/objtool/special.c
+++ b/tools/objtool/special.c
@@ -118,7 +118,7 @@ static int get_alt_entry(struct elf *elf, struct special_entry *entry,
 		}
 	}
 
-	orig_rela = find_rela_by_dest(sec, offset + entry->orig);
+	orig_rela = find_rela_by_dest(elf, sec, offset + entry->orig);
 	if (!orig_rela) {
 		WARN_FUNC("can't find orig rela", sec, offset + entry->orig);
 		return -1;
@@ -133,7 +133,7 @@ static int get_alt_entry(struct elf *elf, struct special_entry *entry,
 	alt->orig_off = orig_rela->addend;
 
 	if (!entry->group || alt->new_len) {
-		new_rela = find_rela_by_dest(sec, offset + entry->new);
+		new_rela = find_rela_by_dest(elf, sec, offset + entry->new);
 		if (!new_rela) {
 			WARN_FUNC("can't find new rela",
 				  sec, offset + entry->new);

^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [tip: core/objtool] objtool: Optimize find_symbol_by_name()
  2020-03-24 15:31 ` [PATCH v3 13/26] objtool: Optimize find_symbol_by_name() Peter Zijlstra
  2020-03-25 10:25   ` Miroslav Benes
@ 2020-03-26 10:08   ` tip-bot2 for Peter Zijlstra
  1 sibling, 0 replies; 89+ messages in thread
From: tip-bot2 for Peter Zijlstra @ 2020-03-26 10:08 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Peter Zijlstra (Intel), Miroslav Benes, Josh Poimboeuf, x86, LKML

The following commit has been merged into the core/objtool branch of tip:

Commit-ID:     cdb3d057a17d56363a831e486ea39e4c389a6cf9
Gitweb:        https://git.kernel.org/tip/cdb3d057a17d56363a831e486ea39e4c389a6cf9
Author:        Peter Zijlstra <peterz@infradead.org>
AuthorDate:    Thu, 12 Mar 2020 10:17:38 +01:00
Committer:     Peter Zijlstra <peterz@infradead.org>
CommitterDate: Wed, 25 Mar 2020 18:28:30 +01:00

objtool: Optimize find_symbol_by_name()

Perf showed that find_symbol_by_name() takes time; add a symbol name
hash.

This shaves another second off of objtool on vmlinux.o runtime, down
to 15 seconds.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lkml.kernel.org/r/20200324160924.676865656@infradead.org
---
 tools/objtool/elf.c | 10 +++++-----
 tools/objtool/elf.h |  2 ++
 2 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index 07db4df..43abae7 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -203,13 +203,11 @@ struct symbol *find_func_containing(struct section *sec, unsigned long offset)
 
 struct symbol *find_symbol_by_name(struct elf *elf, const char *name)
 {
-	struct section *sec;
 	struct symbol *sym;
 
-	list_for_each_entry(sec, &elf->sections, list)
-		list_for_each_entry(sym, &sec->symbol_list, list)
-			if (!strcmp(sym->name, name))
-				return sym;
+	hash_for_each_possible(elf->symbol_name_hash, sym, name_hash, str_hash(name))
+		if (!strcmp(sym->name, name))
+			return sym;
 
 	return NULL;
 }
@@ -386,6 +384,7 @@ static int read_symbols(struct elf *elf)
 			entry = &sym->sec->symbol_list;
 		list_add(&sym->list, entry);
 		hash_add(elf->symbol_hash, &sym->hash, sym->idx);
+		hash_add(elf->symbol_name_hash, &sym->name_hash, str_hash(sym->name));
 	}
 
 	if (stats)
@@ -524,6 +523,7 @@ struct elf *elf_read(const char *name, int flags)
 	memset(elf, 0, sizeof(*elf));
 
 	hash_init(elf->symbol_hash);
+	hash_init(elf->symbol_name_hash);
 	hash_init(elf->section_hash);
 	hash_init(elf->section_name_hash);
 	INIT_LIST_HEAD(&elf->sections);
diff --git a/tools/objtool/elf.h b/tools/objtool/elf.h
index d18f466..3088d92 100644
--- a/tools/objtool/elf.h
+++ b/tools/objtool/elf.h
@@ -47,6 +47,7 @@ struct symbol {
 	struct list_head list;
 	struct rb_node node;
 	struct hlist_node hash;
+	struct hlist_node name_hash;
 	GElf_Sym sym;
 	struct section *sec;
 	char *name;
@@ -77,6 +78,7 @@ struct elf {
 	char *name;
 	struct list_head sections;
 	DECLARE_HASHTABLE(symbol_hash, 20);
+	DECLARE_HASHTABLE(symbol_name_hash, 20);
 	DECLARE_HASHTABLE(section_hash, 16);
 	DECLARE_HASHTABLE(section_name_hash, 16);
 };

^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [tip: core/objtool] objtool: Resize insn_hash
  2020-03-24 15:31 ` [PATCH v3 12/26] objtool: Resize insn_hash Peter Zijlstra
  2020-03-25 10:21   ` Miroslav Benes
@ 2020-03-26 10:08   ` tip-bot2 for Peter Zijlstra
  1 sibling, 0 replies; 89+ messages in thread
From: tip-bot2 for Peter Zijlstra @ 2020-03-26 10:08 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Peter Zijlstra (Intel), Miroslav Benes, Josh Poimboeuf, x86, LKML

The following commit has been merged into the core/objtool branch of tip:

Commit-ID:     513b5ca6b5fbeb766999fb1a4bc9a55c9fb7c9ca
Gitweb:        https://git.kernel.org/tip/513b5ca6b5fbeb766999fb1a4bc9a55c9fb7c9ca
Author:        Peter Zijlstra <peterz@infradead.org>
AuthorDate:    Thu, 12 Mar 2020 10:20:46 +01:00
Committer:     Peter Zijlstra <peterz@infradead.org>
CommitterDate: Wed, 25 Mar 2020 18:28:30 +01:00

objtool: Resize insn_hash

Perf shows we're spending a lot of time in find_insn() and the
statistics show we have around 3.2 million instruction. Increase the
hash table size to reduce the bucket load from around 50 to 3.

This shaves about 2s off of objtool on vmlinux.o runtime, down to 16s.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lkml.kernel.org/r/20200324160924.617882545@infradead.org
---
 tools/objtool/check.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/objtool/check.h b/tools/objtool/check.h
index 6d875ca..f0ce8ff 100644
--- a/tools/objtool/check.h
+++ b/tools/objtool/check.h
@@ -50,7 +50,7 @@ struct instruction {
 struct objtool_file {
 	struct elf *elf;
 	struct list_head insn_list;
-	DECLARE_HASHTABLE(insn_hash, 16);
+	DECLARE_HASHTABLE(insn_hash, 20);
 	bool ignore_unreachables, c_file, hints, rodata;
 };
 

^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [tip: core/objtool] objtool: Rename find_containing_func()
  2020-03-24 15:31 ` [PATCH v3 11/26] objtool: Rename find_containing_func() Peter Zijlstra
  2020-03-25 10:21   ` Miroslav Benes
@ 2020-03-26 10:08   ` tip-bot2 for Peter Zijlstra
  1 sibling, 0 replies; 89+ messages in thread
From: tip-bot2 for Peter Zijlstra @ 2020-03-26 10:08 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Peter Zijlstra (Intel), Miroslav Benes, Josh Poimboeuf, x86, LKML

The following commit has been merged into the core/objtool branch of tip:

Commit-ID:     53d20720bbc8718ef86fdfe53dec0accfb593ef8
Gitweb:        https://git.kernel.org/tip/53d20720bbc8718ef86fdfe53dec0accfb593ef8
Author:        Peter Zijlstra <peterz@infradead.org>
AuthorDate:    Mon, 16 Mar 2020 10:36:53 +01:00
Committer:     Peter Zijlstra <peterz@infradead.org>
CommitterDate: Wed, 25 Mar 2020 18:28:29 +01:00

objtool: Rename find_containing_func()

For consistency; we have:

  find_symbol_by_offset() / find_symbol_containing()
  find_func_by_offset()   / find_containing_func()

fix that.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lkml.kernel.org/r/20200324160924.558470724@infradead.org
---
 tools/objtool/elf.c  | 2 +-
 tools/objtool/elf.h  | 2 +-
 tools/objtool/warn.h | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index 3a8b426..07db4df 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -187,7 +187,7 @@ struct symbol *find_symbol_containing(struct section *sec, unsigned long offset)
 	return NULL;
 }
 
-struct symbol *find_containing_func(struct section *sec, unsigned long offset)
+struct symbol *find_func_containing(struct section *sec, unsigned long offset)
 {
 	struct rb_node *node;
 
diff --git a/tools/objtool/elf.h b/tools/objtool/elf.h
index e4a8d68..d18f466 100644
--- a/tools/objtool/elf.h
+++ b/tools/objtool/elf.h
@@ -91,7 +91,7 @@ struct symbol *find_symbol_containing(struct section *sec, unsigned long offset)
 struct rela *find_rela_by_dest(struct section *sec, unsigned long offset);
 struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset,
 				     unsigned int len);
-struct symbol *find_containing_func(struct section *sec, unsigned long offset);
+struct symbol *find_func_containing(struct section *sec, unsigned long offset);
 struct section *elf_create_section(struct elf *elf, const char *name, size_t
 				   entsize, int nr);
 struct section *elf_create_rela_section(struct elf *elf, struct section *base);
diff --git a/tools/objtool/warn.h b/tools/objtool/warn.h
index cbb0a02..7799f60 100644
--- a/tools/objtool/warn.h
+++ b/tools/objtool/warn.h
@@ -21,7 +21,7 @@ static inline char *offstr(struct section *sec, unsigned long offset)
 	char *name, *str;
 	unsigned long name_off;
 
-	func = find_containing_func(sec, offset);
+	func = find_func_containing(sec, offset);
 	if (func) {
 		name = func->name;
 		name_off = offset - func->offset;

^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [tip: core/objtool] objtool: Optimize find_section_by_index()
  2020-03-24 15:31 ` [PATCH v3 08/26] objtool: Optimize find_section_by_index() Peter Zijlstra
  2020-03-25 10:12   ` Miroslav Benes
@ 2020-03-26 10:08   ` tip-bot2 for Peter Zijlstra
  1 sibling, 0 replies; 89+ messages in thread
From: tip-bot2 for Peter Zijlstra @ 2020-03-26 10:08 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Peter Zijlstra (Intel), Miroslav Benes, Josh Poimboeuf, x86, LKML

The following commit has been merged into the core/objtool branch of tip:

Commit-ID:     530389968739883a61192767e1c215653ba4ba2b
Gitweb:        https://git.kernel.org/tip/530389968739883a61192767e1c215653ba4ba2b
Author:        Peter Zijlstra <peterz@infradead.org>
AuthorDate:    Tue, 10 Mar 2020 18:43:35 +01:00
Committer:     Peter Zijlstra <peterz@infradead.org>
CommitterDate: Wed, 25 Mar 2020 18:28:28 +01:00

objtool: Optimize find_section_by_index()

In order to avoid a linear search (over 20k entries), add an
section_hash to the elf object.

This reduces objtool on vmlinux.o from a few minutes to around 45
seconds.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lkml.kernel.org/r/20200324160924.381249993@infradead.org
---
 tools/objtool/elf.c | 13 ++++++++-----
 tools/objtool/elf.h |  2 ++
 2 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index ff29306..9007713 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -38,7 +38,7 @@ static struct section *find_section_by_index(struct elf *elf,
 {
 	struct section *sec;
 
-	list_for_each_entry(sec, &elf->sections, list)
+	hash_for_each_possible(elf->section_hash, sec, hash, idx)
 		if (sec->idx == idx)
 			return sec;
 
@@ -166,8 +166,6 @@ static int read_sections(struct elf *elf)
 		INIT_LIST_HEAD(&sec->rela_list);
 		hash_init(sec->rela_hash);
 
-		list_add_tail(&sec->list, &elf->sections);
-
 		s = elf_getscn(elf->elf, i);
 		if (!s) {
 			WARN_ELF("elf_getscn");
@@ -201,6 +199,9 @@ static int read_sections(struct elf *elf)
 			}
 		}
 		sec->len = sec->sh.sh_size;
+
+		list_add_tail(&sec->list, &elf->sections);
+		hash_add(elf->section_hash, &sec->hash, sec->idx);
 	}
 
 	if (stats)
@@ -439,6 +440,7 @@ struct elf *elf_read(const char *name, int flags)
 	memset(elf, 0, sizeof(*elf));
 
 	hash_init(elf->symbol_hash);
+	hash_init(elf->section_hash);
 	INIT_LIST_HEAD(&elf->sections);
 
 	elf->fd = open(name, flags);
@@ -501,8 +503,6 @@ struct section *elf_create_section(struct elf *elf, const char *name,
 	INIT_LIST_HEAD(&sec->rela_list);
 	hash_init(sec->rela_hash);
 
-	list_add_tail(&sec->list, &elf->sections);
-
 	s = elf_newscn(elf->elf);
 	if (!s) {
 		WARN_ELF("elf_newscn");
@@ -579,6 +579,9 @@ struct section *elf_create_section(struct elf *elf, const char *name,
 	shstrtab->len += strlen(name) + 1;
 	shstrtab->changed = true;
 
+	list_add_tail(&sec->list, &elf->sections);
+	hash_add(elf->section_hash, &sec->hash, sec->idx);
+
 	return sec;
 }
 
diff --git a/tools/objtool/elf.h b/tools/objtool/elf.h
index 1222980..8c272eb 100644
--- a/tools/objtool/elf.h
+++ b/tools/objtool/elf.h
@@ -25,6 +25,7 @@
 
 struct section {
 	struct list_head list;
+	struct hlist_node hash;
 	GElf_Shdr sh;
 	struct list_head symbol_list;
 	struct list_head rela_list;
@@ -71,6 +72,7 @@ struct elf {
 	char *name;
 	struct list_head sections;
 	DECLARE_HASHTABLE(symbol_hash, 20);
+	DECLARE_HASHTABLE(section_hash, 16);
 };
 
 

^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [tip: core/objtool] objtool: Optimize find_symbol_*() and read_symbols()
  2020-03-24 15:31 ` [PATCH v3 10/26] objtool: Optimize find_symbol_*() and read_symbols() Peter Zijlstra
  2020-03-25 10:20   ` Miroslav Benes
@ 2020-03-26 10:08   ` tip-bot2 for Peter Zijlstra
  1 sibling, 0 replies; 89+ messages in thread
From: tip-bot2 for Peter Zijlstra @ 2020-03-26 10:08 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Peter Zijlstra (Intel), Miroslav Benes, Josh Poimboeuf, x86, LKML

The following commit has been merged into the core/objtool branch of tip:

Commit-ID:     2a362ecc3ec9632aeea4b9a9062db91b2bd9975a
Gitweb:        https://git.kernel.org/tip/2a362ecc3ec9632aeea4b9a9062db91b2bd9975a
Author:        Peter Zijlstra <peterz@infradead.org>
AuthorDate:    Thu, 12 Mar 2020 09:34:42 +01:00
Committer:     Peter Zijlstra <peterz@infradead.org>
CommitterDate: Wed, 25 Mar 2020 18:28:29 +01:00

objtool: Optimize find_symbol_*() and read_symbols()

All of:

  read_symbols(), find_symbol_by_offset(), find_symbol_containing(),
  find_containing_func()

do a linear search of the symbols. Add an RB tree to make it go
faster.

This about halves objtool runtime on vmlinux.o, from 34s to 18s.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lkml.kernel.org/r/20200324160924.499016559@infradead.org
---
 tools/objtool/Build |   5 +-
 tools/objtool/elf.c | 194 ++++++++++++++++++++++++++++++-------------
 tools/objtool/elf.h |   3 +-
 3 files changed, 144 insertions(+), 58 deletions(-)

diff --git a/tools/objtool/Build b/tools/objtool/Build
index 8dc4f08..66f44f5 100644
--- a/tools/objtool/Build
+++ b/tools/objtool/Build
@@ -11,6 +11,7 @@ objtool-y += objtool.o
 objtool-y += libstring.o
 objtool-y += libctype.o
 objtool-y += str_error_r.o
+objtool-y += librbtree.o
 
 CFLAGS += -I$(srctree)/tools/lib
 
@@ -25,3 +26,7 @@ $(OUTPUT)libctype.o: ../lib/ctype.c FORCE
 $(OUTPUT)str_error_r.o: ../lib/str_error_r.c FORCE
 	$(call rule_mkdir)
 	$(call if_changed_dep,cc_o_c)
+
+$(OUTPUT)librbtree.o: ../lib/rbtree.c FORCE
+	$(call rule_mkdir)
+	$(call if_changed_dep,cc_o_c)
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index 20fe40d..3a8b426 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -27,6 +27,90 @@ static inline u32 str_hash(const char *str)
 	return jhash(str, strlen(str), 0);
 }
 
+static void rb_add(struct rb_root *tree, struct rb_node *node,
+		   int (*cmp)(struct rb_node *, const struct rb_node *))
+{
+	struct rb_node **link = &tree->rb_node;
+	struct rb_node *parent = NULL;
+
+	while (*link) {
+		parent = *link;
+		if (cmp(node, parent) < 0)
+			link = &parent->rb_left;
+		else
+			link = &parent->rb_right;
+	}
+
+	rb_link_node(node, parent, link);
+	rb_insert_color(node, tree);
+}
+
+static struct rb_node *rb_find_first(struct rb_root *tree, const void *key,
+			       int (*cmp)(const void *key, const struct rb_node *))
+{
+	struct rb_node *node = tree->rb_node;
+	struct rb_node *match = NULL;
+
+	while (node) {
+		int c = cmp(key, node);
+		if (c <= 0) {
+			if (!c)
+				match = node;
+			node = node->rb_left;
+		} else if (c > 0) {
+			node = node->rb_right;
+		}
+	}
+
+	return match;
+}
+
+static struct rb_node *rb_next_match(struct rb_node *node, const void *key,
+				    int (*cmp)(const void *key, const struct rb_node *))
+{
+	node = rb_next(node);
+	if (node && cmp(key, node))
+		node = NULL;
+	return node;
+}
+
+#define rb_for_each(tree, node, key, cmp) \
+	for ((node) = rb_find_first((tree), (key), (cmp)); \
+	     (node); (node) = rb_next_match((node), (key), (cmp)))
+
+static int symbol_to_offset(struct rb_node *a, const struct rb_node *b)
+{
+	struct symbol *sa = rb_entry(a, struct symbol, node);
+	struct symbol *sb = rb_entry(b, struct symbol, node);
+
+	if (sa->offset < sb->offset)
+		return -1;
+	if (sa->offset > sb->offset)
+		return 1;
+
+	if (sa->len < sb->len)
+		return -1;
+	if (sa->len > sb->len)
+		return 1;
+
+	sa->alias = sb;
+
+	return 0;
+}
+
+static int symbol_by_offset(const void *key, const struct rb_node *node)
+{
+	const struct symbol *s = rb_entry(node, struct symbol, node);
+	const unsigned long *o = key;
+
+	if (*o < s->offset)
+		return -1;
+	if (*o > s->offset + s->len)
+		return 1;
+
+	return 0;
+}
+
 struct section *find_section_by_name(struct elf *elf, const char *name)
 {
 	struct section *sec;
@@ -63,47 +147,69 @@ static struct symbol *find_symbol_by_index(struct elf *elf, unsigned int idx)
 
 struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset)
 {
-	struct symbol *sym;
+	struct rb_node *node;
 
-	list_for_each_entry(sym, &sec->symbol_list, list)
-		if (sym->type != STT_SECTION && sym->offset == offset)
-			return sym;
+	rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) {
+		struct symbol *s = rb_entry(node, struct symbol, node);
+
+		if (s->offset == offset && s->type != STT_SECTION)
+			return s;
+	}
 
 	return NULL;
 }
 
 struct symbol *find_func_by_offset(struct section *sec, unsigned long offset)
 {
-	struct symbol *sym;
+	struct rb_node *node;
 
-	list_for_each_entry(sym, &sec->symbol_list, list)
-		if (sym->type == STT_FUNC && sym->offset == offset)
-			return sym;
+	rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) {
+		struct symbol *s = rb_entry(node, struct symbol, node);
+
+		if (s->offset == offset && s->type == STT_FUNC)
+			return s;
+	}
 
 	return NULL;
 }
 
-struct symbol *find_symbol_by_name(struct elf *elf, const char *name)
+struct symbol *find_symbol_containing(struct section *sec, unsigned long offset)
 {
-	struct section *sec;
-	struct symbol *sym;
+	struct rb_node *node;
 
-	list_for_each_entry(sec, &elf->sections, list)
-		list_for_each_entry(sym, &sec->symbol_list, list)
-			if (!strcmp(sym->name, name))
-				return sym;
+	rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) {
+		struct symbol *s = rb_entry(node, struct symbol, node);
+
+		if (s->type != STT_SECTION)
+			return s;
+	}
 
 	return NULL;
 }
 
-struct symbol *find_symbol_containing(struct section *sec, unsigned long offset)
+struct symbol *find_containing_func(struct section *sec, unsigned long offset)
+{
+	struct rb_node *node;
+
+	rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) {
+		struct symbol *s = rb_entry(node, struct symbol, node);
+
+		if (s->type == STT_FUNC)
+			return s;
+	}
+
+	return NULL;
+}
+
+struct symbol *find_symbol_by_name(struct elf *elf, const char *name)
 {
+	struct section *sec;
 	struct symbol *sym;
 
-	list_for_each_entry(sym, &sec->symbol_list, list)
-		if (sym->type != STT_SECTION &&
-		    offset >= sym->offset && offset < sym->offset + sym->len)
-			return sym;
+	list_for_each_entry(sec, &elf->sections, list)
+		list_for_each_entry(sym, &sec->symbol_list, list)
+			if (!strcmp(sym->name, name))
+				return sym;
 
 	return NULL;
 }
@@ -130,18 +236,6 @@ struct rela *find_rela_by_dest(struct section *sec, unsigned long offset)
 	return find_rela_by_dest_range(sec, offset, 1);
 }
 
-struct symbol *find_containing_func(struct section *sec, unsigned long offset)
-{
-	struct symbol *func;
-
-	list_for_each_entry(func, &sec->symbol_list, list)
-		if (func->type == STT_FUNC && offset >= func->offset &&
-		    offset < func->offset + func->len)
-			return func;
-
-	return NULL;
-}
-
 static int read_sections(struct elf *elf)
 {
 	Elf_Scn *s = NULL;
@@ -225,8 +319,9 @@ static int read_sections(struct elf *elf)
 static int read_symbols(struct elf *elf)
 {
 	struct section *symtab, *sec;
-	struct symbol *sym, *pfunc, *alias;
-	struct list_head *entry, *tmp;
+	struct symbol *sym, *pfunc;
+	struct list_head *entry;
+	struct rb_node *pnode;
 	int symbols_nr, i;
 	char *coldstr;
 
@@ -245,7 +340,7 @@ static int read_symbols(struct elf *elf)
 			return -1;
 		}
 		memset(sym, 0, sizeof(*sym));
-		alias = sym;
+		sym->alias = sym;
 
 		sym->idx = i;
 
@@ -283,29 +378,12 @@ static int read_symbols(struct elf *elf)
 		sym->offset = sym->sym.st_value;
 		sym->len = sym->sym.st_size;
 
-		/* sorted insert into a per-section list */
-		entry = &sym->sec->symbol_list;
-		list_for_each_prev(tmp, &sym->sec->symbol_list) {
-			struct symbol *s;
-
-			s = list_entry(tmp, struct symbol, list);
-
-			if (sym->offset > s->offset) {
-				entry = tmp;
-				break;
-			}
-
-			if (sym->offset == s->offset) {
-				if (sym->len && sym->len == s->len && alias == sym)
-					alias = s;
-
-				if (sym->len >= s->len) {
-					entry = tmp;
-					break;
-				}
-			}
-		}
-		sym->alias = alias;
+		rb_add(&sym->sec->symbol_tree, &sym->node, symbol_to_offset);
+		pnode = rb_prev(&sym->node);
+		if (pnode)
+			entry = &rb_entry(pnode, struct symbol, node)->list;
+		else
+			entry = &sym->sec->symbol_list;
 		list_add(&sym->list, entry);
 		hash_add(elf->symbol_hash, &sym->hash, sym->idx);
 	}
diff --git a/tools/objtool/elf.h b/tools/objtool/elf.h
index ac7c46f..e4a8d68 100644
--- a/tools/objtool/elf.h
+++ b/tools/objtool/elf.h
@@ -10,6 +10,7 @@
 #include <gelf.h>
 #include <linux/list.h>
 #include <linux/hashtable.h>
+#include <linux/rbtree.h>
 #include <linux/jhash.h>
 
 #ifdef LIBELF_USE_DEPRECATED
@@ -29,6 +30,7 @@ struct section {
 	struct hlist_node hash;
 	struct hlist_node name_hash;
 	GElf_Shdr sh;
+	struct rb_root symbol_tree;
 	struct list_head symbol_list;
 	struct list_head rela_list;
 	DECLARE_HASHTABLE(rela_hash, 16);
@@ -43,6 +45,7 @@ struct section {
 
 struct symbol {
 	struct list_head list;
+	struct rb_node node;
 	struct hlist_node hash;
 	GElf_Sym sym;
 	struct section *sec;

^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [tip: core/objtool] objtool: Optimize find_section_by_name()
  2020-03-24 15:31 ` [PATCH v3 09/26] objtool: Optimize find_section_by_name() Peter Zijlstra
  2020-03-25 10:18   ` Miroslav Benes
@ 2020-03-26 10:08   ` tip-bot2 for Peter Zijlstra
  1 sibling, 0 replies; 89+ messages in thread
From: tip-bot2 for Peter Zijlstra @ 2020-03-26 10:08 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Peter Zijlstra (Intel), Miroslav Benes, Josh Poimboeuf, x86, LKML

The following commit has been merged into the core/objtool branch of tip:

Commit-ID:     ae358196fac3a0b4d2a7d47a4f401e3421027b03
Gitweb:        https://git.kernel.org/tip/ae358196fac3a0b4d2a7d47a4f401e3421027b03
Author:        Peter Zijlstra <peterz@infradead.org>
AuthorDate:    Thu, 12 Mar 2020 09:32:10 +01:00
Committer:     Peter Zijlstra <peterz@infradead.org>
CommitterDate: Wed, 25 Mar 2020 18:28:29 +01:00

objtool: Optimize find_section_by_name()

In order to avoid yet another linear search of (20k) sections, add a
name based hash.

This reduces objtool runtime on vmlinux.o by some 10s to around 35s.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lkml.kernel.org/r/20200324160924.440174280@infradead.org
---
 tools/objtool/elf.c | 10 +++++++++-
 tools/objtool/elf.h |  3 +++
 2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index 9007713..20fe40d 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -22,11 +22,16 @@
 
 #define MAX_NAME_LEN 128
 
+static inline u32 str_hash(const char *str)
+{
+	return jhash(str, strlen(str), 0);
+}
+
 struct section *find_section_by_name(struct elf *elf, const char *name)
 {
 	struct section *sec;
 
-	list_for_each_entry(sec, &elf->sections, list)
+	hash_for_each_possible(elf->section_name_hash, sec, name_hash, str_hash(name))
 		if (!strcmp(sec->name, name))
 			return sec;
 
@@ -202,6 +207,7 @@ static int read_sections(struct elf *elf)
 
 		list_add_tail(&sec->list, &elf->sections);
 		hash_add(elf->section_hash, &sec->hash, sec->idx);
+		hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name));
 	}
 
 	if (stats)
@@ -441,6 +447,7 @@ struct elf *elf_read(const char *name, int flags)
 
 	hash_init(elf->symbol_hash);
 	hash_init(elf->section_hash);
+	hash_init(elf->section_name_hash);
 	INIT_LIST_HEAD(&elf->sections);
 
 	elf->fd = open(name, flags);
@@ -581,6 +588,7 @@ struct section *elf_create_section(struct elf *elf, const char *name,
 
 	list_add_tail(&sec->list, &elf->sections);
 	hash_add(elf->section_hash, &sec->hash, sec->idx);
+	hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name));
 
 	return sec;
 }
diff --git a/tools/objtool/elf.h b/tools/objtool/elf.h
index 8c272eb..ac7c46f 100644
--- a/tools/objtool/elf.h
+++ b/tools/objtool/elf.h
@@ -10,6 +10,7 @@
 #include <gelf.h>
 #include <linux/list.h>
 #include <linux/hashtable.h>
+#include <linux/jhash.h>
 
 #ifdef LIBELF_USE_DEPRECATED
 # define elf_getshdrnum    elf_getshnum
@@ -26,6 +27,7 @@
 struct section {
 	struct list_head list;
 	struct hlist_node hash;
+	struct hlist_node name_hash;
 	GElf_Shdr sh;
 	struct list_head symbol_list;
 	struct list_head rela_list;
@@ -73,6 +75,7 @@ struct elf {
 	struct list_head sections;
 	DECLARE_HASHTABLE(symbol_hash, 20);
 	DECLARE_HASHTABLE(section_hash, 16);
+	DECLARE_HASHTABLE(section_name_hash, 16);
 };
 
 

^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [tip: core/objtool] objtool: Add a statistics mode
  2020-03-24 15:31 ` [PATCH v3 07/26] objtool: Add a statistics mode Peter Zijlstra
  2020-03-25 10:10   ` Miroslav Benes
@ 2020-03-26 10:08   ` tip-bot2 for Peter Zijlstra
  1 sibling, 0 replies; 89+ messages in thread
From: tip-bot2 for Peter Zijlstra @ 2020-03-26 10:08 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Peter Zijlstra (Intel), Miroslav Benes, Josh Poimboeuf, x86, LKML

The following commit has been merged into the core/objtool branch of tip:

Commit-ID:     1e11f3fdc326d7466e43185ea943b6156143387c
Gitweb:        https://git.kernel.org/tip/1e11f3fdc326d7466e43185ea943b6156143387c
Author:        Peter Zijlstra <peterz@infradead.org>
AuthorDate:    Thu, 12 Mar 2020 09:26:29 +01:00
Committer:     Peter Zijlstra <peterz@infradead.org>
CommitterDate: Wed, 25 Mar 2020 18:28:28 +01:00

objtool: Add a statistics mode

Have it print a few numbers which can be used to size the hashtables.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lkml.kernel.org/r/20200324160924.321381240@infradead.org
---
 tools/objtool/builtin-check.c |  3 ++-
 tools/objtool/builtin.h       |  2 +-
 tools/objtool/check.c         |  5 +++++
 tools/objtool/elf.c           | 18 +++++++++++++++++-
 4 files changed, 25 insertions(+), 3 deletions(-)

diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c
index c807984..10fbe75 100644
--- a/tools/objtool/builtin-check.c
+++ b/tools/objtool/builtin-check.c
@@ -17,7 +17,7 @@
 #include "builtin.h"
 #include "check.h"
 
-bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess;
+bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats;
 
 static const char * const check_usage[] = {
 	"objtool check [<options>] file.o",
@@ -31,6 +31,7 @@ const struct option check_options[] = {
 	OPT_BOOLEAN('m', "module", &module, "Indicates the object will be part of a kernel module"),
 	OPT_BOOLEAN('b', "backtrace", &backtrace, "unwind on error"),
 	OPT_BOOLEAN('a', "uaccess", &uaccess, "enable uaccess checking"),
+	OPT_BOOLEAN('s', "stats", &stats, "print statistics"),
 	OPT_END(),
 };
 
diff --git a/tools/objtool/builtin.h b/tools/objtool/builtin.h
index a32736f..0b90790 100644
--- a/tools/objtool/builtin.h
+++ b/tools/objtool/builtin.h
@@ -8,7 +8,7 @@
 #include <subcmd/parse-options.h>
 
 extern const struct option check_options[];
-extern bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess;
+extern bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats;
 
 extern int cmd_check(int argc, const char **argv);
 extern int cmd_orc(int argc, const char **argv);
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 43f7d3c..6df1bae 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -239,6 +239,7 @@ static int decode_instructions(struct objtool_file *file)
 	struct symbol *func;
 	unsigned long offset;
 	struct instruction *insn;
+	unsigned long nr_insns = 0;
 	int ret;
 
 	for_each_sec(file, sec) {
@@ -274,6 +275,7 @@ static int decode_instructions(struct objtool_file *file)
 
 			hash_add(file->insn_hash, &insn->hash, insn->offset);
 			list_add_tail(&insn->list, &file->insn_list);
+			nr_insns++;
 		}
 
 		list_for_each_entry(func, &sec->symbol_list, list) {
@@ -291,6 +293,9 @@ static int decode_instructions(struct objtool_file *file)
 		}
 	}
 
+	if (stats)
+		printf("nr_insns: %lu\n", nr_insns);
+
 	return 0;
 
 err:
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index b188b3e..ff29306 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -15,6 +15,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <errno.h>
+#include "builtin.h"
 
 #include "elf.h"
 #include "warn.h"
@@ -202,6 +203,9 @@ static int read_sections(struct elf *elf)
 		sec->len = sec->sh.sh_size;
 	}
 
+	if (stats)
+		printf("nr_sections: %lu\n", (unsigned long)sections_nr);
+
 	/* sanity check, one more call to elf_nextscn() should return NULL */
 	if (elf_nextscn(elf->elf, s)) {
 		WARN("section entry mismatch");
@@ -299,6 +303,9 @@ static int read_symbols(struct elf *elf)
 		hash_add(elf->symbol_hash, &sym->hash, sym->idx);
 	}
 
+	if (stats)
+		printf("nr_symbols: %lu\n", (unsigned long)symbols_nr);
+
 	/* Create parent/child links for any cold subfunctions */
 	list_for_each_entry(sec, &elf->sections, list) {
 		list_for_each_entry(sym, &sec->symbol_list, list) {
@@ -360,6 +367,7 @@ static int read_relas(struct elf *elf)
 	struct rela *rela;
 	int i;
 	unsigned int symndx;
+	unsigned long nr_rela, max_rela = 0, tot_rela = 0;
 
 	list_for_each_entry(sec, &elf->sections, list) {
 		if (sec->sh.sh_type != SHT_RELA)
@@ -374,6 +382,7 @@ static int read_relas(struct elf *elf)
 
 		sec->base->rela = sec;
 
+		nr_rela = 0;
 		for (i = 0; i < sec->sh.sh_size / sec->sh.sh_entsize; i++) {
 			rela = malloc(sizeof(*rela));
 			if (!rela) {
@@ -401,8 +410,15 @@ static int read_relas(struct elf *elf)
 
 			list_add_tail(&rela->list, &sec->rela_list);
 			hash_add(sec->rela_hash, &rela->hash, rela->offset);
-
+			nr_rela++;
 		}
+		max_rela = max(max_rela, nr_rela);
+		tot_rela += nr_rela;
+	}
+
+	if (stats) {
+		printf("max_rela: %lu\n", max_rela);
+		printf("tot_rela: %lu\n", tot_rela);
 	}
 
 	return 0;

^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [tip: core/objtool] objtool: Optimize find_symbol_by_index()
  2020-03-24 15:31 ` [PATCH v3 06/26] objtool: Optimize find_symbol_by_index() Peter Zijlstra
  2020-03-25 10:01   ` Miroslav Benes
@ 2020-03-26 10:08   ` tip-bot2 for Peter Zijlstra
  1 sibling, 0 replies; 89+ messages in thread
From: tip-bot2 for Peter Zijlstra @ 2020-03-26 10:08 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Peter Zijlstra (Intel), Miroslav Benes, Josh Poimboeuf, x86, LKML

The following commit has been merged into the core/objtool branch of tip:

Commit-ID:     65fb11a7f6aeae678043738d06248a4e21f4e4e4
Gitweb:        https://git.kernel.org/tip/65fb11a7f6aeae678043738d06248a4e21f4e4e4
Author:        Peter Zijlstra <peterz@infradead.org>
AuthorDate:    Tue, 10 Mar 2020 18:39:45 +01:00
Committer:     Peter Zijlstra <peterz@infradead.org>
CommitterDate: Wed, 25 Mar 2020 18:28:28 +01:00

objtool: Optimize find_symbol_by_index()

The symbol index is object wide, not per section, so it makes no sense
to have the symbol_hash be part of the section object. By moving it to
the elf object we avoid the linear sections iteration.

This reduces the runtime of objtool on vmlinux.o from over 3 hours (I
gave up) to a few minutes. The defconfig vmlinux.o has around 20k
sections.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lkml.kernel.org/r/20200324160924.261852348@infradead.org
---
 tools/objtool/elf.c | 13 +++++--------
 tools/objtool/elf.h |  3 +--
 2 files changed, 6 insertions(+), 10 deletions(-)

diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index cc4601c..b188b3e 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -46,13 +46,11 @@ static struct section *find_section_by_index(struct elf *elf,
 
 static struct symbol *find_symbol_by_index(struct elf *elf, unsigned int idx)
 {
-	struct section *sec;
 	struct symbol *sym;
 
-	list_for_each_entry(sec, &elf->sections, list)
-		hash_for_each_possible(sec->symbol_hash, sym, hash, idx)
-			if (sym->idx == idx)
-				return sym;
+	hash_for_each_possible(elf->symbol_hash, sym, hash, idx)
+		if (sym->idx == idx)
+			return sym;
 
 	return NULL;
 }
@@ -166,7 +164,6 @@ static int read_sections(struct elf *elf)
 		INIT_LIST_HEAD(&sec->symbol_list);
 		INIT_LIST_HEAD(&sec->rela_list);
 		hash_init(sec->rela_hash);
-		hash_init(sec->symbol_hash);
 
 		list_add_tail(&sec->list, &elf->sections);
 
@@ -299,7 +296,7 @@ static int read_symbols(struct elf *elf)
 		}
 		sym->alias = alias;
 		list_add(&sym->list, entry);
-		hash_add(sym->sec->symbol_hash, &sym->hash, sym->idx);
+		hash_add(elf->symbol_hash, &sym->hash, sym->idx);
 	}
 
 	/* Create parent/child links for any cold subfunctions */
@@ -425,6 +422,7 @@ struct elf *elf_read(const char *name, int flags)
 	}
 	memset(elf, 0, sizeof(*elf));
 
+	hash_init(elf->symbol_hash);
 	INIT_LIST_HEAD(&elf->sections);
 
 	elf->fd = open(name, flags);
@@ -486,7 +484,6 @@ struct section *elf_create_section(struct elf *elf, const char *name,
 	INIT_LIST_HEAD(&sec->symbol_list);
 	INIT_LIST_HEAD(&sec->rela_list);
 	hash_init(sec->rela_hash);
-	hash_init(sec->symbol_hash);
 
 	list_add_tail(&sec->list, &elf->sections);
 
diff --git a/tools/objtool/elf.h b/tools/objtool/elf.h
index a196325..1222980 100644
--- a/tools/objtool/elf.h
+++ b/tools/objtool/elf.h
@@ -27,7 +27,6 @@ struct section {
 	struct list_head list;
 	GElf_Shdr sh;
 	struct list_head symbol_list;
-	DECLARE_HASHTABLE(symbol_hash, 8);
 	struct list_head rela_list;
 	DECLARE_HASHTABLE(rela_hash, 16);
 	struct section *base, *rela;
@@ -71,7 +70,7 @@ struct elf {
 	int fd;
 	char *name;
 	struct list_head sections;
-	DECLARE_HASHTABLE(rela_hash, 16);
+	DECLARE_HASHTABLE(symbol_hash, 20);
 };
 
 

^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [tip: core/objtool] x86/kexec: Make relocate_kernel_64.S objtool clean
  2020-03-24 15:31 ` [PATCH v3 05/26] x86/kexec: Make relocate_kernel_64.S objtool clean Peter Zijlstra
  2020-03-24 20:55   ` Josh Poimboeuf
  2020-03-25  9:56   ` Miroslav Benes
@ 2020-03-26 10:08   ` tip-bot2 for Peter Zijlstra
  2 siblings, 0 replies; 89+ messages in thread
From: tip-bot2 for Peter Zijlstra @ 2020-03-26 10:08 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Josh Poimboeuf, Peter Zijlstra (Intel), Miroslav Benes, x86, LKML

The following commit has been merged into the core/objtool branch of tip:

Commit-ID:     36cc552055a5f95bab479533b4ebbad6a6cea0e1
Gitweb:        https://git.kernel.org/tip/36cc552055a5f95bab479533b4ebbad6a6cea0e1
Author:        Peter Zijlstra <peterz@infradead.org>
AuthorDate:    Tue, 24 Mar 2020 15:35:42 +01:00
Committer:     Peter Zijlstra <peterz@infradead.org>
CommitterDate: Wed, 25 Mar 2020 18:28:28 +01:00

x86/kexec: Make relocate_kernel_64.S objtool clean

Having fixed the biggest objtool issue in this file; fix up the rest
and remove the exception.

Suggested-by: Josh Poimboeuf <jpoimboe@redhat.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lkml.kernel.org/r/20200324160924.202621656@infradead.org
---
 arch/x86/kernel/Makefile             | 1 -
 arch/x86/kernel/relocate_kernel_64.S | 7 +++++++
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 9b294c1..8be5926 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -28,7 +28,6 @@ KASAN_SANITIZE_dumpstack_$(BITS).o			:= n
 KASAN_SANITIZE_stacktrace.o				:= n
 KASAN_SANITIZE_paravirt.o				:= n
 
-OBJECT_FILES_NON_STANDARD_relocate_kernel_$(BITS).o	:= y
 OBJECT_FILES_NON_STANDARD_test_nx.o			:= y
 OBJECT_FILES_NON_STANDARD_paravirt_patch.o		:= y
 
diff --git a/arch/x86/kernel/relocate_kernel_64.S b/arch/x86/kernel/relocate_kernel_64.S
index cc5c8b9..a4d9a26 100644
--- a/arch/x86/kernel/relocate_kernel_64.S
+++ b/arch/x86/kernel/relocate_kernel_64.S
@@ -9,6 +9,8 @@
 #include <asm/kexec.h>
 #include <asm/processor-flags.h>
 #include <asm/pgtable_types.h>
+#include <asm/nospec-branch.h>
+#include <asm/unwind_hints.h>
 
 /*
  * Must be relocatable PIC code callable as a C function
@@ -39,6 +41,7 @@
 	.align PAGE_SIZE
 	.code64
 SYM_CODE_START_NOALIGN(relocate_kernel)
+	UNWIND_HINT_EMPTY
 	/*
 	 * %rdi indirection_page
 	 * %rsi page_list
@@ -105,6 +108,7 @@ SYM_CODE_START_NOALIGN(relocate_kernel)
 SYM_CODE_END(relocate_kernel)
 
 SYM_CODE_START_LOCAL_NOALIGN(identity_mapped)
+	UNWIND_HINT_EMPTY
 	/* set return address to 0 if not preserving context */
 	pushq	$0
 	/* store the start address on the stack */
@@ -192,6 +196,7 @@ SYM_CODE_START_LOCAL_NOALIGN(identity_mapped)
 1:
 	popq	%rdx
 	leaq	PAGE_SIZE(%r10), %rsp
+	ANNOTATE_RETPOLINE_SAFE
 	call	*%rdx
 
 	/* get the re-entry point of the peer system */
@@ -209,6 +214,7 @@ SYM_CODE_START_LOCAL_NOALIGN(identity_mapped)
 SYM_CODE_END(identity_mapped)
 
 SYM_CODE_START_LOCAL_NOALIGN(virtual_mapped)
+	UNWIND_HINT_EMPTY
 	movq	RSP(%r8), %rsp
 	movq	CR4(%r8), %rax
 	movq	%rax, %cr4
@@ -230,6 +236,7 @@ SYM_CODE_END(virtual_mapped)
 
 	/* Do the copies */
 SYM_CODE_START_LOCAL_NOALIGN(swap_pages)
+	UNWIND_HINT_EMPTY
 	movq	%rdi, %rcx 	/* Put the page_list in %rcx */
 	xorl	%edi, %edi
 	xorl	%esi, %esi

^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [tip: core/objtool] x86/kexec: Use RIP relative addressing
  2020-03-24 15:31 ` [PATCH v3 04/26] x86/kexec: Use RIP relative addressing Peter Zijlstra
  2020-03-25  9:34   ` Miroslav Benes
@ 2020-03-26 10:08   ` tip-bot2 for Peter Zijlstra
  1 sibling, 0 replies; 89+ messages in thread
From: tip-bot2 for Peter Zijlstra @ 2020-03-26 10:08 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Brian Gerst, Peter Zijlstra (Intel),
	Miroslav Benes, Josh Poimboeuf, x86, LKML

The following commit has been merged into the core/objtool branch of tip:

Commit-ID:     fc8bd77d6476d7733ace9e03093b4acaee6e0605
Gitweb:        https://git.kernel.org/tip/fc8bd77d6476d7733ace9e03093b4acaee6e0605
Author:        Peter Zijlstra <peterz@infradead.org>
AuthorDate:    Mon, 16 Mar 2020 10:13:45 +01:00
Committer:     Peter Zijlstra <peterz@infradead.org>
CommitterDate: Wed, 25 Mar 2020 18:28:27 +01:00

x86/kexec: Use RIP relative addressing

Normally identity_mapped is not visible to objtool, due to:

  arch/x86/kernel/Makefile:OBJECT_FILES_NON_STANDARD_relocate_kernel_$(BITS).o := y

However, when we want to run objtool on vmlinux.o there is no hiding
it:

  vmlinux.o: warning: objtool: .text+0x4c0f1: unsupported intra-function call

Replace the (i386 inspired) pattern:

	call 1f
  1:	popq %r8
	subq $(1b - relocate_kernel), %r8

With a x86_64 RIP-relative LEA:

	leaq relocate_kernel(%rip), %r8

Suggested-by: Brian Gerst <brgerst@gmail.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lkml.kernel.org/r/20200324160924.143334345@infradead.org
---
 arch/x86/kernel/relocate_kernel_64.S | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/arch/x86/kernel/relocate_kernel_64.S b/arch/x86/kernel/relocate_kernel_64.S
index ef3ba99..cc5c8b9 100644
--- a/arch/x86/kernel/relocate_kernel_64.S
+++ b/arch/x86/kernel/relocate_kernel_64.S
@@ -196,10 +196,7 @@ SYM_CODE_START_LOCAL_NOALIGN(identity_mapped)
 
 	/* get the re-entry point of the peer system */
 	movq	0(%rsp), %rbp
-	call	1f
-1:
-	popq	%r8
-	subq	$(1b - relocate_kernel), %r8
+	leaq	relocate_kernel(%rip), %r8
 	movq	CP_PA_SWAP_PAGE(%r8), %r10
 	movq	CP_PA_BACKUP_PAGES_MAP(%r8), %rdi
 	movq	CP_PA_TABLE_PAGE(%r8), %rax

^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [tip: core/objtool] objtool: Rename func_for_each_insn()
  2020-03-24 15:31 ` [PATCH v3 02/26] objtool: Rename func_for_each_insn() Peter Zijlstra
  2020-03-25  8:43   ` Miroslav Benes
@ 2020-03-26 10:08   ` tip-bot2 for Peter Zijlstra
  1 sibling, 0 replies; 89+ messages in thread
From: tip-bot2 for Peter Zijlstra @ 2020-03-26 10:08 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Peter Zijlstra (Intel), Miroslav Benes, Josh Poimboeuf, x86, LKML

The following commit has been merged into the core/objtool branch of tip:

Commit-ID:     dbf4aeb0a494020644507459c2446e632cba1a05
Gitweb:        https://git.kernel.org/tip/dbf4aeb0a494020644507459c2446e632cba1a05
Author:        Peter Zijlstra <peterz@infradead.org>
AuthorDate:    Tue, 10 Mar 2020 18:24:59 +01:00
Committer:     Peter Zijlstra <peterz@infradead.org>
CommitterDate: Wed, 25 Mar 2020 18:28:27 +01:00

objtool: Rename func_for_each_insn()

There is func_for_each_insn() and func_for_each_insn_all(), the both
iterate the instructions, but the first uses symbol offset/length
while the second uses insn->func.

Rename func_for_each_insn() to sym_for_eac_insn() because it iterates
on symbol information.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lkml.kernel.org/r/20200324160924.024341229@infradead.org
---
 tools/objtool/check.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index da17b5a..564ea1d 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -77,17 +77,17 @@ static struct instruction *next_insn_same_func(struct objtool_file *file,
 	     insn;							\
 	     insn = next_insn_same_func(file, insn))
 
-#define func_for_each_insn(file, func, insn)				\
-	for (insn = find_insn(file, func->sec, func->offset);		\
+#define sym_for_each_insn(file, sym, insn)				\
+	for (insn = find_insn(file, sym->sec, sym->offset);		\
 	     insn && &insn->list != &file->insn_list &&			\
-		insn->sec == func->sec &&				\
-		insn->offset < func->offset + func->len;		\
+		insn->sec == sym->sec &&				\
+		insn->offset < sym->offset + sym->len;			\
 	     insn = list_next_entry(insn, list))
 
-#define func_for_each_insn_continue_reverse(file, func, insn)		\
+#define sym_for_each_insn_continue_reverse(file, sym, insn)		\
 	for (insn = list_prev_entry(insn, list);			\
 	     &insn->list != &file->insn_list &&				\
-		insn->sec == func->sec && insn->offset >= func->offset;	\
+		insn->sec == sym->sec && insn->offset >= sym->offset;	\
 	     insn = list_prev_entry(insn, list))
 
 #define sec_for_each_insn_from(file, insn)				\
@@ -286,7 +286,7 @@ static int decode_instructions(struct objtool_file *file)
 				return -1;
 			}
 
-			func_for_each_insn(file, func, insn)
+			sym_for_each_insn(file, func, insn)
 				insn->func = func;
 		}
 	}
@@ -2064,7 +2064,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
 
 				i = insn;
 				save_insn = NULL;
-				func_for_each_insn_continue_reverse(file, func, i) {
+				sym_for_each_insn_continue_reverse(file, func, i) {
 					if (i->save) {
 						save_insn = i;
 						break;

^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [tip: core/objtool] objtool: Rename func_for_each_insn_all()
  2020-03-24 15:31 ` [PATCH v3 03/26] objtool: Rename func_for_each_insn_all() Peter Zijlstra
  2020-03-25  8:44   ` Miroslav Benes
@ 2020-03-26 10:08   ` tip-bot2 for Peter Zijlstra
  1 sibling, 0 replies; 89+ messages in thread
From: tip-bot2 for Peter Zijlstra @ 2020-03-26 10:08 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Peter Zijlstra (Intel), Miroslav Benes, Josh Poimboeuf, x86, LKML

The following commit has been merged into the core/objtool branch of tip:

Commit-ID:     f0f70adb78108a0cbc321a07133cd78ea4f84699
Gitweb:        https://git.kernel.org/tip/f0f70adb78108a0cbc321a07133cd78ea4f84699
Author:        Peter Zijlstra <peterz@infradead.org>
AuthorDate:    Tue, 10 Mar 2020 18:27:24 +01:00
Committer:     Peter Zijlstra <peterz@infradead.org>
CommitterDate: Wed, 25 Mar 2020 18:28:27 +01:00

objtool: Rename func_for_each_insn_all()

Now that func_for_each_insn() is available, rename
func_for_each_insn_all(). This gets us:

  sym_for_each_insn()  - iterate on symbol offset/len
  func_for_each_insn() - iterate on insn->func

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lkml.kernel.org/r/20200324160924.083720147@infradead.org
---
 tools/objtool/check.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 564ea1d..43f7d3c 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -72,7 +72,7 @@ static struct instruction *next_insn_same_func(struct objtool_file *file,
 	return find_insn(file, func->cfunc->sec, func->cfunc->offset);
 }
 
-#define func_for_each_insn_all(file, func, insn)			\
+#define func_for_each_insn(file, func, insn)				\
 	for (insn = find_insn(file, func->sec, func->offset);		\
 	     insn;							\
 	     insn = next_insn_same_func(file, insn))
@@ -170,7 +170,7 @@ static bool __dead_end_function(struct objtool_file *file, struct symbol *func,
 	if (!insn->func)
 		return false;
 
-	func_for_each_insn_all(file, func, insn) {
+	func_for_each_insn(file, func, insn) {
 		empty = false;
 
 		if (insn->type == INSN_RETURN)
@@ -185,7 +185,7 @@ static bool __dead_end_function(struct objtool_file *file, struct symbol *func,
 	 * case, the function's dead-end status depends on whether the target
 	 * of the sibling call returns.
 	 */
-	func_for_each_insn_all(file, func, insn) {
+	func_for_each_insn(file, func, insn) {
 		if (is_sibling_call(insn)) {
 			struct instruction *dest = insn->jump_dest;
 
@@ -430,7 +430,7 @@ static void add_ignores(struct objtool_file *file)
 			continue;
 		}
 
-		func_for_each_insn_all(file, func, insn)
+		func_for_each_insn(file, func, insn)
 			insn->ignore = true;
 	}
 }
@@ -1122,7 +1122,7 @@ static void mark_func_jump_tables(struct objtool_file *file,
 	struct instruction *insn, *last = NULL;
 	struct rela *rela;
 
-	func_for_each_insn_all(file, func, insn) {
+	func_for_each_insn(file, func, insn) {
 		if (!last)
 			last = insn;
 
@@ -1157,7 +1157,7 @@ static int add_func_jump_tables(struct objtool_file *file,
 	struct instruction *insn;
 	int ret;
 
-	func_for_each_insn_all(file, func, insn) {
+	func_for_each_insn(file, func, insn) {
 		if (!insn->jump_table)
 			continue;
 

^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [tip: core/objtool] objtool: Introduce validate_return()
  2020-03-24 15:31 ` [PATCH v3 01/26] objtool: Introduce validate_return() Peter Zijlstra
  2020-03-25  8:39   ` Miroslav Benes
@ 2020-03-26 10:08   ` tip-bot2 for Peter Zijlstra
  1 sibling, 0 replies; 89+ messages in thread
From: tip-bot2 for Peter Zijlstra @ 2020-03-26 10:08 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Peter Zijlstra (Intel), Miroslav Benes, Josh Poimboeuf, x86, LKML

The following commit has been merged into the core/objtool branch of tip:

Commit-ID:     a92e92d1a749e9bae9828f34f632d56ac2c6d2c3
Gitweb:        https://git.kernel.org/tip/a92e92d1a749e9bae9828f34f632d56ac2c6d2c3
Author:        Peter Zijlstra <peterz@infradead.org>
AuthorDate:    Tue, 10 Mar 2020 18:07:44 +01:00
Committer:     Peter Zijlstra <peterz@infradead.org>
CommitterDate: Wed, 25 Mar 2020 18:28:27 +01:00

objtool: Introduce validate_return()

Trivial 'cleanup' to save one indentation level and match
validate_call().

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lkml.kernel.org/r/20200324160923.963996225@infradead.org
---
 tools/objtool/check.c | 64 +++++++++++++++++++++++-------------------
 1 file changed, 36 insertions(+), 28 deletions(-)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 6b6178e..da17b5a 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -1975,6 +1975,41 @@ static int validate_sibling_call(struct instruction *insn, struct insn_state *st
 	return validate_call(insn, state);
 }
 
+static int validate_return(struct symbol *func, struct instruction *insn, struct insn_state *state)
+{
+	if (state->uaccess && !func_uaccess_safe(func)) {
+		WARN_FUNC("return with UACCESS enabled",
+			  insn->sec, insn->offset);
+		return 1;
+	}
+
+	if (!state->uaccess && func_uaccess_safe(func)) {
+		WARN_FUNC("return with UACCESS disabled from a UACCESS-safe function",
+			  insn->sec, insn->offset);
+		return 1;
+	}
+
+	if (state->df) {
+		WARN_FUNC("return with DF set",
+			  insn->sec, insn->offset);
+		return 1;
+	}
+
+	if (func && has_modified_stack_frame(state)) {
+		WARN_FUNC("return with modified stack frame",
+			  insn->sec, insn->offset);
+		return 1;
+	}
+
+	if (state->bp_scratch) {
+		WARN("%s uses BP as a scratch register",
+		     func->name);
+		return 1;
+	}
+
+	return 0;
+}
+
 /*
  * Follow the branch starting at the given instruction, and recursively follow
  * any other branches (jumps).  Meanwhile, track the frame pointer state at
@@ -2090,34 +2125,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
 		switch (insn->type) {
 
 		case INSN_RETURN:
-			if (state.uaccess && !func_uaccess_safe(func)) {
-				WARN_FUNC("return with UACCESS enabled", sec, insn->offset);
-				return 1;
-			}
-
-			if (!state.uaccess && func_uaccess_safe(func)) {
-				WARN_FUNC("return with UACCESS disabled from a UACCESS-safe function", sec, insn->offset);
-				return 1;
-			}
-
-			if (state.df) {
-				WARN_FUNC("return with DF set", sec, insn->offset);
-				return 1;
-			}
-
-			if (func && has_modified_stack_frame(&state)) {
-				WARN_FUNC("return with modified stack frame",
-					  sec, insn->offset);
-				return 1;
-			}
-
-			if (state.bp_scratch) {
-				WARN("%s uses BP as a scratch register",
-				     func->name);
-				return 1;
-			}
-
-			return 0;
+			return validate_return(func, insn, &state);
 
 		case INSN_CALL:
 		case INSN_CALL_DYNAMIC:

^ permalink raw reply related	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 14/26] objtool: Optimize read_sections()
  2020-03-24 15:31 ` [PATCH v3 14/26] objtool: Optimize read_sections() Peter Zijlstra
  2020-03-25 12:10   ` Miroslav Benes
  2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
@ 2020-04-21 14:47   ` youling257
  2020-04-21 15:49     ` Peter Zijlstra
  2 siblings, 1 reply; 89+ messages in thread
From: youling257 @ 2020-04-21 14:47 UTC (permalink / raw)
  To: peterz
  Cc: tglx, jpoimboe, linux-kernel, x86, mhiramat, mbenes, brgerst, youling257

this patch cause 64bit kernel build failed on 32bit userspace, please fix.

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 14/26] objtool: Optimize read_sections()
  2020-04-21 14:47   ` [PATCH v3 14/26] " youling257
@ 2020-04-21 15:49     ` Peter Zijlstra
  2020-04-21 15:57       ` Borislav Petkov
  2020-04-21 17:50       ` youling 257
  0 siblings, 2 replies; 89+ messages in thread
From: Peter Zijlstra @ 2020-04-21 15:49 UTC (permalink / raw)
  To: youling257; +Cc: tglx, jpoimboe, linux-kernel, x86, mhiramat, mbenes, brgerst

On Tue, Apr 21, 2020 at 10:47:14PM +0800, youling257 wrote:
> this patch cause 64bit kernel build failed on 32bit userspace, please fix.

You've now spend more time and effort complaining about this than it
would've been to fix it yourself. The fix is absolutely trivial.

Anyway, I have a fix, it'll get there eventually. Excuse me for not
considering 32bit a priority.

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 14/26] objtool: Optimize read_sections()
  2020-04-21 15:49     ` Peter Zijlstra
@ 2020-04-21 15:57       ` Borislav Petkov
  2020-04-21 17:50       ` youling 257
  1 sibling, 0 replies; 89+ messages in thread
From: Borislav Petkov @ 2020-04-21 15:57 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: youling257, tglx, jpoimboe, linux-kernel, x86, mhiramat, mbenes, brgerst

On Tue, Apr 21, 2020 at 05:49:28PM +0200, Peter Zijlstra wrote:
> Excuse me for not considering 32bit a priority.

rm -rf 32bit already!

-- 
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [PATCH v3 14/26] objtool: Optimize read_sections()
  2020-04-21 15:49     ` Peter Zijlstra
  2020-04-21 15:57       ` Borislav Petkov
@ 2020-04-21 17:50       ` youling 257
  1 sibling, 0 replies; 89+ messages in thread
From: youling 257 @ 2020-04-21 17:50 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: tglx, jpoimboe, linux-kernel, x86, mhiramat, mbenes, brgerst

linux 5.7rc3 will fix? please submit the patch.

2020-04-21 23:49 GMT+08:00, Peter Zijlstra <peterz@infradead.org>:
> On Tue, Apr 21, 2020 at 10:47:14PM +0800, youling257 wrote:
>> this patch cause 64bit kernel build failed on 32bit userspace, please
>> fix.
>
> You've now spend more time and effort complaining about this than it
> would've been to fix it yourself. The fix is absolutely trivial.
>
> Anyway, I have a fix, it'll get there eventually. Excuse me for not
> considering 32bit a priority.
>

^ permalink raw reply	[flat|nested] 89+ messages in thread

end of thread, other threads:[~2020-04-21 17:50 UTC | newest]

Thread overview: 89+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-03-24 15:31 [PATCH v3 00/26] objtool: vmlinux.o and noinstr validation Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 01/26] objtool: Introduce validate_return() Peter Zijlstra
2020-03-25  8:39   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 02/26] objtool: Rename func_for_each_insn() Peter Zijlstra
2020-03-25  8:43   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 03/26] objtool: Rename func_for_each_insn_all() Peter Zijlstra
2020-03-25  8:44   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 04/26] x86/kexec: Use RIP relative addressing Peter Zijlstra
2020-03-25  9:34   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 05/26] x86/kexec: Make relocate_kernel_64.S objtool clean Peter Zijlstra
2020-03-24 20:55   ` Josh Poimboeuf
2020-03-25  9:56   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 06/26] objtool: Optimize find_symbol_by_index() Peter Zijlstra
2020-03-25 10:01   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 07/26] objtool: Add a statistics mode Peter Zijlstra
2020-03-25 10:10   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 08/26] objtool: Optimize find_section_by_index() Peter Zijlstra
2020-03-25 10:12   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 09/26] objtool: Optimize find_section_by_name() Peter Zijlstra
2020-03-25 10:18   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 10/26] objtool: Optimize find_symbol_*() and read_symbols() Peter Zijlstra
2020-03-25 10:20   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 11/26] objtool: Rename find_containing_func() Peter Zijlstra
2020-03-25 10:21   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 12/26] objtool: Resize insn_hash Peter Zijlstra
2020-03-25 10:21   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 13/26] objtool: Optimize find_symbol_by_name() Peter Zijlstra
2020-03-25 10:25   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 14/26] objtool: Optimize read_sections() Peter Zijlstra
2020-03-25 12:10   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-04-21 14:47   ` [PATCH v3 14/26] " youling257
2020-04-21 15:49     ` Peter Zijlstra
2020-04-21 15:57       ` Borislav Petkov
2020-04-21 17:50       ` youling 257
2020-03-24 15:31 ` [PATCH v3 15/26] objtool: Delete cleanup() Peter Zijlstra
2020-03-25 12:11   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 16/26] objtool: Optimize find_rela_by_dest_range() Peter Zijlstra
2020-03-25 12:19   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 17/26] objtool: Re-arrange validate_functions() Peter Zijlstra
2020-03-24 21:10   ` Josh Poimboeuf
2020-03-24 21:15     ` Peter Zijlstra
2020-03-25 12:22   ` Miroslav Benes
2020-03-26 10:08   ` [tip: core/objtool] " tip-bot2 for Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 18/26] objtool: Fix !CFI insn_state propagation Peter Zijlstra
2020-03-24 21:40   ` Josh Poimboeuf
2020-03-24 22:11     ` Peter Zijlstra
2020-03-24 23:00       ` Peter Zijlstra
2020-03-25 14:39         ` Josh Poimboeuf
2020-03-25 14:41   ` [PATCH v3.1 18a/26] objtool: Remove CFI save/restore special case Peter Zijlstra
2020-03-25 14:42   ` [PATCH v3.1 18b/26] objtool: Factor out CFI hints Peter Zijlstra
2020-03-25 14:43   ` [PATCH v3.1 18c/26] objtool: Rename struct cfi_state Peter Zijlstra
2020-03-25 14:43   ` [PATCH v3.1 18d/26] objtool: Fix !CFI insn_state propagation Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 19/26] objtool: Implement noinstr validation Peter Zijlstra
2020-03-24 21:41   ` Josh Poimboeuf
2020-03-25 14:44   ` [PATCH v3.1 " Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 20/26] objtool: Optimize !vmlinux.o again Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 21/26] objtool: Use sec_offset_hash() for insn_hash Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 22/26] objtool: Detect loading function pointers across noinstr Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 23/26] kbuild/objtool: Add objtool-vmlinux.o pass Peter Zijlstra
2020-03-24 22:03   ` Josh Poimboeuf
2020-03-24 22:05     ` Peter Zijlstra
2020-03-24 15:31 ` [PATCH v3 24/26] objtool: Avoid iterating !text section symbols Peter Zijlstra
2020-03-24 22:09   ` Josh Poimboeuf
2020-03-24 15:31 ` [PATCH v3 25/26] objtool: Rearrange validate_section() Peter Zijlstra
2020-03-24 22:10   ` Josh Poimboeuf
2020-03-24 15:31 ` [PATCH v3 26/26] objtool: Add STT_NOTYPE noinstr validation Peter Zijlstra
2020-03-24 22:16   ` Josh Poimboeuf
2020-03-24 22:34     ` Peter Zijlstra
2020-03-25 14:42       ` Josh Poimboeuf
2020-03-25 15:53         ` Peter Zijlstra
2020-03-25 16:40           ` Josh Poimboeuf
2020-03-25 16:50             ` Peter Zijlstra
2020-03-26  8:01               ` Julien Thierry

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.