]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
objtool: Fix weak symbol hole detection for .cold functions
authorJosh Poimboeuf <jpoimboe@kernel.org>
Wed, 17 Sep 2025 16:03:38 +0000 (09:03 -0700)
committerJosh Poimboeuf <jpoimboe@kernel.org>
Tue, 14 Oct 2025 21:46:47 +0000 (14:46 -0700)
When ignore_unreachable_insn() looks for weak function holes which jump
to their .cold functions, it assumes the parent function comes before
the corresponding .cold function in the symbol table.  That's not
necessarily the case with -ffunction-sections.

Mark all the holes beforehand (including .cold functions) so the
ordering of the discovery doesn't matter.

Acked-by: Petr Mladek <pmladek@suse.com>
Tested-by: Joe Lawrence <joe.lawrence@redhat.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
tools/objtool/check.c
tools/objtool/include/objtool/check.h

index 1d28ff73ebc9ebb55ec2ff358b0c8aa8f0bdfdec..86f6e4da536c2527d55335c5e0834848fa37bbbc 100644 (file)
@@ -2507,6 +2507,44 @@ static void mark_rodata(struct objtool_file *file)
        file->rodata = found;
 }
 
+static void mark_holes(struct objtool_file *file)
+{
+       struct instruction *insn;
+       bool in_hole = false;
+
+       if (!opts.link)
+               return;
+
+       /*
+        * Whole archive runs might encounter dead code from weak symbols.
+        * This is where the linker will have dropped the weak symbol in
+        * favour of a regular symbol, but leaves the code in place.
+        */
+       for_each_insn(file, insn) {
+               if (insn->sym || !find_symbol_hole_containing(insn->sec, insn->offset)) {
+                       in_hole = false;
+                       continue;
+               }
+
+               /* Skip function padding and pfx code */
+               if (!in_hole && insn->type == INSN_NOP)
+                       continue;
+
+               in_hole = true;
+               insn->hole = 1;
+
+               /*
+                * If this hole jumps to a .cold function, mark it ignore.
+                */
+               if (insn->jump_dest) {
+                       struct symbol *dest_func = insn_func(insn->jump_dest);
+
+                       if (dest_func && dest_func->cold)
+                               dest_func->ignore = true;
+               }
+       }
+}
+
 static int decode_sections(struct objtool_file *file)
 {
        mark_rodata(file);
@@ -2560,6 +2598,9 @@ static int decode_sections(struct objtool_file *file)
        if (read_unwind_hints(file))
                return -1;
 
+       /* Must be after add_jump_destinations() */
+       mark_holes(file);
+
        /*
         * Must be after add_call_destinations() such that it can override
         * dead_end_function() marks.
@@ -4021,7 +4062,8 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructio
        struct instruction *prev_insn;
        int i;
 
-       if (insn->type == INSN_NOP || insn->type == INSN_TRAP || (func && func->ignore))
+       if (insn->type == INSN_NOP || insn->type == INSN_TRAP ||
+           insn->hole || (func && func->ignore))
                return true;
 
        /*
@@ -4032,46 +4074,6 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructio
            !strcmp(insn->sec->name, ".altinstr_aux"))
                return true;
 
-       /*
-        * Whole archive runs might encounter dead code from weak symbols.
-        * This is where the linker will have dropped the weak symbol in
-        * favour of a regular symbol, but leaves the code in place.
-        *
-        * In this case we'll find a piece of code (whole function) that is not
-        * covered by a !section symbol. Ignore them.
-        */
-       if (opts.link && !func) {
-               int size = find_symbol_hole_containing(insn->sec, insn->offset);
-               unsigned long end = insn->offset + size;
-
-               if (!size) /* not a hole */
-                       return false;
-
-               if (size < 0) /* hole until the end */
-                       return true;
-
-               sec_for_each_insn_continue(file, insn) {
-                       /*
-                        * If we reach a visited instruction at or before the
-                        * end of the hole, ignore the unreachable.
-                        */
-                       if (insn->visited)
-                               return true;
-
-                       if (insn->offset >= end)
-                               break;
-
-                       /*
-                        * If this hole jumps to a .cold function, mark it ignore too.
-                        */
-                       if (insn->jump_dest && insn_func(insn->jump_dest) &&
-                           insn_func(insn->jump_dest)->cold)
-                               insn_func(insn->jump_dest)->ignore = true;
-               }
-
-               return false;
-       }
-
        if (!func)
                return false;
 
index 00fb745e723391839e822db4c4c0706b34a26b55..0f4e7ac929ef0c769c2413d2fca1436b352c1be5 100644 (file)
@@ -64,7 +64,8 @@ struct instruction {
            noendbr             : 1,
            unret               : 1,
            visited             : 4,
-           no_reloc            : 1;
+           no_reloc            : 1,
+           hole                : 1;
                /* 10 bit hole */
 
        struct alt_group *alt_group;