Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit da975dd4 authored by Zong Li's avatar Zong Li Committed by Palmer Dabbelt
Browse files

RISC-V: Support GOT_HI20/CALL_PLT relocation type in kernel module



For CALL_PLT, emit the plt entry only when offset is more than 32-bit.

For PCREL_LO12, it uses the location of corresponding HI20 to
get the address of external symbol. It should check the HI20 type
is the PCREL_HI20 or GOT_HI20, because sometime the location will
have two or more relocation types.
For example:
0:   00000797                auipc   a5,0x0
                     0: R_RISCV_ALIGN        *ABS*
                     0: R_RISCV_GOT_HI20     SYMBOL
4:   0007b783                ld      a5,0(a5) # 0 <SYMBOL>
                     4: R_RISCV_PCREL_LO12_I .L0
                     4: R_RISCV_RELAX        *ABS*

Signed-off-by: default avatarZong Li <zong@andestech.com>
Signed-off-by: default avatarPalmer Dabbelt <palmer@sifive.com>
parent b8bde0ef
Loading
Loading
Loading
Loading
+52 −10
Original line number Diff line number Diff line
@@ -92,6 +92,28 @@ static int apply_r_riscv_pcrel_lo12_s_rela(struct module *me, u32 *location,
	return 0;
}

static int apply_r_riscv_got_hi20_rela(struct module *me, u32 *location,
				       Elf_Addr v)
{
	s64 offset = (void *)v - (void *)location;
	s32 hi20;

	/* Always emit the got entry */
	if (IS_ENABLED(CONFIG_MODULE_SECTIONS)) {
		offset = module_emit_got_entry(me, v);
		offset = (void *)offset - (void *)location;
	} else {
		pr_err(
		  "%s: can not generate the GOT entry for symbol = %016llx from PC = %p\n",
		  me->name, v, location);
		return -EINVAL;
	}

	hi20 = (offset + 0x800) & 0xfffff000;
	*location = (*location & 0xfff) | hi20;
	return 0;
}

static int apply_r_riscv_call_plt_rela(struct module *me, u32 *location,
				       Elf_Addr v)
{
@@ -100,11 +122,17 @@ static int apply_r_riscv_call_plt_rela(struct module *me, u32 *location,
	u32 hi20, lo12;

	if (offset != fill_v) {
		/* Only emit the plt entry if offset over 32-bit range */
		if (IS_ENABLED(CONFIG_MODULE_SECTIONS)) {
			offset = module_emit_plt_entry(me, v);
			offset = (void *)offset - (void *)location;
		} else {
			pr_err(
			  "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n",
			  me->name, v, location);
			return -EINVAL;
		}
	}

	hi20 = (offset + 0x800) & 0xfffff000;
	lo12 = (offset - hi20) & 0xfff;
@@ -127,6 +155,7 @@ static int (*reloc_handlers_rela[]) (struct module *me, u32 *location,
	[R_RISCV_PCREL_HI20]		= apply_r_riscv_pcrel_hi20_rela,
	[R_RISCV_PCREL_LO12_I]		= apply_r_riscv_pcrel_lo12_i_rela,
	[R_RISCV_PCREL_LO12_S]		= apply_r_riscv_pcrel_lo12_s_rela,
	[R_RISCV_GOT_HI20]		= apply_r_riscv_got_hi20_rela,
	[R_RISCV_CALL_PLT]		= apply_r_riscv_call_plt_rela,
	[R_RISCV_RELAX]			= apply_r_riscv_relax_rela,
};
@@ -184,25 +213,38 @@ int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
				u64 hi20_loc =
					sechdrs[sechdrs[relsec].sh_info].sh_addr
					+ rel[j].r_offset;
				/* Find the corresponding HI20 PC-relative relocation entry */
				if (hi20_loc == sym->st_value) {
				u32 hi20_type = ELF_RISCV_R_TYPE(rel[j].r_info);

				/* Find the corresponding HI20 relocation entry */
				if (hi20_loc == sym->st_value
				    && (hi20_type == R_RISCV_PCREL_HI20
					|| hi20_type == R_RISCV_GOT_HI20)) {
					s32 hi20, lo12;
					Elf_Sym *hi20_sym =
						(Elf_Sym *)sechdrs[symindex].sh_addr
						+ ELF_RISCV_R_SYM(rel[j].r_info);
					u64 hi20_sym_val =
						hi20_sym->st_value
						+ rel[j].r_addend;

					/* Calculate lo12 */
					s64 offset = hi20_sym_val - hi20_loc;
					s32 hi20 = (offset + 0x800) & 0xfffff000;
					s32 lo12 = offset - hi20;
					u64 offset = hi20_sym_val - hi20_loc;
					if (IS_ENABLED(CONFIG_MODULE_SECTIONS)
					    && hi20_type == R_RISCV_GOT_HI20) {
						offset = module_emit_got_entry(
							 me, hi20_sym_val);
						offset = offset - hi20_loc;
					}
					hi20 = (offset + 0x800) & 0xfffff000;
					lo12 = offset - hi20;
					v = lo12;

					break;
				}
			}
			if (j == sechdrs[relsec].sh_size / sizeof(*rel)) {
				pr_err(
				  "%s: Can not find HI20 PC-relative relocation information\n",
				  "%s: Can not find HI20 relocation information\n",
				  me->name);
				return -EINVAL;
			}