Commit 59b3d4a9 authored by Xi Ruoyao's avatar Xi Ruoyao Committed by Huacai Chen

LoongArch: Support R_LARCH_GOT_PC_{LO12,HI20} in modules

GCC >= 13 and GNU assembler >= 2.40 use these relocations to address
external symbols, so we need to add them.

Let the module loader emit GOT entries for data symbols so we would be
able to handle GOT relocations. The GOT entry is just the data's symbol
address.

In module.lds, emit a stub .got section for a section header entry. The
actual content of the section entry will be filled at runtime by module_
frob_arch_sections().
Tested-by: default avatarWANG Xuerui <git@xen0n.name>
Signed-off-by: default avatarXi Ruoyao <xry111@xry111.site>
Signed-off-by: default avatarHuacai Chen <chenhuacai@loongson.cn>
parent 9bd1e380
...@@ -17,10 +17,15 @@ struct mod_section { ...@@ -17,10 +17,15 @@ struct mod_section {
}; };
struct mod_arch_specific { struct mod_arch_specific {
struct mod_section got;
struct mod_section plt; struct mod_section plt;
struct mod_section plt_idx; struct mod_section plt_idx;
}; };
struct got_entry {
Elf_Addr symbol_addr;
};
struct plt_entry { struct plt_entry {
u32 inst_lu12iw; u32 inst_lu12iw;
u32 inst_lu32id; u32 inst_lu32id;
...@@ -29,10 +34,16 @@ struct plt_entry { ...@@ -29,10 +34,16 @@ struct plt_entry {
}; };
struct plt_idx_entry { struct plt_idx_entry {
unsigned long symbol_addr; Elf_Addr symbol_addr;
}; };
Elf_Addr module_emit_plt_entry(struct module *mod, unsigned long val); Elf_Addr module_emit_got_entry(struct module *mod, Elf_Addr val);
Elf_Addr module_emit_plt_entry(struct module *mod, Elf_Addr val);
static inline struct got_entry emit_got_entry(Elf_Addr val)
{
return (struct got_entry) { val };
}
static inline struct plt_entry emit_plt_entry(unsigned long val) static inline struct plt_entry emit_plt_entry(unsigned long val)
{ {
...@@ -77,4 +88,16 @@ static inline struct plt_entry *get_plt_entry(unsigned long val, ...@@ -77,4 +88,16 @@ static inline struct plt_entry *get_plt_entry(unsigned long val,
return plt + plt_idx; return plt + plt_idx;
} }
static inline struct got_entry *get_got_entry(Elf_Addr val,
const struct mod_section *sec)
{
struct got_entry *got = (struct got_entry *)sec->shdr->sh_addr;
int i;
for (i = 0; i < sec->num_entries; i++)
if (got[i].symbol_addr == val)
return &got[i];
return NULL;
}
#endif /* _ASM_MODULE_H */ #endif /* _ASM_MODULE_H */
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
/* Copyright (C) 2020-2022 Loongson Technology Corporation Limited */ /* Copyright (C) 2020-2022 Loongson Technology Corporation Limited */
SECTIONS { SECTIONS {
. = ALIGN(4); . = ALIGN(4);
.got : { BYTE(0) }
.plt : { BYTE(0) } .plt : { BYTE(0) }
.plt.idx : { BYTE(0) } .plt.idx : { BYTE(0) }
} }
...@@ -7,7 +7,33 @@ ...@@ -7,7 +7,33 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
Elf_Addr module_emit_plt_entry(struct module *mod, unsigned long val) Elf_Addr module_emit_got_entry(struct module *mod, Elf_Addr val)
{
struct mod_section *got_sec = &mod->arch.got;
int i = got_sec->num_entries;
struct got_entry *got = get_got_entry(val, got_sec);
if (got)
return (Elf_Addr)got;
/* There is no GOT entry for val yet, create a new one. */
got = (struct got_entry *)got_sec->shdr->sh_addr;
got[i] = emit_got_entry(val);
got_sec->num_entries++;
if (got_sec->num_entries > got_sec->max_entries) {
/*
* This may happen when the module contains a GOT_HI20 without
* a paired GOT_LO12. Such a module is broken, reject it.
*/
pr_err("%s: module contains bad GOT relocation\n", mod->name);
return 0;
}
return (Elf_Addr)&got[i];
}
Elf_Addr module_emit_plt_entry(struct module *mod, Elf_Addr val)
{ {
int nr; int nr;
struct mod_section *plt_sec = &mod->arch.plt; struct mod_section *plt_sec = &mod->arch.plt;
...@@ -50,7 +76,8 @@ static bool duplicate_rela(const Elf_Rela *rela, int idx) ...@@ -50,7 +76,8 @@ static bool duplicate_rela(const Elf_Rela *rela, int idx)
return false; return false;
} }
static void count_max_entries(Elf_Rela *relas, int num, unsigned int *plts) static void count_max_entries(Elf_Rela *relas, int num,
unsigned int *plts, unsigned int *gots)
{ {
unsigned int i, type; unsigned int i, type;
...@@ -62,6 +89,10 @@ static void count_max_entries(Elf_Rela *relas, int num, unsigned int *plts) ...@@ -62,6 +89,10 @@ static void count_max_entries(Elf_Rela *relas, int num, unsigned int *plts)
if (!duplicate_rela(relas, i)) if (!duplicate_rela(relas, i))
(*plts)++; (*plts)++;
break; break;
case R_LARCH_GOT_PC_HI20:
if (!duplicate_rela(relas, i))
(*gots)++;
break;
default: default:
break; /* Do nothing. */ break; /* Do nothing. */
} }
...@@ -71,18 +102,24 @@ static void count_max_entries(Elf_Rela *relas, int num, unsigned int *plts) ...@@ -71,18 +102,24 @@ static void count_max_entries(Elf_Rela *relas, int num, unsigned int *plts)
int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
char *secstrings, struct module *mod) char *secstrings, struct module *mod)
{ {
unsigned int i, num_plts = 0; unsigned int i, num_plts = 0, num_gots = 0;
/* /*
* Find the empty .plt sections. * Find the empty .plt sections.
*/ */
for (i = 0; i < ehdr->e_shnum; i++) { for (i = 0; i < ehdr->e_shnum; i++) {
if (!strcmp(secstrings + sechdrs[i].sh_name, ".plt")) if (!strcmp(secstrings + sechdrs[i].sh_name, ".got"))
mod->arch.got.shdr = sechdrs + i;
else if (!strcmp(secstrings + sechdrs[i].sh_name, ".plt"))
mod->arch.plt.shdr = sechdrs + i; mod->arch.plt.shdr = sechdrs + i;
else if (!strcmp(secstrings + sechdrs[i].sh_name, ".plt.idx")) else if (!strcmp(secstrings + sechdrs[i].sh_name, ".plt.idx"))
mod->arch.plt_idx.shdr = sechdrs + i; mod->arch.plt_idx.shdr = sechdrs + i;
} }
if (!mod->arch.got.shdr) {
pr_err("%s: module GOT section(s) missing\n", mod->name);
return -ENOEXEC;
}
if (!mod->arch.plt.shdr) { if (!mod->arch.plt.shdr) {
pr_err("%s: module PLT section(s) missing\n", mod->name); pr_err("%s: module PLT section(s) missing\n", mod->name);
return -ENOEXEC; return -ENOEXEC;
...@@ -105,9 +142,16 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, ...@@ -105,9 +142,16 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
if (!(dst_sec->sh_flags & SHF_EXECINSTR)) if (!(dst_sec->sh_flags & SHF_EXECINSTR))
continue; continue;
count_max_entries(relas, num_rela, &num_plts); count_max_entries(relas, num_rela, &num_plts, &num_gots);
} }
mod->arch.got.shdr->sh_type = SHT_NOBITS;
mod->arch.got.shdr->sh_flags = SHF_ALLOC;
mod->arch.got.shdr->sh_addralign = L1_CACHE_BYTES;
mod->arch.got.shdr->sh_size = (num_gots + 1) * sizeof(struct got_entry);
mod->arch.got.num_entries = 0;
mod->arch.got.max_entries = num_gots;
mod->arch.plt.shdr->sh_type = SHT_NOBITS; mod->arch.plt.shdr->sh_type = SHT_NOBITS;
mod->arch.plt.shdr->sh_flags = SHF_EXECINSTR | SHF_ALLOC; mod->arch.plt.shdr->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
mod->arch.plt.shdr->sh_addralign = L1_CACHE_BYTES; mod->arch.plt.shdr->sh_addralign = L1_CACHE_BYTES;
......
...@@ -348,6 +348,29 @@ static int apply_r_larch_pcala(struct module *mod, u32 *location, Elf_Addr v, ...@@ -348,6 +348,29 @@ static int apply_r_larch_pcala(struct module *mod, u32 *location, Elf_Addr v,
return 0; return 0;
} }
static int apply_r_larch_got_pc(struct module *mod, u32 *location, Elf_Addr v,
s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
{
Elf_Addr got = module_emit_got_entry(mod, v);
if (!got)
return -EINVAL;
switch (type) {
case R_LARCH_GOT_PC_LO12:
type = R_LARCH_PCALA_LO12;
break;
case R_LARCH_GOT_PC_HI20:
type = R_LARCH_PCALA_HI20;
break;
default:
pr_err("%s: Unsupport relocation type %u\n", mod->name, type);
return -EINVAL;
}
return apply_r_larch_pcala(mod, location, got, rela_stack, rela_stack_top, type);
}
/* /*
* reloc_handlers_rela() - Apply a particular relocation to a module * reloc_handlers_rela() - Apply a particular relocation to a module
* @mod: the module to apply the reloc to * @mod: the module to apply the reloc to
...@@ -379,6 +402,7 @@ static reloc_rela_handler reloc_rela_handlers[] = { ...@@ -379,6 +402,7 @@ static reloc_rela_handler reloc_rela_handlers[] = {
[R_LARCH_ADD32 ... R_LARCH_SUB64] = apply_r_larch_add_sub, [R_LARCH_ADD32 ... R_LARCH_SUB64] = apply_r_larch_add_sub,
[R_LARCH_B26] = apply_r_larch_b26, [R_LARCH_B26] = apply_r_larch_b26,
[R_LARCH_PCALA_HI20...R_LARCH_PCALA64_HI12] = apply_r_larch_pcala, [R_LARCH_PCALA_HI20...R_LARCH_PCALA64_HI12] = apply_r_larch_pcala,
[R_LARCH_GOT_PC_HI20...R_LARCH_GOT_PC_LO12] = apply_r_larch_got_pc,
}; };
int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment