[PATCH v6 5/6] LoongArch: Support PC-relative relocations in modules

Xi Ruoyao posted 6 patches 3 years, 7 months ago
There is a newer version of this series
[PATCH v6 5/6] LoongArch: Support PC-relative relocations in modules
Posted by Xi Ruoyao 3 years, 7 months ago
Binutils >= 2.40 uses R_LARCH_B26 instead of R_LARCH_SOP_PUSH_PLT_PCREL,
and R_LARCH_PCALA* instead of R_LARCH_SOP_PUSH_PCREL.

Handle R_LARCH_B26 and R_LARCH_PCALA* in the module loader.  For
R_LARCH_B26, also create a PLT entry as needed.

Signed-off-by: Xi Ruoyao <xry111@xry111.site>
---
 arch/loongarch/kernel/module-sections.c |  7 ++-
 arch/loongarch/kernel/module.c          | 75 +++++++++++++++++++++++++
 2 files changed, 81 insertions(+), 1 deletion(-)

diff --git a/arch/loongarch/kernel/module-sections.c b/arch/loongarch/kernel/module-sections.c
index 6d498288977d..c67b9cb220eb 100644
--- a/arch/loongarch/kernel/module-sections.c
+++ b/arch/loongarch/kernel/module-sections.c
@@ -56,9 +56,14 @@ static void count_max_entries(Elf_Rela *relas, int num, unsigned int *plts)
 
 	for (i = 0; i < num; i++) {
 		type = ELF_R_TYPE(relas[i].r_info);
-		if (type == R_LARCH_SOP_PUSH_PLT_PCREL) {
+		switch (type) {
+		case R_LARCH_SOP_PUSH_PLT_PCREL:
+		case R_LARCH_B26:
 			if (!duplicate_rela(relas, i))
 				(*plts)++;
+			break;
+		default:
+			/* Do nothing. */
 		}
 	}
 }
diff --git a/arch/loongarch/kernel/module.c b/arch/loongarch/kernel/module.c
index 755d91ef8d85..0024bc6c4af1 100644
--- a/arch/loongarch/kernel/module.c
+++ b/arch/loongarch/kernel/module.c
@@ -281,6 +281,79 @@ static int apply_r_larch_add_sub(struct module *mod, u32 *location, Elf_Addr v,
 	}
 }
 
+static int apply_r_larch_b26(struct module *mod, u32 *location, Elf_Addr v,
+			s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
+{
+	ptrdiff_t offset = (void *)v - (void *)location;
+	union loongarch_instruction *insn = (union loongarch_instruction *)location;
+
+	if (offset >= SZ_128M)
+		v = module_emit_plt_entry(mod, v);
+
+	if (offset < -SZ_128M)
+		v = module_emit_plt_entry(mod, v);
+
+	offset = (void *)v - (void *)location;
+
+	if (offset & 3) {
+		pr_err("module %s: jump offset = 0x%llx unaligned! dangerous R_LARCH_B26 (%u) relocation\n",
+				mod->name, (long long)offset, type);
+		return -ENOEXEC;
+	}
+
+	if (!signed_imm_check(offset, 28)) {
+		pr_err("module %s: jump offset = 0x%llx overflow! dangerous R_LARCH_B26 (%u) relocation\n",
+				mod->name, (long long)offset, type);
+		return -ENOEXEC;
+	}
+
+	offset >>= 2;
+	insn->reg0i26_format.immediate_l = offset & 0xffff;
+	insn->reg0i26_format.immediate_h = (offset >> 16) & 0x3ff;
+	return 0;
+}
+
+static int apply_r_larch_pcala(struct module *mod, u32 *location, Elf_Addr v,
+			s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
+{
+	union loongarch_instruction *insn = (union loongarch_instruction *)location;
+	/* Use s32 for a sign-extension deliberately. */
+	s32 offset_hi20 = (void *)((v + 0x800) & ~0xfff) -
+		(void *)((Elf_Addr)location & ~0xfff);
+	Elf_Addr anchor = (((Elf_Addr)location) & ~0xfff) + offset_hi20;
+	ptrdiff_t offset_rem = (void *)v - (void *)anchor;
+
+	switch (type) {
+	case R_LARCH_PCALA_HI20:
+		v = offset_hi20 >> 12;
+		break;
+	case R_LARCH_PCALA64_LO20:
+		v = offset_rem >> 32;
+		break;
+	case R_LARCH_PCALA64_HI12:
+		v = offset_rem >> 52;
+		break;
+	default:
+		/* Do nothing. */
+	}
+
+	switch (type) {
+	case R_LARCH_PCALA_HI20:
+	case R_LARCH_PCALA64_LO20:
+		insn->reg1i20_format.immediate = v & 0xfffff;
+		break;
+	case R_LARCH_PCALA_LO12:
+	case R_LARCH_PCALA64_HI12:
+		insn->reg2i12_format.immediate = v & 0xfff;
+		break;
+	default:
+		pr_err("%s: Unsupport relocation type %u\n", mod->name, type);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 /*
  * reloc_handlers_rela() - Apply a particular relocation to a module
  * @mod: the module to apply the reloc to
@@ -310,6 +383,8 @@ static reloc_rela_handler reloc_rela_handlers[] = {
 	[R_LARCH_SOP_SUB ... R_LARCH_SOP_IF_ELSE] 	     = apply_r_larch_sop,
 	[R_LARCH_SOP_POP_32_S_10_5 ... R_LARCH_SOP_POP_32_U] = apply_r_larch_sop_imm_field,
 	[R_LARCH_ADD32 ... R_LARCH_SUB64]		     = apply_r_larch_add_sub,
+	[R_LARCH_B26]					     = apply_r_larch_b26,
+	[R_LARCH_PCALA_HI20...R_LARCH_PCALA64_HI12]	     = apply_r_larch_pcala,
 };
 
 int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
-- 
2.37.0
Re: [PATCH v6 5/6] LoongArch: Support PC-relative relocations in modules
Posted by Huacai Chen 3 years, 7 months ago
Hi, Ruoyao,

On Mon, Aug 29, 2022 at 9:35 PM Xi Ruoyao <xry111@xry111.site> wrote:
>
> Binutils >= 2.40 uses R_LARCH_B26 instead of R_LARCH_SOP_PUSH_PLT_PCREL,
> and R_LARCH_PCALA* instead of R_LARCH_SOP_PUSH_PCREL.
>
> Handle R_LARCH_B26 and R_LARCH_PCALA* in the module loader.  For
> R_LARCH_B26, also create a PLT entry as needed.
>
> Signed-off-by: Xi Ruoyao <xry111@xry111.site>
> ---
>  arch/loongarch/kernel/module-sections.c |  7 ++-
>  arch/loongarch/kernel/module.c          | 75 +++++++++++++++++++++++++
>  2 files changed, 81 insertions(+), 1 deletion(-)
>
> diff --git a/arch/loongarch/kernel/module-sections.c b/arch/loongarch/kernel/module-sections.c
> index 6d498288977d..c67b9cb220eb 100644
> --- a/arch/loongarch/kernel/module-sections.c
> +++ b/arch/loongarch/kernel/module-sections.c
> @@ -56,9 +56,14 @@ static void count_max_entries(Elf_Rela *relas, int num, unsigned int *plts)
>
>         for (i = 0; i < num; i++) {
>                 type = ELF_R_TYPE(relas[i].r_info);
> -               if (type == R_LARCH_SOP_PUSH_PLT_PCREL) {
> +               switch (type) {
> +               case R_LARCH_SOP_PUSH_PLT_PCREL:
> +               case R_LARCH_B26:
>                         if (!duplicate_rela(relas, i))
>                                 (*plts)++;
> +                       break;
> +               default:
> +                       /* Do nothing. */
>                 }
>         }
>  }
> diff --git a/arch/loongarch/kernel/module.c b/arch/loongarch/kernel/module.c
> index 755d91ef8d85..0024bc6c4af1 100644
> --- a/arch/loongarch/kernel/module.c
> +++ b/arch/loongarch/kernel/module.c
> @@ -281,6 +281,79 @@ static int apply_r_larch_add_sub(struct module *mod, u32 *location, Elf_Addr v,
>         }
>  }
>
> +static int apply_r_larch_b26(struct module *mod, u32 *location, Elf_Addr v,
> +                       s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
> +{
> +       ptrdiff_t offset = (void *)v - (void *)location;
> +       union loongarch_instruction *insn = (union loongarch_instruction *)location;
> +
> +       if (offset >= SZ_128M)
> +               v = module_emit_plt_entry(mod, v);
> +
> +       if (offset < -SZ_128M)
> +               v = module_emit_plt_entry(mod, v);
> +
> +       offset = (void *)v - (void *)location;
> +
> +       if (offset & 3) {
> +               pr_err("module %s: jump offset = 0x%llx unaligned! dangerous R_LARCH_B26 (%u) relocation\n",
> +                               mod->name, (long long)offset, type);
> +               return -ENOEXEC;
> +       }
> +
> +       if (!signed_imm_check(offset, 28)) {
> +               pr_err("module %s: jump offset = 0x%llx overflow! dangerous R_LARCH_B26 (%u) relocation\n",
> +                               mod->name, (long long)offset, type);
> +               return -ENOEXEC;
> +       }
> +
> +       offset >>= 2;
> +       insn->reg0i26_format.immediate_l = offset & 0xffff;
> +       insn->reg0i26_format.immediate_h = (offset >> 16) & 0x3ff;
> +       return 0;
> +}
> +
> +static int apply_r_larch_pcala(struct module *mod, u32 *location, Elf_Addr v,
> +                       s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
> +{
> +       union loongarch_instruction *insn = (union loongarch_instruction *)location;
> +       /* Use s32 for a sign-extension deliberately. */
> +       s32 offset_hi20 = (void *)((v + 0x800) & ~0xfff) -
> +               (void *)((Elf_Addr)location & ~0xfff);
> +       Elf_Addr anchor = (((Elf_Addr)location) & ~0xfff) + offset_hi20;
> +       ptrdiff_t offset_rem = (void *)v - (void *)anchor;
> +
> +       switch (type) {
> +       case R_LARCH_PCALA_HI20:
> +               v = offset_hi20 >> 12;
> +               break;
> +       case R_LARCH_PCALA64_LO20:
> +               v = offset_rem >> 32;
> +               break;
> +       case R_LARCH_PCALA64_HI12:
> +               v = offset_rem >> 52;
> +               break;
> +       default:
> +               /* Do nothing. */
> +       }
> +
> +       switch (type) {
> +       case R_LARCH_PCALA_HI20:
> +       case R_LARCH_PCALA64_LO20:
> +               insn->reg1i20_format.immediate = v & 0xfffff;
> +               break;
> +       case R_LARCH_PCALA_LO12:
> +       case R_LARCH_PCALA64_HI12:
> +               insn->reg2i12_format.immediate = v & 0xfff;
> +               break;
> +       default:
> +               pr_err("%s: Unsupport relocation type %u\n", mod->name, type);
> +               return -EINVAL;
> +       }
Can we merge the two switch here?

Huacai
> +
> +       return 0;
> +}
> +
>  /*
>   * reloc_handlers_rela() - Apply a particular relocation to a module
>   * @mod: the module to apply the reloc to
> @@ -310,6 +383,8 @@ static reloc_rela_handler reloc_rela_handlers[] = {
>         [R_LARCH_SOP_SUB ... R_LARCH_SOP_IF_ELSE]            = apply_r_larch_sop,
>         [R_LARCH_SOP_POP_32_S_10_5 ... R_LARCH_SOP_POP_32_U] = apply_r_larch_sop_imm_field,
>         [R_LARCH_ADD32 ... R_LARCH_SUB64]                    = apply_r_larch_add_sub,
> +       [R_LARCH_B26]                                        = apply_r_larch_b26,
> +       [R_LARCH_PCALA_HI20...R_LARCH_PCALA64_HI12]          = apply_r_larch_pcala,
>  };
>
>  int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
> --
> 2.37.0
>
>
Re: [PATCH v6 5/6] LoongArch: Support PC-relative relocations in modules
Posted by WANG Xuerui 3 years, 7 months ago
On 2022/8/29 23:08, Huacai Chen wrote:
> [snip]
>> +static int apply_r_larch_pcala(struct module *mod, u32 *location, Elf_Addr v,
>> +                       s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
>> +{
>> +       union loongarch_instruction *insn = (union loongarch_instruction *)location;
>> +       /* Use s32 for a sign-extension deliberately. */
>> +       s32 offset_hi20 = (void *)((v + 0x800) & ~0xfff) -
>> +               (void *)((Elf_Addr)location & ~0xfff);
>> +       Elf_Addr anchor = (((Elf_Addr)location) & ~0xfff) + offset_hi20;
>> +       ptrdiff_t offset_rem = (void *)v - (void *)anchor;
>> +
>> +       switch (type) {
>> +       case R_LARCH_PCALA_HI20:
>> +               v = offset_hi20 >> 12;
>> +               break;
>> +       case R_LARCH_PCALA64_LO20:
>> +               v = offset_rem >> 32;
>> +               break;
>> +       case R_LARCH_PCALA64_HI12:
>> +               v = offset_rem >> 52;
>> +               break;
>> +       default:
>> +               /* Do nothing. */
>> +       }
>> +
>> +       switch (type) {
>> +       case R_LARCH_PCALA_HI20:
>> +       case R_LARCH_PCALA64_LO20:
>> +               insn->reg1i20_format.immediate = v & 0xfffff;
>> +               break;
>> +       case R_LARCH_PCALA_LO12:
>> +       case R_LARCH_PCALA64_HI12:
>> +               insn->reg2i12_format.immediate = v & 0xfff;
>> +               break;
>> +       default:
>> +               pr_err("%s: Unsupport relocation type %u\n", mod->name, type);
>> +               return -EINVAL;
>> +       }
> Can we merge the two switch here?

IMO leaving as-is or even splitting into two functions would be 
acceptable, as the two switches are performing two different things -- 
namely "adjustFixupValue" (in LLVM-speak) and actually inserting the 
value into the insn word. But an argument for merging the two can be 
made too, because the v2.00 reloc types are purposely designed with 
unique use case for each, meaning there is actually no flexibility in 
between the fixup value's calculation and application. So I think this 
eventually comes down to coder's preference?

-- 
WANG "xen0n" Xuerui

Linux/LoongArch mailing list: https://lore.kernel.org/loongarch/
Re: [PATCH v6 5/6] LoongArch: Support PC-relative relocations in modules
Posted by Huacai Chen 3 years, 7 months ago
On Tue, Aug 30, 2022 at 9:42 AM WANG Xuerui <kernel@xen0n.name> wrote:
>
> On 2022/8/29 23:08, Huacai Chen wrote:
> > [snip]
> >> +static int apply_r_larch_pcala(struct module *mod, u32 *location, Elf_Addr v,
> >> +                       s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
> >> +{
> >> +       union loongarch_instruction *insn = (union loongarch_instruction *)location;
> >> +       /* Use s32 for a sign-extension deliberately. */
> >> +       s32 offset_hi20 = (void *)((v + 0x800) & ~0xfff) -
> >> +               (void *)((Elf_Addr)location & ~0xfff);
> >> +       Elf_Addr anchor = (((Elf_Addr)location) & ~0xfff) + offset_hi20;
> >> +       ptrdiff_t offset_rem = (void *)v - (void *)anchor;
> >> +
> >> +       switch (type) {
> >> +       case R_LARCH_PCALA_HI20:
> >> +               v = offset_hi20 >> 12;
> >> +               break;
> >> +       case R_LARCH_PCALA64_LO20:
> >> +               v = offset_rem >> 32;
> >> +               break;
> >> +       case R_LARCH_PCALA64_HI12:
> >> +               v = offset_rem >> 52;
> >> +               break;
> >> +       default:
> >> +               /* Do nothing. */
> >> +       }
> >> +
> >> +       switch (type) {
> >> +       case R_LARCH_PCALA_HI20:
> >> +       case R_LARCH_PCALA64_LO20:
> >> +               insn->reg1i20_format.immediate = v & 0xfffff;
> >> +               break;
> >> +       case R_LARCH_PCALA_LO12:
> >> +       case R_LARCH_PCALA64_HI12:
> >> +               insn->reg2i12_format.immediate = v & 0xfff;
> >> +               break;
> >> +       default:
> >> +               pr_err("%s: Unsupport relocation type %u\n", mod->name, type);
> >> +               return -EINVAL;
> >> +       }
> > Can we merge the two switch here?
>
> IMO leaving as-is or even splitting into two functions would be
> acceptable, as the two switches are performing two different things --
> namely "adjustFixupValue" (in LLVM-speak) and actually inserting the
> value into the insn word. But an argument for merging the two can be
> made too, because the v2.00 reloc types are purposely designed with
> unique use case for each, meaning there is actually no flexibility in
> between the fixup value's calculation and application. So I think this
> eventually comes down to coder's preference?
Merging them can just make me understand the logic better, and save
some lines. :)

Huacai
>
> --
> WANG "xen0n" Xuerui
>
> Linux/LoongArch mailing list: https://lore.kernel.org/loongarch/
>