[PATCH v4 13/14] objtool/klp: Keep LoongArch tablejump annotation table entries

From: George Guo

Date: Fri Jul 24 2026 - 07:57:49 EST


From: George Guo <guodongtai@xxxxxxxxxx>

With -mannotate-tablejump, LoongArch compilers emit a
.discard.tablejump_annotate section. Each entry is a pair of 8-byte
words: the address of a jump instruction and the address of its jump
table. objtool reads these pairs to find switch jump tables when
decoding.

klp-diff creates one fake symbol per 8-byte word and clones a word only
if should_keep_special_sym() accepts it. The default rule keeps a word
only if it references a function that was cloned into the output
module. The instruction-side word references the function and is
kept. The table-side word references the jump table via its .rodata
section symbol, which is not a function symbol, so it is dropped.

The cloned annotate section then holds only instruction-side words,
compacted together. The pairing is destroyed. Parsing the malformed
section crashes objtool on the patch module and no .ko is produced:

Building patch module: livepatch-shadow-newpid.ko
livepatch-shadow-newpid.o: error: SIGSEGV: objtool crash!

Keep all .discard.tablejump_annotate words whose referenced symbol has
been cloned.

Reproduced with the shadow-newpid test, which patches
proc_pid_status(). That function contains two switch jump tables.

Before, klp-diff clones only the instruction-side words:

DEBUG: vmlinux.o: _discard_tablejump_annotate_57441 [+DATA]
DEBUG: vmlinux.o: .discard.tablejump_annotate+0x0: proc_pid_status+0xc70 [FUNC GLOBAL]
DEBUG: vmlinux.o: _discard_tablejump_annotate_57443 [+DATA]
DEBUG: vmlinux.o: .discard.tablejump_annotate+0x8: proc_pid_status+0xd2c [FUNC GLOBAL]

After, the full pairs are kept, including the .rodata table words:

DEBUG: vmlinux.o: _discard_tablejump_annotate_57441 [+DATA]
DEBUG: vmlinux.o: .discard.tablejump_annotate+0x0: proc_pid_status+0xc70 [FUNC GLOBAL]
DEBUG: vmlinux.o: _discard_tablejump_annotate_57442 [+DATA]
DEBUG: vmlinux.o: .discard.tablejump_annotate+0x8: .rodata.proc_pid_status+0x0 [SECTION]
DEBUG: vmlinux.o: _discard_tablejump_annotate_57443 [+DATA]
DEBUG: vmlinux.o: .discard.tablejump_annotate+0x10: proc_pid_status+0xd2c [FUNC GLOBAL]
DEBUG: vmlinux.o: _discard_tablejump_annotate_57444 [+DATA]
DEBUG: vmlinux.o: .discard.tablejump_annotate+0x18: .rodata.proc_pid_status+0x80 [SECTION]

and the patch module builds.

Co-developed-by: Kexin Liu <liukexin@xxxxxxxxxx>
Signed-off-by: Kexin Liu <liukexin@xxxxxxxxxx>
Signed-off-by: George Guo <guodongtai@xxxxxxxxxx>
---
tools/objtool/klp-diff.c | 11 +++++++++++
1 file changed, 11 insertions(+)

diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index 92cf0fc3ff2f..f151ffc71184 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -1956,6 +1956,7 @@ static int create_fake_symbols(struct elf *elf)
static bool should_keep_special_sym(struct elf *elf, struct symbol *sym)
{
bool annotate_insn = !strcmp(sym->sec->name, ".discard.annotate_insn");
+ bool tablejump_annotate = !strcmp(sym->sec->name, ".discard.tablejump_annotate");
struct reloc *reloc;

if (is_sec_sym(sym) || !sym->sec->rsec)
@@ -1968,6 +1969,16 @@ static bool should_keep_special_sym(struct elf *elf, struct symbol *sym)
if (!reloc->sym->clone || is_undef_sym(reloc->sym->clone))
continue;

+ /*
+ * .discard.tablejump_annotate (LoongArch -mannotate-tablejump)
+ * holds pairs of words: a jump instruction and its jump table.
+ * The table word references the table via its .rodata section
+ * symbol, which the is_func_sym() rule below would drop,
+ * breaking the pairing. Keep both words of each entry.
+ */
+ if (tablejump_annotate)
+ return true;
+
/*
* Keep special section references to cloned functions.
* In some cases annotate_insn can also reference cloned alt
--
2.53.0