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/moduleloader.h>
16 #include <linux/module.h>
17 #include <linux/kernel.h>
18 #include <linux/elf.h>
19 #include <linux/vmalloc.h>
21 void *module_alloc(unsigned long size
)
28 void module_free(struct module
*mod
, void *module_region
)
30 vfree(mod
->arch
.syminfo
);
31 mod
->arch
.syminfo
= NULL
;
34 /* FIXME: if module_region == mod->init_region, trim exception
38 static inline int check_rela(Elf32_Rela
*rela
, struct module
*module
,
39 char *strings
, Elf32_Sym
*symbols
)
41 struct mod_arch_syminfo
*info
;
43 info
= module
->arch
.syminfo
+ ELF32_R_SYM(rela
->r_info
);
44 switch (ELF32_R_TYPE(rela
->r_info
)) {
49 case R_AVR32_GOT18SW
: /* mcall */
50 case R_AVR32_GOT16S
: /* ld.w */
51 if (rela
->r_addend
!= 0) {
53 "GOT relocation against %s at offset %u with addend\n",
54 strings
+ symbols
[ELF32_R_SYM(rela
->r_info
)].st_name
,
58 if (info
->got_offset
== -1UL) {
59 info
->got_offset
= module
->arch
.got_size
;
60 module
->arch
.got_size
+= sizeof(void *);
62 pr_debug("GOT[%3lu] %s\n", info
->got_offset
,
63 strings
+ symbols
[ELF32_R_SYM(rela
->r_info
)].st_name
);
70 int module_frob_arch_sections(Elf_Ehdr
*hdr
, Elf_Shdr
*sechdrs
,
71 char *secstrings
, struct module
*module
)
80 /* Find the symbol table */
82 for (i
= 0; i
< hdr
->e_shnum
; i
++)
83 switch (sechdrs
[i
].sh_type
) {
89 printk(KERN_ERR
"module %s: no symbol table\n", module
->name
);
93 /* Allocate room for one syminfo structure per symbol. */
94 module
->arch
.nsyms
= symtab
->sh_size
/ sizeof(Elf_Sym
);
95 module
->arch
.syminfo
= vmalloc(module
->arch
.nsyms
96 * sizeof(struct mod_arch_syminfo
));
97 if (!module
->arch
.syminfo
)
100 symbols
= (void *)hdr
+ symtab
->sh_offset
;
101 strings
= (void *)hdr
+ sechdrs
[symtab
->sh_link
].sh_offset
;
102 for (i
= 0; i
< module
->arch
.nsyms
; i
++) {
103 if (symbols
[i
].st_shndx
== SHN_UNDEF
&&
104 strcmp(strings
+ symbols
[i
].st_name
,
105 "_GLOBAL_OFFSET_TABLE_") == 0)
106 /* "Define" it as absolute. */
107 symbols
[i
].st_shndx
= SHN_ABS
;
108 module
->arch
.syminfo
[i
].got_offset
= -1UL;
109 module
->arch
.syminfo
[i
].got_initialized
= 0;
112 /* Allocate GOT entries for symbols that need it. */
113 module
->arch
.got_size
= 0;
114 for (i
= 0; i
< hdr
->e_shnum
; i
++) {
115 if (sechdrs
[i
].sh_type
!= SHT_RELA
)
117 nrela
= sechdrs
[i
].sh_size
/ sizeof(Elf32_Rela
);
118 rela
= (void *)hdr
+ sechdrs
[i
].sh_offset
;
119 for (j
= 0; j
< nrela
; j
++) {
120 ret
= check_rela(rela
+ j
, module
,
123 goto out_free_syminfo
;
128 * Increase core size to make room for GOT and set start
131 module
->core_size
= ALIGN(module
->core_size
, 4);
132 module
->arch
.got_offset
= module
->core_size
;
133 module
->core_size
+= module
->arch
.got_size
;
138 vfree(module
->arch
.syminfo
);
139 module
->arch
.syminfo
= NULL
;
144 static inline int reloc_overflow(struct module
*module
, const char *reloc_name
,
145 Elf32_Addr relocation
)
147 printk(KERN_ERR
"module %s: Value %lx does not fit relocation %s\n",
148 module
->name
, (unsigned long)relocation
, reloc_name
);
152 #define get_u16(loc) (*((uint16_t *)loc))
153 #define put_u16(loc, val) (*((uint16_t *)loc) = (val))
155 int apply_relocate_add(Elf32_Shdr
*sechdrs
, const char *strtab
,
156 unsigned int symindex
, unsigned int relindex
,
157 struct module
*module
)
159 Elf32_Shdr
*symsec
= sechdrs
+ symindex
;
160 Elf32_Shdr
*relsec
= sechdrs
+ relindex
;
161 Elf32_Shdr
*dstsec
= sechdrs
+ relsec
->sh_info
;
162 Elf32_Rela
*rel
= (void *)relsec
->sh_addr
;
166 for (i
= 0; i
< relsec
->sh_size
/ sizeof(Elf32_Rela
); i
++, rel
++) {
167 struct mod_arch_syminfo
*info
;
169 Elf32_Addr relocation
;
173 location
= (void *)dstsec
->sh_addr
+ rel
->r_offset
;
174 sym
= (Elf32_Sym
*)symsec
->sh_addr
+ ELF32_R_SYM(rel
->r_info
);
175 relocation
= sym
->st_value
+ rel
->r_addend
;
177 info
= module
->arch
.syminfo
+ ELF32_R_SYM(rel
->r_info
);
179 /* Initialize GOT entry if necessary */
180 switch (ELF32_R_TYPE(rel
->r_info
)) {
185 case R_AVR32_GOT18SW
:
187 if (!info
->got_initialized
) {
190 gotent
= (module
->module_core
191 + module
->arch
.got_offset
193 *gotent
= relocation
;
194 info
->got_initialized
= 1;
197 relocation
= info
->got_offset
;
201 switch (ELF32_R_TYPE(rel
->r_info
)) {
203 case R_AVR32_32_CPENT
:
204 *location
= relocation
;
206 case R_AVR32_22H_PCREL
:
207 relocation
-= (Elf32_Addr
)location
;
208 if ((relocation
& 0xffe00001) != 0
209 && (relocation
& 0xffc00001) != 0xffc00000)
210 return reloc_overflow(module
,
216 value
= ((value
& 0xe1ef0000)
217 | (relocation
& 0xffff)
218 | ((relocation
& 0x10000) << 4)
219 | ((relocation
& 0x1e0000) << 8));
222 case R_AVR32_11H_PCREL
:
223 relocation
-= (Elf32_Addr
)location
;
224 if ((relocation
& 0xfffffc01) != 0
225 && (relocation
& 0xfffff801) != 0xfffff800)
226 return reloc_overflow(module
,
229 value
= get_u16(location
);
230 value
= ((value
& 0xf00c)
231 | ((relocation
& 0x1fe) << 3)
232 | ((relocation
& 0x600) >> 9));
233 put_u16(location
, value
);
235 case R_AVR32_9H_PCREL
:
236 relocation
-= (Elf32_Addr
)location
;
237 if ((relocation
& 0xffffff01) != 0
238 && (relocation
& 0xfffffe01) != 0xfffffe00)
239 return reloc_overflow(module
,
242 value
= get_u16(location
);
243 value
= ((value
& 0xf00f)
244 | ((relocation
& 0x1fe) << 3));
245 put_u16(location
, value
);
247 case R_AVR32_9UW_PCREL
:
248 relocation
-= ((Elf32_Addr
)location
) & 0xfffffffc;
249 if ((relocation
& 0xfffffc03) != 0)
250 return reloc_overflow(module
,
253 value
= get_u16(location
);
254 value
= ((value
& 0xf80f)
255 | ((relocation
& 0x1fc) << 2));
256 put_u16(location
, value
);
260 * R6 = PC - (PC - GOT)
262 * At this point, relocation contains the
263 * value of PC. Just subtract the value of
264 * GOT, and we're done.
266 pr_debug("GOTPC: PC=0x%x, got_offset=0x%lx, core=0x%p\n",
267 relocation
, module
->arch
.got_offset
,
268 module
->module_core
);
269 relocation
-= ((unsigned long)module
->module_core
270 + module
->arch
.got_offset
);
271 *location
= relocation
;
273 case R_AVR32_GOT18SW
:
274 if ((relocation
& 0xfffe0003) != 0
275 && (relocation
& 0xfffc0003) != 0xffff0000)
276 return reloc_overflow(module
, "R_AVR32_GOT18SW",
281 if ((relocation
& 0xffff8000) != 0
282 && (relocation
& 0xffff0000) != 0xffff0000)
283 return reloc_overflow(module
, "R_AVR32_GOT16S",
285 pr_debug("GOT reloc @ 0x%x -> %u\n",
286 rel
->r_offset
, relocation
);
288 value
= ((value
& 0xffff0000)
289 | (relocation
& 0xffff));
294 printk(KERN_ERR
"module %s: Unknown relocation: %u\n",
295 module
->name
, ELF32_R_TYPE(rel
->r_info
));
303 int apply_relocate(Elf32_Shdr
*sechdrs
, const char *strtab
,
304 unsigned int symindex
, unsigned int relindex
,
305 struct module
*module
)
307 printk(KERN_ERR
"module %s: REL relocations are not supported\n",
312 int module_finalize(const Elf_Ehdr
*hdr
, const Elf_Shdr
*sechdrs
,
313 struct module
*module
)
315 vfree(module
->arch
.syminfo
);
316 module
->arch
.syminfo
= NULL
;
321 void module_arch_cleanup(struct module
*module
)