1 /* Kernel module help for Alpha.
2 Copyright (C) 2002 Richard Henderson.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 #include <linux/moduleloader.h>
19 #include <linux/elf.h>
20 #include <linux/vmalloc.h>
22 #include <linux/string.h>
23 #include <linux/kernel.h>
24 #include <linux/slab.h>
29 #define DEBUGP(fmt...)
33 module_alloc(unsigned long size
)
41 module_free(struct module
*mod
, void *module_region
)
46 /* Allocate the GOT at the end of the core sections. */
49 struct got_entry
*next
;
50 Elf64_Sxword r_addend
;
55 process_reloc_for_got(Elf64_Rela
*rela
,
56 struct got_entry
*chains
, Elf64_Xword
*poffset
)
58 unsigned long r_sym
= ELF64_R_SYM (rela
->r_info
);
59 unsigned long r_type
= ELF64_R_TYPE (rela
->r_info
);
60 Elf64_Sxword r_addend
= rela
->r_addend
;
63 if (r_type
!= R_ALPHA_LITERAL
)
66 for (g
= chains
+ r_sym
; g
; g
= g
->next
)
67 if (g
->r_addend
== r_addend
) {
68 if (g
->got_offset
== 0) {
69 g
->got_offset
= *poffset
;
75 g
= kmalloc (sizeof (*g
), GFP_KERNEL
);
76 g
->next
= chains
[r_sym
].next
;
77 g
->r_addend
= r_addend
;
78 g
->got_offset
= *poffset
;
80 chains
[r_sym
].next
= g
;
83 /* Trick: most of the ELF64_R_TYPE field is unused. There are
84 42 valid relocation types, and a 32-bit field. Co-opt the
85 bits above 256 to store the got offset for this reloc. */
86 rela
->r_info
|= g
->got_offset
<< 8;
90 module_frob_arch_sections(Elf64_Ehdr
*hdr
, Elf64_Shdr
*sechdrs
,
91 char *secstrings
, struct module
*me
)
93 struct got_entry
*chains
;
95 Elf64_Shdr
*esechdrs
, *symtab
, *s
, *got
;
96 unsigned long nsyms
, nrela
, i
;
98 esechdrs
= sechdrs
+ hdr
->e_shnum
;
101 /* Find out how large the symbol table is. Allocate one got_entry
102 head per symbol. Normally this will be enough, but not always.
103 We'll chain different offsets for the symbol down each head. */
104 for (s
= sechdrs
; s
< esechdrs
; ++s
)
105 if (s
->sh_type
== SHT_SYMTAB
)
107 else if (!strcmp(".got", secstrings
+ s
->sh_name
)) {
109 me
->arch
.gotsecindex
= s
- sechdrs
;
113 printk(KERN_ERR
"module %s: no symbol table\n", me
->name
);
117 printk(KERN_ERR
"module %s: no got section\n", me
->name
);
121 nsyms
= symtab
->sh_size
/ sizeof(Elf64_Sym
);
122 chains
= kmalloc(nsyms
* sizeof(struct got_entry
), GFP_KERNEL
);
123 memset(chains
, 0, nsyms
* sizeof(struct got_entry
));
126 got
->sh_addralign
= 8;
127 got
->sh_type
= SHT_NOBITS
;
129 /* Examine all LITERAL relocations to find out what GOT entries
130 are required. This sizes the GOT section as well. */
131 for (s
= sechdrs
; s
< esechdrs
; ++s
)
132 if (s
->sh_type
== SHT_RELA
) {
133 nrela
= s
->sh_size
/ sizeof(Elf64_Rela
);
134 rela
= (void *)hdr
+ s
->sh_offset
;
135 for (i
= 0; i
< nrela
; ++i
)
136 process_reloc_for_got(rela
+i
, chains
,
140 /* Free the memory we allocated. */
141 for (i
= 0; i
< nsyms
; ++i
) {
142 struct got_entry
*g
, *n
;
143 for (g
= chains
[i
].next
; g
; g
= n
) {
154 apply_relocate(Elf64_Shdr
*sechdrs
, const char *strtab
, unsigned int symindex
,
155 unsigned int relsec
, struct module
*me
)
157 printk(KERN_ERR
"module %s: REL relocation unsupported\n", me
->name
);
162 apply_relocate_add(Elf64_Shdr
*sechdrs
, const char *strtab
,
163 unsigned int symindex
, unsigned int relsec
,
166 Elf64_Rela
*rela
= (void *)sechdrs
[relsec
].sh_addr
;
167 unsigned long i
, n
= sechdrs
[relsec
].sh_size
/ sizeof(*rela
);
168 Elf64_Sym
*symtab
, *sym
;
169 void *base
, *location
;
170 unsigned long got
, gp
;
172 DEBUGP("Applying relocate section %u to %u\n", relsec
,
173 sechdrs
[relsec
].sh_info
);
175 base
= (void *)sechdrs
[sechdrs
[relsec
].sh_info
].sh_addr
;
176 symtab
= (Elf64_Sym
*)sechdrs
[symindex
].sh_addr
;
178 /* The small sections were sorted to the end of the segment.
179 The following should definitely cover them. */
180 gp
= (u64
)me
->module_core
+ me
->core_size
- 0x8000;
181 got
= sechdrs
[me
->arch
.gotsecindex
].sh_addr
;
183 for (i
= 0; i
< n
; i
++) {
184 unsigned long r_sym
= ELF64_R_SYM (rela
[i
].r_info
);
185 unsigned long r_type
= ELF64_R_TYPE (rela
[i
].r_info
);
186 unsigned long r_got_offset
= r_type
>> 8;
187 unsigned long value
, hi
, lo
;
190 /* This is where to make the change. */
191 location
= base
+ rela
[i
].r_offset
;
193 /* This is the symbol it is referring to. Note that all
194 unresolved symbols have been resolved. */
195 sym
= symtab
+ r_sym
;
196 value
= sym
->st_value
+ rela
[i
].r_addend
;
201 case R_ALPHA_REFQUAD
:
202 /* BUG() can produce misaligned relocations. */
203 ((u32
*)location
)[0] = value
;
204 ((u32
*)location
)[1] = value
>> 32;
206 case R_ALPHA_GPREL32
:
208 if ((int)value
!= value
)
210 *(u32
*)location
= value
;
212 case R_ALPHA_LITERAL
:
213 hi
= got
+ r_got_offset
;
217 *(u16
*)location
= lo
;
223 value
= gp
- (u64
)location
;
225 hi
= (int)(value
- lo
);
226 if (hi
+ lo
!= value
)
228 *(u16
*)location
= hi
>> 16;
229 *(u16
*)(location
+ rela
[i
].r_addend
) = lo
;
232 /* BRSGP is only allowed to bind to local symbols.
233 If the section is undef, this means that the
234 value was resolved from somewhere else. */
235 if (sym
->st_shndx
== SHN_UNDEF
)
237 if ((sym
->st_other
& STO_ALPHA_STD_GPLOAD
) ==
238 STO_ALPHA_STD_GPLOAD
)
239 /* Omit the prologue. */
243 value
-= (u64
)location
+ 4;
246 value
= (long)value
>> 2;
247 if (value
+ (1<<21) >= 1<<22)
250 value
|= *(u32
*)location
& ~0x1fffff;
251 *(u32
*)location
= value
;
256 value
-= (u64
)location
;
257 if ((int)value
!= value
)
259 *(u32
*)location
= value
;
262 value
-= (u64
)location
;
263 *(u64
*)location
= value
;
265 case R_ALPHA_GPRELHIGH
:
266 value
= (long)(value
- gp
+ 0x8000) >> 16;
267 if ((short) value
!= value
)
269 *(u16
*)location
= value
;
271 case R_ALPHA_GPRELLOW
:
273 *(u16
*)location
= value
;
275 case R_ALPHA_GPREL16
:
277 if ((short) value
!= value
)
279 *(u16
*)location
= value
;
282 printk(KERN_ERR
"module %s: Unknown relocation: %lu\n",
286 if (ELF64_ST_TYPE (sym
->st_info
) == STT_SECTION
)
288 "module %s: Relocation (type %lu) overflow vs section %d\n",
289 me
->name
, r_type
, sym
->st_shndx
);
292 "module %s: Relocation (type %lu) overflow vs %s\n",
293 me
->name
, r_type
, strtab
+ sym
->st_name
);
302 module_finalize(const Elf_Ehdr
*hdr
, const Elf_Shdr
*sechdrs
,
309 module_arch_cleanup(struct module
*mod
)