1
2
3
4
5
6
7
8
9#include <linux/elf.h>
10#include <linux/kernel.h>
11#include <linux/module.h>
12#include <linux/sort.h>
13
14struct plt_entry {
15
16
17
18
19
20
21
22
23 __le32 mov0;
24 __le32 mov1;
25 __le32 mov2;
26 __le32 br;
27};
28
29u64 module_emit_plt_entry(struct module *mod, const Elf64_Rela *rela,
30 Elf64_Sym *sym)
31{
32 struct plt_entry *plt = (struct plt_entry *)mod->arch.plt->sh_addr;
33 int i = mod->arch.plt_num_entries;
34 u64 val = sym->st_value + rela->r_addend;
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 if (rela->r_addend == 0 && sym->st_size != 0) {
50 BUG_ON(sym->st_size < (u64)plt || sym->st_size >= (u64)&plt[i]);
51 return sym->st_size;
52 }
53
54 mod->arch.plt_num_entries++;
55 BUG_ON(mod->arch.plt_num_entries > mod->arch.plt_max_entries);
56
57
58
59
60
61
62
63
64
65
66
67
68 plt[i] = (struct plt_entry){
69 cpu_to_le32(0x92800010 | (((~val ) & 0xffff)) << 5),
70 cpu_to_le32(0xf2a00010 | ((( val >> 16) & 0xffff)) << 5),
71 cpu_to_le32(0xf2c00010 | ((( val >> 32) & 0xffff)) << 5),
72 cpu_to_le32(0xd61f0200)
73 };
74
75 if (rela->r_addend == 0)
76 sym->st_size = (u64)&plt[i];
77
78 return (u64)&plt[i];
79}
80
81#define cmp_3way(a,b) ((a) < (b) ? -1 : (a) > (b))
82
83static int cmp_rela(const void *a, const void *b)
84{
85 const Elf64_Rela *x = a, *y = b;
86 int i;
87
88
89 i = cmp_3way(ELF64_R_TYPE(x->r_info), ELF64_R_TYPE(y->r_info));
90 if (i == 0)
91 i = cmp_3way(ELF64_R_SYM(x->r_info), ELF64_R_SYM(y->r_info));
92 if (i == 0)
93 i = cmp_3way(x->r_addend, y->r_addend);
94 return i;
95}
96
97static bool duplicate_rel(const Elf64_Rela *rela, int num)
98{
99
100
101
102
103
104 return num > 0 && cmp_rela(rela + num, rela + num - 1) == 0;
105}
106
107static unsigned int count_plts(Elf64_Sym *syms, Elf64_Rela *rela, int num)
108{
109 unsigned int ret = 0;
110 Elf64_Sym *s;
111 int i;
112
113 for (i = 0; i < num; i++) {
114 switch (ELF64_R_TYPE(rela[i].r_info)) {
115 case R_AARCH64_JUMP26:
116 case R_AARCH64_CALL26:
117
118
119
120
121
122
123
124 s = syms + ELF64_R_SYM(rela[i].r_info);
125 if (s->st_shndx != SHN_UNDEF)
126 break;
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141 if (rela[i].r_addend != 0 || !duplicate_rel(rela, i))
142 ret++;
143 break;
144 }
145 }
146 return ret;
147}
148
149int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
150 char *secstrings, struct module *mod)
151{
152 unsigned long plt_max_entries = 0;
153 Elf64_Sym *syms = NULL;
154 int i;
155
156
157
158
159
160 for (i = 0; i < ehdr->e_shnum; i++) {
161 if (strcmp(".plt", secstrings + sechdrs[i].sh_name) == 0)
162 mod->arch.plt = sechdrs + i;
163 else if (sechdrs[i].sh_type == SHT_SYMTAB)
164 syms = (Elf64_Sym *)sechdrs[i].sh_addr;
165 }
166
167 if (!mod->arch.plt) {
168 pr_err("%s: module PLT section missing\n", mod->name);
169 return -ENOEXEC;
170 }
171 if (!syms) {
172 pr_err("%s: module symtab section missing\n", mod->name);
173 return -ENOEXEC;
174 }
175
176 for (i = 0; i < ehdr->e_shnum; i++) {
177 Elf64_Rela *rels = (void *)ehdr + sechdrs[i].sh_offset;
178 int numrels = sechdrs[i].sh_size / sizeof(Elf64_Rela);
179 Elf64_Shdr *dstsec = sechdrs + sechdrs[i].sh_info;
180
181 if (sechdrs[i].sh_type != SHT_RELA)
182 continue;
183
184
185 if (!(dstsec->sh_flags & SHF_EXECINSTR))
186 continue;
187
188
189 sort(rels, numrels, sizeof(Elf64_Rela), cmp_rela, NULL);
190
191 plt_max_entries += count_plts(syms, rels, numrels);
192 }
193
194 mod->arch.plt->sh_type = SHT_NOBITS;
195 mod->arch.plt->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
196 mod->arch.plt->sh_addralign = L1_CACHE_BYTES;
197 mod->arch.plt->sh_size = plt_max_entries * sizeof(struct plt_entry);
198 mod->arch.plt_num_entries = 0;
199 mod->arch.plt_max_entries = plt_max_entries;
200 return 0;
201}
202