[PATCH v3 12/12] objtool/klp: Fold LoongArch paired ADD/SUB relocations into PCREL
From: George Guo <hidden>
Date: 2026-07-07 07:23:48
Also in:
lkml, llvm, loongarch
Subsystem:
objtool, the rest · Maintainers:
Josh Poimboeuf, Peter Zijlstra, Linus Torvalds
From: George Guo <redacted> On LoongArch, the "key - ." field of a __jump_table entry can come out as two relocations at the same offset, R_LARCH_ADD64 plus R_LARCH_SUB64. clang's integrated assembler emits this pair when the key symbol is not defined in the same translation unit (for example __tracepoint_netif_rx); GAS, and clang for locally-defined keys, produce a single R_LARCH_64_PCREL instead, which is why GCC builds do not hit this. objtool's elf_create_reloc() allows only one relocation per offset, so cloning such an entry fails with: vmlinux.o: error: objtool: __jump_table_23683+0x8: duplicate reloc When the SUB half points at the relocation's own position (the "sym - ." pattern), the pair means the same thing as one PC-relative relocation. Add arch_normalize_paired_reloc() which rewrites the ADD half to R_LARCH_32_PCREL/R_LARCH_64_PCREL and skips the SUB half. The LoongArch module loader supports both forms. Reported-by: Joe Lawrence <joe.lawrence@redhat.com> Signed-off-by: George Guo <redacted> --- tools/objtool/arch/loongarch/decode.c | 62 +++++++++++++++++++ .../objtool/arch/loongarch/include/arch/elf.h | 18 ++++++ tools/objtool/klp-diff.c | 13 ++++ 3 files changed, 93 insertions(+)
diff --git a/tools/objtool/arch/loongarch/decode.c b/tools/objtool/arch/loongarch/decode.c
index 12facd0cc8d1..273398ea4486 100644
--- a/tools/objtool/arch/loongarch/decode.c
+++ b/tools/objtool/arch/loongarch/decode.c@@ -39,6 +39,68 @@ u64 arch_adjusted_addend(struct reloc *reloc) return reloc_addend(reloc); } +/* + * A cross-section difference like the "key - ." field of a __jump_table + * entry may come out as a paired R_LARCH_ADD plus R_LARCH_SUB relocation + * at the same offset: clang's integrated assembler does this when the + * symbol is not defined in the same translation unit (GAS, and clang for + * locally-defined symbols, emit a single PCREL instead). objtool allows + * only one relocation per offset, so the pair breaks cloning. + * + * When the SUB half points at the reloc's own position ("sym - ."), the + * pair means the same as a single PC-relative relocation, which the + * module loader also supports: rewrite the ADD half to R_LARCH_*_PCREL + * and tell the caller to skip the SUB half. + * + * Return 1 to skip the reloc, 0 to proceed, -1 on error. + */ +int arch_normalize_paired_reloc(struct elf *elf, struct reloc *reloc) +{ + struct section *rsec = reloc->sec; + unsigned int sub_type, pcrel_type; + struct reloc *sub, *add; + + switch (reloc_type(reloc)) { + case R_LARCH_ADD32: + sub_type = R_LARCH_SUB32; + pcrel_type = R_LARCH_32_PCREL; + break; + case R_LARCH_ADD64: + sub_type = R_LARCH_SUB64; + pcrel_type = R_LARCH_64_PCREL; + break; + case R_LARCH_SUB32: + case R_LARCH_SUB64: + /* + * Skip only if the paired ADD (the preceding reloc) has + * been rewritten to PCREL; otherwise leave the pair + * intact so a failed conversion stays loud. + */ + add = reloc_idx(reloc) ? reloc - 1 : NULL; + if (add && reloc_offset(add) == reloc_offset(reloc) && + (reloc_type(add) == R_LARCH_32_PCREL || + reloc_type(add) == R_LARCH_64_PCREL)) + return 1; + return 0; + default: + return 0; + } + + /* The paired SUB reloc immediately follows the ADD */ + sub = rsec_next_reloc(rsec, reloc); + if (!sub || reloc_offset(sub) != reloc_offset(reloc) || + reloc_type(sub) != sub_type) + return 0; + + /* Only a "sym - ." difference is PC-relative */ + if (sub->sym->sec != rsec->base || + sub->sym->offset + reloc_addend(sub) != reloc_offset(sub)) + return 0; + + set_reloc_type(elf, reloc, pcrel_type); + return 0; +} + bool arch_pc_relative_reloc(struct reloc *reloc) { return false;
diff --git a/tools/objtool/arch/loongarch/include/arch/elf.h b/tools/objtool/arch/loongarch/include/arch/elf.h
index ec79062c9554..0103f27fccfc 100644
--- a/tools/objtool/arch/loongarch/include/arch/elf.h
+++ b/tools/objtool/arch/loongarch/include/arch/elf.h@@ -15,6 +15,18 @@ #ifndef R_LARCH_64 #define R_LARCH_64 2 #endif +#ifndef R_LARCH_ADD32 +#define R_LARCH_ADD32 50 +#endif +#ifndef R_LARCH_ADD64 +#define R_LARCH_ADD64 51 +#endif +#ifndef R_LARCH_SUB32 +#define R_LARCH_SUB32 55 +#endif +#ifndef R_LARCH_SUB64 +#define R_LARCH_SUB64 56 +#endif #ifndef R_LARCH_32_PCREL #define R_LARCH_32_PCREL 99 #endif
@@ -34,4 +46,10 @@ #define R_TEXT32 R_LARCH_32_PCREL #define R_TEXT64 R_LARCH_32_PCREL +#define ARCH_HAS_PAIRED_RELOCS 1 + +struct elf; +struct reloc; +int arch_normalize_paired_reloc(struct elf *elf, struct reloc *reloc); + #endif /* _OBJTOOL_ARCH_ELF_H */
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index 0fa3ce324346..02b2150e3ca3 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c@@ -1504,6 +1504,13 @@ static bool is_uncorrelated_section(struct section *sec) strstarts(sec->name, ".data..Lanon."); /* Clang */ } +#ifndef ARCH_HAS_PAIRED_RELOCS +static inline int arch_normalize_paired_reloc(struct elf *elf, struct reloc *reloc) +{ + return 0; +} +#endif + /* * Convert a relocation symbol reference to the needed format: either a section * symbol or the underlying symbol itself. Return -1 error, 0 success, 1 skip.
@@ -1511,10 +1518,16 @@ static bool is_uncorrelated_section(struct section *sec) static int convert_reloc_sym(struct elf *elf, struct reloc *reloc) { struct section *sec = reloc->sym->sec; + int ret; if (reloc_type(reloc) == R_NONE) return 1; + /* Fold paired ADD/SUB relocs (LoongArch) into a single PCREL */ + ret = arch_normalize_paired_reloc(elf, reloc); + if (ret) + return ret; + if (is_uncorrelated_section(sec)) return convert_reloc_sym_to_secsym(elf, reloc);
--
2.25.1