[PATCH 07/14] objtool: Extricate checksum calculation from validate_branch()

Josh Poimboeuf posted 14 patches 1 month ago
There is a newer version of this series
[PATCH 07/14] objtool: Extricate checksum calculation from validate_branch()
Posted by Josh Poimboeuf 1 month ago
In preparation for porting the checksum code to other arches, make its
functionality independent from validate_branch().

Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
 tools/objtool/check.c                    | 71 +++++++++++++++++-------
 tools/objtool/include/objtool/checksum.h |  6 +-
 2 files changed, 53 insertions(+), 24 deletions(-)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index a30379e4ff97..f73cf1382e5c 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -2633,8 +2633,7 @@ static bool validate_branch_enabled(void)
 {
 	return opts.stackval ||
 	       opts.orc ||
-	       opts.uaccess ||
-	       opts.checksum;
+	       opts.uaccess;
 }
 
 static int decode_sections(struct objtool_file *file)
@@ -3649,6 +3648,7 @@ static bool skip_alt_group(struct instruction *insn)
 	return alt_insn->type == INSN_CLAC || alt_insn->type == INSN_STAC;
 }
 
+#ifdef BUILD_KLP
 static int checksum_debug_init(struct objtool_file *file)
 {
 	char *dup, *s;
@@ -3691,9 +3691,30 @@ static void checksum_update_insn(struct objtool_file *file, struct symbol *func,
 				 struct instruction *insn)
 {
 	struct reloc *reloc = insn_reloc(file, insn);
+	struct alternative *alt;
 	unsigned long offset;
 	struct symbol *sym;
 
+	for (alt = insn->alts; alt; alt = alt->next) {
+		struct alt_group *alt_group = alt->insn->alt_group;
+
+		checksum_update(func, insn, &alt->type, sizeof(alt->type));
+
+		if (alt_group && alt_group->orig_group) {
+			struct instruction *alt_insn;
+
+			checksum_update(func, insn, &alt_group->feature, sizeof(alt_group->feature));
+
+			for (alt_insn = alt->insn; alt_insn; alt_insn = next_insn_same_sec(file, alt_insn)) {
+				checksum_update_insn(file, func, alt_insn);
+				if (alt_insn == alt_group->last_insn)
+					break;
+			}
+		} else {
+			checksum_update(func, insn, &alt->insn->offset, sizeof(alt->insn->offset));
+		}
+	}
+
 	if (insn->fake)
 		return;
 
@@ -3702,9 +3723,11 @@ static void checksum_update_insn(struct objtool_file *file, struct symbol *func,
 	if (!reloc) {
 		struct symbol *call_dest = insn_call_dest(insn);
 
-		if (call_dest)
+		if (call_dest) {
+			/* intra-TU call without reloc */
 			checksum_update(func, insn, call_dest->demangled_name,
 					strlen(call_dest->demangled_name));
+		}
 		return;
 	}
 
@@ -3731,6 +3754,29 @@ static void checksum_update_insn(struct objtool_file *file, struct symbol *func,
 	checksum_update(func, insn, &offset, sizeof(offset));
 }
 
+static int calculate_checksums(struct objtool_file *file)
+{
+	struct instruction *insn;
+	struct symbol *func;
+
+	if (checksum_debug_init(file))
+		return -1;
+
+	for_each_sym(file->elf, func) {
+		if (!is_func_sym(func))
+			continue;
+
+		checksum_init(func);
+
+		func_for_each_insn(file, func, insn)
+			checksum_update_insn(file, func, insn);
+
+		checksum_finish(func);
+	}
+	return 0;
+}
+#endif /* BUILD_KLP */
+
 static int validate_branch(struct objtool_file *file, struct symbol *func,
 			   struct instruction *insn, struct insn_state state);
 static int do_validate_branch(struct objtool_file *file, struct symbol *func,
@@ -4012,9 +4058,6 @@ static int do_validate_branch(struct objtool_file *file, struct symbol *func,
 		insn->trace = 0;
 		next_insn = next_insn_to_validate(file, insn);
 
-		if (opts.checksum && func && insn->sec)
-			checksum_update_insn(file, func, insn);
-
 		if (func && insn_func(insn) && func != insn_func(insn)->pfunc) {
 			/* Ignore KCFI type preambles, which always fall through */
 			if (is_prefix_func(func))
@@ -4080,9 +4123,6 @@ static int validate_unwind_hint(struct objtool_file *file,
 		struct symbol *func = insn_func(insn);
 		int ret;
 
-		if (opts.checksum)
-			checksum_init(func);
-
 		ret = validate_branch(file, func, insn, *state);
 		if (ret)
 			BT_INSN(insn, "<=== (hint)");
@@ -4525,9 +4565,6 @@ static int validate_symbol(struct objtool_file *file, struct section *sec,
 
 	func = insn_func(insn);
 
-	if (opts.checksum)
-		checksum_init(func);
-
 	if (opts.trace && !fnmatch(opts.trace, sym->name, 0)) {
 		trace_enable();
 		TRACE("%s: validation begin\n", sym->name);
@@ -4540,9 +4577,6 @@ static int validate_symbol(struct objtool_file *file, struct section *sec,
 	TRACE("%s: validation %s\n\n", sym->name, ret ? "failed" : "end");
 	trace_disable();
 
-	if (opts.checksum)
-		checksum_finish(func);
-
 	return ret;
 }
 
@@ -4997,10 +5031,6 @@ int check(struct objtool_file *file)
 	cfi_hash_add(&init_cfi);
 	cfi_hash_add(&func_cfi);
 
-	ret = checksum_debug_init(file);
-	if (ret)
-		goto out;
-
 	ret = decode_sections(file);
 	if (ret)
 		goto out;
@@ -5091,6 +5121,9 @@ int check(struct objtool_file *file)
 		warnings += check_abs_references(file);
 
 	if (opts.checksum) {
+		ret = calculate_checksums(file);
+		if (ret)
+			goto out;
 		ret = create_sym_checksum_section(file);
 		if (ret)
 			goto out;
diff --git a/tools/objtool/include/objtool/checksum.h b/tools/objtool/include/objtool/checksum.h
index 7fe21608722a..36a17688c716 100644
--- a/tools/objtool/include/objtool/checksum.h
+++ b/tools/objtool/include/objtool/checksum.h
@@ -32,11 +32,7 @@ static inline void checksum_finish(struct symbol *func)
 
 #else /* !BUILD_KLP */
 
-static inline void checksum_init(struct symbol *func) {}
-static inline void checksum_update(struct symbol *func,
-				   struct instruction *insn,
-				   const void *data, size_t size) {}
-static inline void checksum_finish(struct symbol *func) {}
+static inline int calculate_checksums(struct objtool_file *file) { return -ENOSYS; }
 
 #endif /* !BUILD_KLP */
 
-- 
2.53.0
Re: [PATCH 07/14] objtool: Extricate checksum calculation from validate_branch()
Posted by Miroslav Benes 1 month ago
Hi,

> @@ -3691,9 +3691,30 @@ static void checksum_update_insn(struct objtool_file *file, struct symbol *func,
>  				 struct instruction *insn)
>  {
>  	struct reloc *reloc = insn_reloc(file, insn);
> +	struct alternative *alt;
>  	unsigned long offset;
>  	struct symbol *sym;
>  
> +	for (alt = insn->alts; alt; alt = alt->next) {
> +		struct alt_group *alt_group = alt->insn->alt_group;
> +
> +		checksum_update(func, insn, &alt->type, sizeof(alt->type));
> +
> +		if (alt_group && alt_group->orig_group) {
> +			struct instruction *alt_insn;
> +
> +			checksum_update(func, insn, &alt_group->feature, sizeof(alt_group->feature));
> +
> +			for (alt_insn = alt->insn; alt_insn; alt_insn = next_insn_same_sec(file, alt_insn)) {
> +				checksum_update_insn(file, func, alt_insn);
> +				if (alt_insn == alt_group->last_insn)
> +					break;
> +			}
> +		} else {
> +			checksum_update(func, insn, &alt->insn->offset, sizeof(alt->insn->offset));
> +		}
> +	}
> +

does this hunk belong to the patch? Unless I am missing something, it 
might be worth a separate one.

Miroslav
Re: [PATCH 07/14] objtool: Extricate checksum calculation from validate_branch()
Posted by Josh Poimboeuf 1 month ago
On Tue, Mar 10, 2026 at 11:47:41AM +0100, Miroslav Benes wrote:
> Hi,
> 
> > @@ -3691,9 +3691,30 @@ static void checksum_update_insn(struct objtool_file *file, struct symbol *func,
> >  				 struct instruction *insn)
> >  {
> >  	struct reloc *reloc = insn_reloc(file, insn);
> > +	struct alternative *alt;
> >  	unsigned long offset;
> >  	struct symbol *sym;
> >  
> > +	for (alt = insn->alts; alt; alt = alt->next) {
> > +		struct alt_group *alt_group = alt->insn->alt_group;
> > +
> > +		checksum_update(func, insn, &alt->type, sizeof(alt->type));
> > +
> > +		if (alt_group && alt_group->orig_group) {
> > +			struct instruction *alt_insn;
> > +
> > +			checksum_update(func, insn, &alt_group->feature, sizeof(alt_group->feature));
> > +
> > +			for (alt_insn = alt->insn; alt_insn; alt_insn = next_insn_same_sec(file, alt_insn)) {
> > +				checksum_update_insn(file, func, alt_insn);
> > +				if (alt_insn == alt_group->last_insn)
> > +					break;
> > +			}
> > +		} else {
> > +			checksum_update(func, insn, &alt->insn->offset, sizeof(alt->insn->offset));
> > +		}
> > +	}
> > +
> 
> does this hunk belong to the patch? Unless I am missing something, it 
> might be worth a separate one.

It belongs, but I should have clarified that in the patch description.

This hunk wasn't needed before because validate_branch() already
iterates all the alternatives, so it was calling checksum_update_insn()
for every instruction in the function, including the alternatives.

Now that it's no longer called by validate_branch(),
checksum_update_insn() has to manually iterate the alternatives.

-- 
Josh
Re: [PATCH 07/14] objtool: Extricate checksum calculation from validate_branch()
Posted by Miroslav Benes 4 weeks, 1 day ago
On Tue, 10 Mar 2026, Josh Poimboeuf wrote:

> On Tue, Mar 10, 2026 at 11:47:41AM +0100, Miroslav Benes wrote:
> > Hi,
> > 
> > > @@ -3691,9 +3691,30 @@ static void checksum_update_insn(struct objtool_file *file, struct symbol *func,
> > >  				 struct instruction *insn)
> > >  {
> > >  	struct reloc *reloc = insn_reloc(file, insn);
> > > +	struct alternative *alt;
> > >  	unsigned long offset;
> > >  	struct symbol *sym;
> > >  
> > > +	for (alt = insn->alts; alt; alt = alt->next) {
> > > +		struct alt_group *alt_group = alt->insn->alt_group;
> > > +
> > > +		checksum_update(func, insn, &alt->type, sizeof(alt->type));
> > > +
> > > +		if (alt_group && alt_group->orig_group) {
> > > +			struct instruction *alt_insn;
> > > +
> > > +			checksum_update(func, insn, &alt_group->feature, sizeof(alt_group->feature));
> > > +
> > > +			for (alt_insn = alt->insn; alt_insn; alt_insn = next_insn_same_sec(file, alt_insn)) {
> > > +				checksum_update_insn(file, func, alt_insn);
> > > +				if (alt_insn == alt_group->last_insn)
> > > +					break;
> > > +			}
> > > +		} else {
> > > +			checksum_update(func, insn, &alt->insn->offset, sizeof(alt->insn->offset));
> > > +		}
> > > +	}
> > > +
> > 
> > does this hunk belong to the patch? Unless I am missing something, it 
> > might be worth a separate one.
> 
> It belongs, but I should have clarified that in the patch description.
> 
> This hunk wasn't needed before because validate_branch() already
> iterates all the alternatives, so it was calling checksum_update_insn()
> for every instruction in the function, including the alternatives.
> 
> Now that it's no longer called by validate_branch(),
> checksum_update_insn() has to manually iterate the alternatives.

After writing the email I had a suspicion it must have been something like 
above but failed to find it. Now I see that next_insn_to_validate() called 
in do_validate_branch() handles exactly that. Thanks for the pointer. The 
patch looks good to me then (and the rest as well as far as I can judge).

I must admit that objtool has gotten so complex that I have a hard time to 
track everything in the code :).

Regards
Miroslav
Re: [PATCH 07/14] objtool: Extricate checksum calculation from validate_branch()
Posted by Josh Poimboeuf 3 weeks, 2 days ago
On Wed, Mar 11, 2026 at 09:24:22AM +0100, Miroslav Benes wrote:
> On Tue, 10 Mar 2026, Josh Poimboeuf wrote:
> 
> > On Tue, Mar 10, 2026 at 11:47:41AM +0100, Miroslav Benes wrote:
> > > Hi,
> > > 
> > > > @@ -3691,9 +3691,30 @@ static void checksum_update_insn(struct objtool_file *file, struct symbol *func,
> > > >  				 struct instruction *insn)
> > > >  {
> > > >  	struct reloc *reloc = insn_reloc(file, insn);
> > > > +	struct alternative *alt;
> > > >  	unsigned long offset;
> > > >  	struct symbol *sym;
> > > >  
> > > > +	for (alt = insn->alts; alt; alt = alt->next) {
> > > > +		struct alt_group *alt_group = alt->insn->alt_group;
> > > > +
> > > > +		checksum_update(func, insn, &alt->type, sizeof(alt->type));
> > > > +
> > > > +		if (alt_group && alt_group->orig_group) {
> > > > +			struct instruction *alt_insn;
> > > > +
> > > > +			checksum_update(func, insn, &alt_group->feature, sizeof(alt_group->feature));
> > > > +
> > > > +			for (alt_insn = alt->insn; alt_insn; alt_insn = next_insn_same_sec(file, alt_insn)) {
> > > > +				checksum_update_insn(file, func, alt_insn);
> > > > +				if (alt_insn == alt_group->last_insn)
> > > > +					break;
> > > > +			}
> > > > +		} else {
> > > > +			checksum_update(func, insn, &alt->insn->offset, sizeof(alt->insn->offset));
> > > > +		}
> > > > +	}
> > > > +
> > > 
> > > does this hunk belong to the patch? Unless I am missing something, it 
> > > might be worth a separate one.
> > 
> > It belongs, but I should have clarified that in the patch description.
> > 
> > This hunk wasn't needed before because validate_branch() already
> > iterates all the alternatives, so it was calling checksum_update_insn()
> > for every instruction in the function, including the alternatives.
> > 
> > Now that it's no longer called by validate_branch(),
> > checksum_update_insn() has to manually iterate the alternatives.
> 
> After writing the email I had a suspicion it must have been something like 
> above but failed to find it. Now I see that next_insn_to_validate() called 
> in do_validate_branch() handles exactly that. Thanks for the pointer. The 
> patch looks good to me then (and the rest as well as far as I can judge).

Actually, next_insn_to_validate() helps with an edge case for directing
code flow from the end of an alternative back to the original code.

The code which traverses the alternatives is in validate_insn():

	if (insn->alts) {
		for (alt = insn->alts; alt; alt = alt->next) {
			TRACE_ALT_BEGIN(insn, alt, alt_name);
			ret = validate_branch(file, func, alt->insn, *statep);
			TRACE_ALT_END(insn, alt, alt_name);
			if (ret) {
				BT_INSN(insn, "(alt)");
				return ret;
			}
		}
		TRACE_ALT_INFO_NOADDR(insn, "/ ", "DEFAULT");
	}

> I must admit that objtool has gotten so complex that I have a hard time to 
> track everything in the code :).

The code hasn't changed *too* much, it's just that validate_branch() got
split up more when the tracing code went in, so things are organized a
bit differently.  Most of that code is now in validate_insn().

-- 
Josh