1ab1ef68eSZong Li /* SPDX-License-Identifier: GPL-2.0
2ab1ef68eSZong Li *
3ab1ef68eSZong Li * Copyright (C) 2014-2017 Linaro Ltd. <[email protected]>
4ab1ef68eSZong Li *
5ab1ef68eSZong Li * Copyright (C) 2018 Andes Technology Corporation <[email protected]>
6ab1ef68eSZong Li */
7ab1ef68eSZong Li
8ab1ef68eSZong Li #include <linux/elf.h>
9ab1ef68eSZong Li #include <linux/kernel.h>
10ab1ef68eSZong Li #include <linux/module.h>
115ed881bcSPaul Walmsley #include <linux/moduleloader.h>
12ab1ef68eSZong Li
module_emit_got_entry(struct module * mod,unsigned long val)132cffc956SZong Li unsigned long module_emit_got_entry(struct module *mod, unsigned long val)
14ab1ef68eSZong Li {
15ab1ef68eSZong Li struct mod_section *got_sec = &mod->arch.got;
16ab1ef68eSZong Li int i = got_sec->num_entries;
17ab1ef68eSZong Li struct got_entry *got = get_got_entry(val, got_sec);
18ab1ef68eSZong Li
19ab1ef68eSZong Li if (got)
202cffc956SZong Li return (unsigned long)got;
21ab1ef68eSZong Li
22ab1ef68eSZong Li /* There is no duplicate entry, create a new one */
23ab1ef68eSZong Li got = (struct got_entry *)got_sec->shdr->sh_addr;
24ab1ef68eSZong Li got[i] = emit_got_entry(val);
25ab1ef68eSZong Li
26ab1ef68eSZong Li got_sec->num_entries++;
27ab1ef68eSZong Li BUG_ON(got_sec->num_entries > got_sec->max_entries);
28ab1ef68eSZong Li
292cffc956SZong Li return (unsigned long)&got[i];
30ab1ef68eSZong Li }
31ab1ef68eSZong Li
module_emit_plt_entry(struct module * mod,unsigned long val)322cffc956SZong Li unsigned long module_emit_plt_entry(struct module *mod, unsigned long val)
33ab1ef68eSZong Li {
34b8bde0efSZong Li struct mod_section *got_plt_sec = &mod->arch.got_plt;
35b8bde0efSZong Li struct got_entry *got_plt;
36ab1ef68eSZong Li struct mod_section *plt_sec = &mod->arch.plt;
37b8bde0efSZong Li struct plt_entry *plt = get_plt_entry(val, plt_sec, got_plt_sec);
38ab1ef68eSZong Li int i = plt_sec->num_entries;
39ab1ef68eSZong Li
40ab1ef68eSZong Li if (plt)
412cffc956SZong Li return (unsigned long)plt;
42ab1ef68eSZong Li
43ab1ef68eSZong Li /* There is no duplicate entry, create a new one */
44b8bde0efSZong Li got_plt = (struct got_entry *)got_plt_sec->shdr->sh_addr;
45b8bde0efSZong Li got_plt[i] = emit_got_entry(val);
46ab1ef68eSZong Li plt = (struct plt_entry *)plt_sec->shdr->sh_addr;
472cffc956SZong Li plt[i] = emit_plt_entry(val,
482cffc956SZong Li (unsigned long)&plt[i],
492cffc956SZong Li (unsigned long)&got_plt[i]);
50ab1ef68eSZong Li
51ab1ef68eSZong Li plt_sec->num_entries++;
52b8bde0efSZong Li got_plt_sec->num_entries++;
53ab1ef68eSZong Li BUG_ON(plt_sec->num_entries > plt_sec->max_entries);
54ab1ef68eSZong Li
552cffc956SZong Li return (unsigned long)&plt[i];
56ab1ef68eSZong Li }
57ab1ef68eSZong Li
is_rela_equal(const Elf_Rela * x,const Elf_Rela * y)582cffc956SZong Li static int is_rela_equal(const Elf_Rela *x, const Elf_Rela *y)
59ab1ef68eSZong Li {
60ab1ef68eSZong Li return x->r_info == y->r_info && x->r_addend == y->r_addend;
61ab1ef68eSZong Li }
62ab1ef68eSZong Li
duplicate_rela(const Elf_Rela * rela,int idx)632cffc956SZong Li static bool duplicate_rela(const Elf_Rela *rela, int idx)
64ab1ef68eSZong Li {
65ab1ef68eSZong Li int i;
66ab1ef68eSZong Li for (i = 0; i < idx; i++) {
67ab1ef68eSZong Li if (is_rela_equal(&rela[i], &rela[idx]))
68ab1ef68eSZong Li return true;
69ab1ef68eSZong Li }
70ab1ef68eSZong Li return false;
71ab1ef68eSZong Li }
72ab1ef68eSZong Li
count_max_entries(Elf_Rela * relas,int num,unsigned int * plts,unsigned int * gots)732cffc956SZong Li static void count_max_entries(Elf_Rela *relas, int num,
74ab1ef68eSZong Li unsigned int *plts, unsigned int *gots)
75ab1ef68eSZong Li {
76*1ee1313fSSamuel Holland for (int i = 0; i < num; i++) {
77*1ee1313fSSamuel Holland switch (ELF_R_TYPE(relas[i].r_info)) {
78*1ee1313fSSamuel Holland case R_RISCV_CALL_PLT:
79*1ee1313fSSamuel Holland case R_RISCV_PLT32:
80ab1ef68eSZong Li if (!duplicate_rela(relas, i))
81ab1ef68eSZong Li (*plts)++;
82*1ee1313fSSamuel Holland break;
83*1ee1313fSSamuel Holland case R_RISCV_GOT_HI20:
84ab1ef68eSZong Li if (!duplicate_rela(relas, i))
85ab1ef68eSZong Li (*gots)++;
86*1ee1313fSSamuel Holland break;
87ab1ef68eSZong Li }
88ab1ef68eSZong Li }
89ab1ef68eSZong Li }
90ab1ef68eSZong Li
module_frob_arch_sections(Elf_Ehdr * ehdr,Elf_Shdr * sechdrs,char * secstrings,struct module * mod)91ab1ef68eSZong Li int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
92ab1ef68eSZong Li char *secstrings, struct module *mod)
93ab1ef68eSZong Li {
94ab1ef68eSZong Li unsigned int num_plts = 0;
95ab1ef68eSZong Li unsigned int num_gots = 0;
96ab1ef68eSZong Li int i;
97ab1ef68eSZong Li
98ab1ef68eSZong Li /*
99ab1ef68eSZong Li * Find the empty .got and .plt sections.
100ab1ef68eSZong Li */
101ab1ef68eSZong Li for (i = 0; i < ehdr->e_shnum; i++) {
102ab1ef68eSZong Li if (!strcmp(secstrings + sechdrs[i].sh_name, ".plt"))
103ab1ef68eSZong Li mod->arch.plt.shdr = sechdrs + i;
104ab1ef68eSZong Li else if (!strcmp(secstrings + sechdrs[i].sh_name, ".got"))
105ab1ef68eSZong Li mod->arch.got.shdr = sechdrs + i;
106b8bde0efSZong Li else if (!strcmp(secstrings + sechdrs[i].sh_name, ".got.plt"))
107b8bde0efSZong Li mod->arch.got_plt.shdr = sechdrs + i;
108ab1ef68eSZong Li }
109ab1ef68eSZong Li
110ab1ef68eSZong Li if (!mod->arch.plt.shdr) {
111ab1ef68eSZong Li pr_err("%s: module PLT section(s) missing\n", mod->name);
112ab1ef68eSZong Li return -ENOEXEC;
113ab1ef68eSZong Li }
114ab1ef68eSZong Li if (!mod->arch.got.shdr) {
115ab1ef68eSZong Li pr_err("%s: module GOT section(s) missing\n", mod->name);
116ab1ef68eSZong Li return -ENOEXEC;
117ab1ef68eSZong Li }
118b8bde0efSZong Li if (!mod->arch.got_plt.shdr) {
119b8bde0efSZong Li pr_err("%s: module GOT.PLT section(s) missing\n", mod->name);
120b8bde0efSZong Li return -ENOEXEC;
121b8bde0efSZong Li }
122ab1ef68eSZong Li
123ab1ef68eSZong Li /* Calculate the maxinum number of entries */
124ab1ef68eSZong Li for (i = 0; i < ehdr->e_shnum; i++) {
1252cffc956SZong Li Elf_Rela *relas = (void *)ehdr + sechdrs[i].sh_offset;
1262cffc956SZong Li int num_rela = sechdrs[i].sh_size / sizeof(Elf_Rela);
1272cffc956SZong Li Elf_Shdr *dst_sec = sechdrs + sechdrs[i].sh_info;
128ab1ef68eSZong Li
129ab1ef68eSZong Li if (sechdrs[i].sh_type != SHT_RELA)
130ab1ef68eSZong Li continue;
131ab1ef68eSZong Li
132ab1ef68eSZong Li /* ignore relocations that operate on non-exec sections */
133ab1ef68eSZong Li if (!(dst_sec->sh_flags & SHF_EXECINSTR))
134ab1ef68eSZong Li continue;
135ab1ef68eSZong Li
136ab1ef68eSZong Li count_max_entries(relas, num_rela, &num_plts, &num_gots);
137ab1ef68eSZong Li }
138ab1ef68eSZong Li
139ab1ef68eSZong Li mod->arch.plt.shdr->sh_type = SHT_NOBITS;
140ab1ef68eSZong Li mod->arch.plt.shdr->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
141ab1ef68eSZong Li mod->arch.plt.shdr->sh_addralign = L1_CACHE_BYTES;
142ab1ef68eSZong Li mod->arch.plt.shdr->sh_size = (num_plts + 1) * sizeof(struct plt_entry);
143ab1ef68eSZong Li mod->arch.plt.num_entries = 0;
144ab1ef68eSZong Li mod->arch.plt.max_entries = num_plts;
145ab1ef68eSZong Li
146ab1ef68eSZong Li mod->arch.got.shdr->sh_type = SHT_NOBITS;
147ab1ef68eSZong Li mod->arch.got.shdr->sh_flags = SHF_ALLOC;
148ab1ef68eSZong Li mod->arch.got.shdr->sh_addralign = L1_CACHE_BYTES;
149ab1ef68eSZong Li mod->arch.got.shdr->sh_size = (num_gots + 1) * sizeof(struct got_entry);
150ab1ef68eSZong Li mod->arch.got.num_entries = 0;
151ab1ef68eSZong Li mod->arch.got.max_entries = num_gots;
152ab1ef68eSZong Li
153b8bde0efSZong Li mod->arch.got_plt.shdr->sh_type = SHT_NOBITS;
154b8bde0efSZong Li mod->arch.got_plt.shdr->sh_flags = SHF_ALLOC;
155b8bde0efSZong Li mod->arch.got_plt.shdr->sh_addralign = L1_CACHE_BYTES;
156b8bde0efSZong Li mod->arch.got_plt.shdr->sh_size = (num_plts + 1) * sizeof(struct got_entry);
157b8bde0efSZong Li mod->arch.got_plt.num_entries = 0;
158b8bde0efSZong Li mod->arch.got_plt.max_entries = num_plts;
159ab1ef68eSZong Li return 0;
160ab1ef68eSZong Li }
161