2 * AVR32-specific kernel module loader
4 * Copyright (C) 2005-2006 Atmel Corporation
6 * GOT initialization parts are based on the s390 version
7 * Copyright (C) 2002, 2003 IBM Deutschland Entwicklung GmbH,
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
15 #include <linux/bug.h>
16 #include <linux/elf.h>
17 #include <linux/kernel.h>
18 #include <linux/module.h>
19 #include <linux/moduleloader.h>
20 #include <linux/vmalloc.h>
22 void *module_alloc(unsigned long size
)
29 void module_free(struct module
*mod
, void *module_region
)
31 vfree(mod
->arch
.syminfo
);
32 mod
->arch
.syminfo
= NULL
;
35 /* FIXME: if module_region == mod->init_region, trim exception
39 static inline int check_rela(Elf32_Rela
*rela
, struct module
*module
,
40 char *strings
, Elf32_Sym
*symbols
)
42 struct mod_arch_syminfo
*info
;
44 info
= module
->arch
.syminfo
+ ELF32_R_SYM(rela
->r_info
);
45 switch (ELF32_R_TYPE(rela
->r_info
)) {
50 case R_AVR32_GOT18SW
: /* mcall */
51 case R_AVR32_GOT16S
: /* ld.w */
52 if (rela
->r_addend
!= 0) {
54 "GOT relocation against %s at offset %u with addend\n",
55 strings
+ symbols
[ELF32_R_SYM(rela
->r_info
)].st_name
,
59 if (info
->got_offset
== -1UL) {
60 info
->got_offset
= module
->arch
.got_size
;
61 module
->arch
.got_size
+= sizeof(void *);
63 pr_debug("GOT[%3lu] %s\n", info
->got_offset
,
64 strings
+ symbols
[ELF32_R_SYM(rela
->r_info
)].st_name
);
71 int module_frob_arch_sections(Elf_Ehdr
*hdr
, Elf_Shdr
*sechdrs
,
72 char *secstrings
, struct module
*module
)
81 /* Find the symbol table */
83 for (i
= 0; i
< hdr
->e_shnum
; i
++)
84 switch (sechdrs
[i
].sh_type
) {
90 printk(KERN_ERR
"module %s: no symbol table\n", module
->name
);
94 /* Allocate room for one syminfo structure per symbol. */
95 module
->arch
.nsyms
= symtab
->sh_size
/ sizeof(Elf_Sym
);
96 module
->arch
.syminfo
= vmalloc(module
->arch
.nsyms
97 * sizeof(struct mod_arch_syminfo
));
98 if (!module
->arch
.syminfo
)
101 symbols
= (void *)hdr
+ symtab
->sh_offset
;
102 strings
= (void *)hdr
+ sechdrs
[symtab
->sh_link
].sh_offset
;
103 for (i
= 0; i
< module
->arch
.nsyms
; i
++) {
104 if (symbols
[i
].st_shndx
== SHN_UNDEF
&&
105 strcmp(strings
+ symbols
[i
].st_name
,
106 "_GLOBAL_OFFSET_TABLE_") == 0)
107 /* "Define" it as absolute. */
108 symbols
[i
].st_shndx
= SHN_ABS
;
109 module
->arch
.syminfo
[i
].got_offset
= -1UL;
110 module
->arch
.syminfo
[i
].got_initialized
= 0;
113 /* Allocate GOT entries for symbols that need it. */
114 module
->arch
.got_size
= 0;
115 for (i
= 0; i
< hdr
->e_shnum
; i
++) {
116 if (sechdrs
[i
].sh_type
!= SHT_RELA
)
118 nrela
= sechdrs
[i
].sh_size
/ sizeof(Elf32_Rela
);
119 rela
= (void *)hdr
+ sechdrs
[i
].sh_offset
;
120 for (j
= 0; j
< nrela
; j
++) {
121 ret
= check_rela(rela
+ j
, module
,
124 goto out_free_syminfo
;
129 * Increase core size to make room for GOT and set start
132 module
->core_size
= ALIGN(module
->core_size
, 4);
133 module
->arch
.got_offset
= module
->core_size
;
134 module
->core_size
+= module
->arch
.got_size
;
139 vfree(module
->arch
.syminfo
);
140 module
->arch
.syminfo
= NULL
;
145 static inline int reloc_overflow(struct module
*module
, const char *reloc_name
,
146 Elf32_Addr relocation
)
148 printk(KERN_ERR
"module %s: Value %lx does not fit relocation %s\n",
149 module
->name
, (unsigned long)relocation
, reloc_name
);
153 #define get_u16(loc) (*((uint16_t *)loc))
154 #define put_u16(loc, val) (*((uint16_t *)loc) = (val))
156 int apply_relocate_add(Elf32_Shdr
*sechdrs
, const char *strtab
,
157 unsigned int symindex
, unsigned int relindex
,
158 struct module
*module
)
160 Elf32_Shdr
*symsec
= sechdrs
+ symindex
;
161 Elf32_Shdr
*relsec
= sechdrs
+ relindex
;
162 Elf32_Shdr
*dstsec
= sechdrs
+ relsec
->sh_info
;
163 Elf32_Rela
*rel
= (void *)relsec
->sh_addr
;
167 for (i
= 0; i
< relsec
->sh_size
/ sizeof(Elf32_Rela
); i
++, rel
++) {
168 struct mod_arch_syminfo
*info
;
170 Elf32_Addr relocation
;
174 location
= (void *)dstsec
->sh_addr
+ rel
->r_offset
;
175 sym
= (Elf32_Sym
*)symsec
->sh_addr
+ ELF32_R_SYM(rel
->r_info
);
176 relocation
= sym
->st_value
+ rel
->r_addend
;
178 info
= module
->arch
.syminfo
+ ELF32_R_SYM(rel
->r_info
);
180 /* Initialize GOT entry if necessary */
181 switch (ELF32_R_TYPE(rel
->r_info
)) {
186 case R_AVR32_GOT18SW
:
188 if (!info
->got_initialized
) {
191 gotent
= (module
->module_core
192 + module
->arch
.got_offset
194 *gotent
= relocation
;
195 info
->got_initialized
= 1;
198 relocation
= info
->got_offset
;
202 switch (ELF32_R_TYPE(rel
->r_info
)) {
204 case R_AVR32_32_CPENT
:
205 *location
= relocation
;
207 case R_AVR32_22H_PCREL
:
208 relocation
-= (Elf32_Addr
)location
;
209 if ((relocation
& 0xffe00001) != 0
210 && (relocation
& 0xffc00001) != 0xffc00000)
211 return reloc_overflow(module
,
217 value
= ((value
& 0xe1ef0000)
218 | (relocation
& 0xffff)
219 | ((relocation
& 0x10000) << 4)
220 | ((relocation
& 0x1e0000) << 8));
223 case R_AVR32_11H_PCREL
:
224 relocation
-= (Elf32_Addr
)location
;
225 if ((relocation
& 0xfffffc01) != 0
226 && (relocation
& 0xfffff801) != 0xfffff800)
227 return reloc_overflow(module
,
230 value
= get_u16(location
);
231 value
= ((value
& 0xf00c)
232 | ((relocation
& 0x1fe) << 3)
233 | ((relocation
& 0x600) >> 9));
234 put_u16(location
, value
);
236 case R_AVR32_9H_PCREL
:
237 relocation
-= (Elf32_Addr
)location
;
238 if ((relocation
& 0xffffff01) != 0
239 && (relocation
& 0xfffffe01) != 0xfffffe00)
240 return reloc_overflow(module
,
243 value
= get_u16(location
);
244 value
= ((value
& 0xf00f)
245 | ((relocation
& 0x1fe) << 3));
246 put_u16(location
, value
);
248 case R_AVR32_9UW_PCREL
:
249 relocation
-= ((Elf32_Addr
)location
) & 0xfffffffc;
250 if ((relocation
& 0xfffffc03) != 0)
251 return reloc_overflow(module
,
254 value
= get_u16(location
);
255 value
= ((value
& 0xf80f)
256 | ((relocation
& 0x1fc) << 2));
257 put_u16(location
, value
);
261 * R6 = PC - (PC - GOT)
263 * At this point, relocation contains the
264 * value of PC. Just subtract the value of
265 * GOT, and we're done.
267 pr_debug("GOTPC: PC=0x%x, got_offset=0x%lx, core=0x%p\n",
268 relocation
, module
->arch
.got_offset
,
269 module
->module_core
);
270 relocation
-= ((unsigned long)module
->module_core
271 + module
->arch
.got_offset
);
272 *location
= relocation
;
274 case R_AVR32_GOT18SW
:
275 if ((relocation
& 0xfffe0003) != 0
276 && (relocation
& 0xfffc0003) != 0xffff0000)
277 return reloc_overflow(module
, "R_AVR32_GOT18SW",
282 if ((relocation
& 0xffff8000) != 0
283 && (relocation
& 0xffff0000) != 0xffff0000)
284 return reloc_overflow(module
, "R_AVR32_GOT16S",
286 pr_debug("GOT reloc @ 0x%x -> %u\n",
287 rel
->r_offset
, relocation
);
289 value
= ((value
& 0xffff0000)
290 | (relocation
& 0xffff));
295 printk(KERN_ERR
"module %s: Unknown relocation: %u\n",
296 module
->name
, ELF32_R_TYPE(rel
->r_info
));
304 int apply_relocate(Elf32_Shdr
*sechdrs
, const char *strtab
,
305 unsigned int symindex
, unsigned int relindex
,
306 struct module
*module
)
308 printk(KERN_ERR
"module %s: REL relocations are not supported\n",
313 int module_finalize(const Elf_Ehdr
*hdr
, const Elf_Shdr
*sechdrs
,
314 struct module
*module
)
316 vfree(module
->arch
.syminfo
);
317 module
->arch
.syminfo
= NULL
;
319 return module_bug_finalize(hdr
, sechdrs
, module
);
322 void module_arch_cleanup(struct module
*module
)
324 module_bug_cleanup(module
);