aboutsummaryrefslogtreecommitdiffstats
path: root/tools/objtool/check.c
diff options
context:
space:
mode:
authorPeter Zijlstra <peterz@infradead.org>2022-03-08 16:30:46 +0100
committerPeter Zijlstra <peterz@infradead.org>2022-03-15 10:32:43 +0100
commit4adb23686795e9c88e3217b5d7b4524c0da9d04f (patch)
tree3e82aa694a452dd067389d5f551d996cef0715b6 /tools/objtool/check.c
parentobjtool: Rename --duplicate to --lto (diff)
downloadlinux-dev-4adb23686795e9c88e3217b5d7b4524c0da9d04f.tar.xz
linux-dev-4adb23686795e9c88e3217b5d7b4524c0da9d04f.zip
objtool: Ignore extra-symbol code
There's a fun implementation detail on linking STB_WEAK symbols. When the linker combines two translation units, where one contains a weak function and the other an override for it. It simply strips the STB_WEAK symbol from the symbol table, but doesn't actually remove the code. The result is that when objtool is ran in a whole-archive kind of way, it will encounter *heaps* of unused (and unreferenced) code. All rudiments of weak functions. Additionally, when a weak implementation is split into a .cold subfunction that .cold symbol is left in place, even though completely unused. Teach objtool to ignore such rudiments by searching for symbol holes; that is, code ranges that fall outside the given symbol bounds. Specifically, ignore a sequence of unreachable instruction iff they occupy a single hole, additionally ignore any .cold subfunctions referenced. Both ld.bfd and ld.lld behave like this. LTO builds otoh can (and do) properly DCE weak functions. Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Acked-by: Josh Poimboeuf <jpoimboe@redhat.com> Link: https://lore.kernel.org/r/20220308154319.232019347@infradead.org
Diffstat (limited to 'tools/objtool/check.c')
-rw-r--r--tools/objtool/check.c43
1 files changed, 43 insertions, 0 deletions
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index ae1d4f996803..0e0e5b5a72c8 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -3346,6 +3346,49 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructio
!strcmp(insn->sec->name, ".altinstr_aux"))
return true;
+ /*
+ * Whole archive runs might encounder 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 (!insn->func && lto) {
+ 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->jump_dest->func &&
+ strstr(insn->jump_dest->func->name, ".cold")) {
+ struct instruction *dest = insn->jump_dest;
+ func_for_each_insn(file, dest->func, dest)
+ dest->ignore = true;
+ }
+ }
+
+ return false;
+ }
+
if (!insn->func)
return false;