objtool: Handle PC relative relocation type
authorTiezhu Yang <yangtiezhu@loongson.cn>
Tue, 11 Feb 2025 11:50:12 +0000 (19:50 +0800)
committerJosh Poimboeuf <jpoimboe@kernel.org>
Wed, 12 Mar 2025 22:43:38 +0000 (15:43 -0700)
For the most part, an absolute relocation type is used for rodata.
In the case of STT_SECTION, reloc->sym->offset is always zero, for
the other symbol types, reloc_addend(reloc) is always zero, thus it
can use a simple statement "reloc->sym->offset + reloc_addend(reloc)"
to obtain the symbol offset for various symbol types.

When compiling on LoongArch, there exist PC relative relocation types
for rodata, it needs to calculate the symbol offset with "S + A - PC"
according to the spec of "ELF for the LoongArch Architecture".

If there is only one jump table in the rodata, the "PC" is the entry
address which is equal with the value of reloc_offset(reloc), at this
time, reloc_offset(table) is 0.

If there are many jump tables in the rodata, the "PC" is the offset
of the jump table's base address which is equal with the value of
reloc_offset(reloc) - reloc_offset(table).

So for LoongArch, if the relocation type is PC relative, it can use a
statement "reloc_offset(reloc) - reloc_offset(table)" to get the "PC"
value when calculating the symbol offset with "S + A - PC" for one or
many jump tables in the rodata.

Add an arch-specific function arch_jump_table_sym_offset() to assign
the symbol offset, for the most part that is an absolute relocation,
the default value is "reloc->sym->offset + reloc_addend(reloc)" in
the weak definition, it can be overridden by each architecture that
has different requirements.

Link: https://github.com/loongson/la-abi-specs/blob/release/laelf.adoc
Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
Link: https://lore.kernel.org/r/20250211115016.26913-4-yangtiezhu@loongson.cn
Acked-by: Huacai Chen <chenhuacai@loongson.cn>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
tools/objtool/arch/loongarch/decode.c
tools/objtool/arch/loongarch/include/arch/elf.h
tools/objtool/check.c
tools/objtool/include/objtool/arch.h

index b64205b89f6b468252a59f548d6011621ad81010..02e49055596674548be3c85d042dd7ab40dcc790 100644 (file)
@@ -5,10 +5,7 @@
 #include <asm/inst.h>
 #include <asm/orc_types.h>
 #include <linux/objtool_types.h>
-
-#ifndef EM_LOONGARCH
-#define EM_LOONGARCH   258
-#endif
+#include <arch/elf.h>
 
 int arch_ftrace_match(char *name)
 {
@@ -374,3 +371,15 @@ unsigned int arch_reloc_size(struct reloc *reloc)
                return 8;
        }
 }
+
+unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct reloc *table)
+{
+       switch (reloc_type(reloc)) {
+       case R_LARCH_32_PCREL:
+       case R_LARCH_64_PCREL:
+               return reloc->sym->offset + reloc_addend(reloc) -
+                      (reloc_offset(reloc) - reloc_offset(table));
+       default:
+               return reloc->sym->offset + reloc_addend(reloc);
+       }
+}
index 9623d663220effd0422b0d9372aba6a6c5c00a19..ec79062c9554dbc818a3e56ad438ab4b8ef584a2 100644 (file)
 #ifndef R_LARCH_32_PCREL
 #define R_LARCH_32_PCREL       99
 #endif
+#ifndef R_LARCH_64_PCREL
+#define R_LARCH_64_PCREL       109
+#endif
+
+#ifndef EM_LOONGARCH
+#define EM_LOONGARCH           258
+#endif
 
 #define R_NONE                 R_LARCH_NONE
 #define R_ABS32                        R_LARCH_32
index f762d231c747dbb256c6dc6bd1a1d37a384b77d4..7dbf22c6da9d4681a6011f51b35499b5190f094e 100644 (file)
@@ -1944,6 +1944,11 @@ out:
        return ret;
 }
 
+__weak unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct reloc *table)
+{
+       return reloc->sym->offset + reloc_addend(reloc);
+}
+
 static int add_jump_table(struct objtool_file *file, struct instruction *insn,
                          struct reloc *next_table)
 {
@@ -1972,7 +1977,7 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn,
                if (prev_offset && reloc_offset(reloc) != prev_offset + arch_reloc_size(reloc))
                        break;
 
-               sym_offset = reloc->sym->offset + reloc_addend(reloc);
+               sym_offset = arch_jump_table_sym_offset(reloc, table);
 
                /* Detect function pointers from contiguous objects: */
                if (reloc->sym->sec == pfunc->sec && sym_offset == pfunc->offset)
index 396f7c6c81c0f824863e658aec91f636803833e1..089a1acc48a8d04d0523b852e698a002c39ca8b2 100644 (file)
@@ -98,5 +98,6 @@ int arch_rewrite_retpolines(struct objtool_file *file);
 bool arch_pc_relative_reloc(struct reloc *reloc);
 
 unsigned int arch_reloc_size(struct reloc *reloc);
+unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct reloc *table);
 
 #endif /* _ARCH_H */