Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/alpha/kernel/module.c
26424 views
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 (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(nsyms, 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+i, chains,
116
&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(g);
125
}
126
}
127
kfree(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
273