1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* Kernel module help for Alpha. |
3 | Copyright (C) 2002 Richard Henderson. |
4 | |
5 | */ |
6 | #include <linux/moduleloader.h> |
7 | #include <linux/elf.h> |
8 | #include <linux/vmalloc.h> |
9 | #include <linux/fs.h> |
10 | #include <linux/string.h> |
11 | #include <linux/kernel.h> |
12 | #include <linux/slab.h> |
13 | |
14 | #if 0 |
15 | #define DEBUGP printk |
16 | #else |
17 | #define DEBUGP(fmt...) |
18 | #endif |
19 | |
20 | /* Allocate the GOT at the end of the core sections. */ |
21 | |
22 | struct got_entry { |
23 | struct got_entry *next; |
24 | Elf64_Sxword r_addend; |
25 | int got_offset; |
26 | }; |
27 | |
28 | static inline void |
29 | process_reloc_for_got(Elf64_Rela *rela, |
30 | struct got_entry *chains, Elf64_Xword *poffset) |
31 | { |
32 | unsigned long r_sym = ELF64_R_SYM (rela->r_info); |
33 | unsigned long r_type = ELF64_R_TYPE (rela->r_info); |
34 | Elf64_Sxword r_addend = rela->r_addend; |
35 | struct got_entry *g; |
36 | |
37 | if (r_type != R_ALPHA_LITERAL) |
38 | return; |
39 | |
40 | for (g = chains + r_sym; g ; g = g->next) |
41 | if (g->r_addend == r_addend) { |
42 | if (g->got_offset == 0) { |
43 | g->got_offset = *poffset; |
44 | *poffset += 8; |
45 | } |
46 | goto found_entry; |
47 | } |
48 | |
49 | g = kmalloc (size: sizeof (*g), GFP_KERNEL); |
50 | g->next = chains[r_sym].next; |
51 | g->r_addend = r_addend; |
52 | g->got_offset = *poffset; |
53 | *poffset += 8; |
54 | chains[r_sym].next = g; |
55 | |
56 | found_entry: |
57 | /* Trick: most of the ELF64_R_TYPE field is unused. There are |
58 | 42 valid relocation types, and a 32-bit field. Co-opt the |
59 | bits above 256 to store the got offset for this reloc. */ |
60 | rela->r_info |= g->got_offset << 8; |
61 | } |
62 | |
63 | int |
64 | module_frob_arch_sections(Elf64_Ehdr *hdr, Elf64_Shdr *sechdrs, |
65 | char *secstrings, struct module *me) |
66 | { |
67 | struct got_entry *chains; |
68 | Elf64_Rela *rela; |
69 | Elf64_Shdr *esechdrs, *symtab, *s, *got; |
70 | unsigned long nsyms, nrela, i; |
71 | |
72 | esechdrs = sechdrs + hdr->e_shnum; |
73 | symtab = got = NULL; |
74 | |
75 | /* Find out how large the symbol table is. Allocate one got_entry |
76 | head per symbol. Normally this will be enough, but not always. |
77 | We'll chain different offsets for the symbol down each head. */ |
78 | for (s = sechdrs; s < esechdrs; ++s) |
79 | if (s->sh_type == SHT_SYMTAB) |
80 | symtab = s; |
81 | else if (!strcmp(".got" , secstrings + s->sh_name)) { |
82 | got = s; |
83 | me->arch.gotsecindex = s - sechdrs; |
84 | } |
85 | |
86 | if (!symtab) { |
87 | printk(KERN_ERR "module %s: no symbol table\n" , me->name); |
88 | return -ENOEXEC; |
89 | } |
90 | if (!got) { |
91 | printk(KERN_ERR "module %s: no got section\n" , me->name); |
92 | return -ENOEXEC; |
93 | } |
94 | |
95 | nsyms = symtab->sh_size / sizeof(Elf64_Sym); |
96 | chains = kcalloc(n: nsyms, size: sizeof(struct got_entry), GFP_KERNEL); |
97 | if (!chains) { |
98 | printk(KERN_ERR |
99 | "module %s: no memory for symbol chain buffer\n" , |
100 | me->name); |
101 | return -ENOMEM; |
102 | } |
103 | |
104 | got->sh_size = 0; |
105 | got->sh_addralign = 8; |
106 | got->sh_type = SHT_NOBITS; |
107 | |
108 | /* Examine all LITERAL relocations to find out what GOT entries |
109 | are required. This sizes the GOT section as well. */ |
110 | for (s = sechdrs; s < esechdrs; ++s) |
111 | if (s->sh_type == SHT_RELA) { |
112 | nrela = s->sh_size / sizeof(Elf64_Rela); |
113 | rela = (void *)hdr + s->sh_offset; |
114 | for (i = 0; i < nrela; ++i) |
115 | process_reloc_for_got(rela: rela+i, chains, |
116 | poffset: &got->sh_size); |
117 | } |
118 | |
119 | /* Free the memory we allocated. */ |
120 | for (i = 0; i < nsyms; ++i) { |
121 | struct got_entry *g, *n; |
122 | for (g = chains[i].next; g ; g = n) { |
123 | n = g->next; |
124 | kfree(objp: g); |
125 | } |
126 | } |
127 | kfree(objp: chains); |
128 | |
129 | return 0; |
130 | } |
131 | |
132 | int |
133 | apply_relocate_add(Elf64_Shdr *sechdrs, const char *strtab, |
134 | unsigned int symindex, unsigned int relsec, |
135 | struct module *me) |
136 | { |
137 | Elf64_Rela *rela = (void *)sechdrs[relsec].sh_addr; |
138 | unsigned long i, n = sechdrs[relsec].sh_size / sizeof(*rela); |
139 | Elf64_Sym *symtab, *sym; |
140 | void *base, *location; |
141 | unsigned long got, gp; |
142 | |
143 | DEBUGP("Applying relocate section %u to %u\n" , relsec, |
144 | sechdrs[relsec].sh_info); |
145 | |
146 | base = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr; |
147 | symtab = (Elf64_Sym *)sechdrs[symindex].sh_addr; |
148 | |
149 | got = sechdrs[me->arch.gotsecindex].sh_addr; |
150 | gp = got + 0x8000; |
151 | |
152 | for (i = 0; i < n; i++) { |
153 | unsigned long r_sym = ELF64_R_SYM (rela[i].r_info); |
154 | unsigned long r_type = ELF64_R_TYPE (rela[i].r_info); |
155 | unsigned long r_got_offset = r_type >> 8; |
156 | unsigned long value, hi, lo; |
157 | r_type &= 0xff; |
158 | |
159 | /* This is where to make the change. */ |
160 | location = base + rela[i].r_offset; |
161 | |
162 | /* This is the symbol it is referring to. Note that all |
163 | unresolved symbols have been resolved. */ |
164 | sym = symtab + r_sym; |
165 | value = sym->st_value + rela[i].r_addend; |
166 | |
167 | switch (r_type) { |
168 | case R_ALPHA_NONE: |
169 | break; |
170 | case R_ALPHA_REFLONG: |
171 | *(u32 *)location = value; |
172 | break; |
173 | case R_ALPHA_REFQUAD: |
174 | /* BUG() can produce misaligned relocations. */ |
175 | ((u32 *)location)[0] = value; |
176 | ((u32 *)location)[1] = value >> 32; |
177 | break; |
178 | case R_ALPHA_GPREL32: |
179 | value -= gp; |
180 | if ((int)value != value) |
181 | goto reloc_overflow; |
182 | *(u32 *)location = value; |
183 | break; |
184 | case R_ALPHA_LITERAL: |
185 | hi = got + r_got_offset; |
186 | lo = hi - gp; |
187 | if ((short)lo != lo) |
188 | goto reloc_overflow; |
189 | *(u16 *)location = lo; |
190 | *(u64 *)hi = value; |
191 | break; |
192 | case R_ALPHA_LITUSE: |
193 | break; |
194 | case R_ALPHA_GPDISP: |
195 | value = gp - (u64)location; |
196 | lo = (short)value; |
197 | hi = (int)(value - lo); |
198 | if (hi + lo != value) |
199 | goto reloc_overflow; |
200 | *(u16 *)location = hi >> 16; |
201 | *(u16 *)(location + rela[i].r_addend) = lo; |
202 | break; |
203 | case R_ALPHA_BRSGP: |
204 | /* BRSGP is only allowed to bind to local symbols. |
205 | If the section is undef, this means that the |
206 | value was resolved from somewhere else. */ |
207 | if (sym->st_shndx == SHN_UNDEF) |
208 | goto reloc_overflow; |
209 | if ((sym->st_other & STO_ALPHA_STD_GPLOAD) == |
210 | STO_ALPHA_STD_GPLOAD) |
211 | /* Omit the prologue. */ |
212 | value += 8; |
213 | fallthrough; |
214 | case R_ALPHA_BRADDR: |
215 | value -= (u64)location + 4; |
216 | if (value & 3) |
217 | goto reloc_overflow; |
218 | value = (long)value >> 2; |
219 | if (value + (1<<21) >= 1<<22) |
220 | goto reloc_overflow; |
221 | value &= 0x1fffff; |
222 | value |= *(u32 *)location & ~0x1fffff; |
223 | *(u32 *)location = value; |
224 | break; |
225 | case R_ALPHA_HINT: |
226 | break; |
227 | case R_ALPHA_SREL32: |
228 | value -= (u64)location; |
229 | if ((int)value != value) |
230 | goto reloc_overflow; |
231 | *(u32 *)location = value; |
232 | break; |
233 | case R_ALPHA_SREL64: |
234 | value -= (u64)location; |
235 | *(u64 *)location = value; |
236 | break; |
237 | case R_ALPHA_GPRELHIGH: |
238 | value = (long)(value - gp + 0x8000) >> 16; |
239 | if ((short) value != value) |
240 | goto reloc_overflow; |
241 | *(u16 *)location = value; |
242 | break; |
243 | case R_ALPHA_GPRELLOW: |
244 | value -= gp; |
245 | *(u16 *)location = value; |
246 | break; |
247 | case R_ALPHA_GPREL16: |
248 | value -= gp; |
249 | if ((short) value != value) |
250 | goto reloc_overflow; |
251 | *(u16 *)location = value; |
252 | break; |
253 | default: |
254 | printk(KERN_ERR "module %s: Unknown relocation: %lu\n" , |
255 | me->name, r_type); |
256 | return -ENOEXEC; |
257 | reloc_overflow: |
258 | if (ELF64_ST_TYPE (sym->st_info) == STT_SECTION) |
259 | printk(KERN_ERR |
260 | "module %s: Relocation (type %lu) overflow vs section %d\n" , |
261 | me->name, r_type, sym->st_shndx); |
262 | else |
263 | printk(KERN_ERR |
264 | "module %s: Relocation (type %lu) overflow vs %s\n" , |
265 | me->name, r_type, strtab + sym->st_name); |
266 | return -ENOEXEC; |
267 | } |
268 | } |
269 | |
270 | return 0; |
271 | } |
272 | |