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
;
37 static inline int check_rela(Elf32_Rela
*rela
, struct module
*module
,
38 char *strings
, Elf32_Sym
*symbols
)
40 struct mod_arch_syminfo
*info
;
42 info
= module
->arch
.syminfo
+ ELF32_R_SYM(rela
->r_info
);
43 switch (ELF32_R_TYPE(rela
->r_info
)) {
48 case R_AVR32_GOT18SW
: /* mcall */
49 case R_AVR32_GOT16S
: /* ld.w */
50 if (rela
->r_addend
!= 0) {
52 "GOT relocation against %s at offset %u with addend\n",
53 strings
+ symbols
[ELF32_R_SYM(rela
->r_info
)].st_name
,
57 if (info
->got_offset
== -1UL) {
58 info
->got_offset
= module
->arch
.got_size
;
59 module
->arch
.got_size
+= sizeof(void *);
61 pr_debug("GOT[%3lu] %s\n", info
->got_offset
,
62 strings
+ symbols
[ELF32_R_SYM(rela
->r_info
)].st_name
);
69 int module_frob_arch_sections(Elf_Ehdr
*hdr
, Elf_Shdr
*sechdrs
,
70 char *secstrings
, struct module
*module
)
79 /* Find the symbol table */
81 for (i
= 0; i
< hdr
->e_shnum
; i
++)
82 switch (sechdrs
[i
].sh_type
) {
88 printk(KERN_ERR
"module %s: no symbol table\n", module
->name
);
92 /* Allocate room for one syminfo structure per symbol. */
93 module
->arch
.nsyms
= symtab
->sh_size
/ sizeof(Elf_Sym
);
94 module
->arch
.syminfo
= vmalloc(module
->arch
.nsyms
95 * sizeof(struct mod_arch_syminfo
));
96 if (!module
->arch
.syminfo
)
99 symbols
= (void *)hdr
+ symtab
->sh_offset
;
100 strings
= (void *)hdr
+ sechdrs
[symtab
->sh_link
].sh_offset
;
101 for (i
= 0; i
< module
->arch
.nsyms
; i
++) {
102 if (symbols
[i
].st_shndx
== SHN_UNDEF
&&
103 strcmp(strings
+ symbols
[i
].st_name
,
104 "_GLOBAL_OFFSET_TABLE_") == 0)
105 /* "Define" it as absolute. */
106 symbols
[i
].st_shndx
= SHN_ABS
;
107 module
->arch
.syminfo
[i
].got_offset
= -1UL;
108 module
->arch
.syminfo
[i
].got_initialized
= 0;
111 /* Allocate GOT entries for symbols that need it. */
112 module
->arch
.got_size
= 0;
113 for (i
= 0; i
< hdr
->e_shnum
; i
++) {
114 if (sechdrs
[i
].sh_type
!= SHT_RELA
)
116 nrela
= sechdrs
[i
].sh_size
/ sizeof(Elf32_Rela
);
117 rela
= (void *)hdr
+ sechdrs
[i
].sh_offset
;
118 for (j
= 0; j
< nrela
; j
++) {
119 ret
= check_rela(rela
+ j
, module
,
122 goto out_free_syminfo
;
127 * Increase core size to make room for GOT and set start
130 module
->core_size
= ALIGN(module
->core_size
, 4);
131 module
->arch
.got_offset
= module
->core_size
;
132 module
->core_size
+= module
->arch
.got_size
;
137 vfree(module
->arch
.syminfo
);
138 module
->arch
.syminfo
= NULL
;
143 static inline int reloc_overflow(struct module
*module
, const char *reloc_name
,
144 Elf32_Addr relocation
)
146 printk(KERN_ERR
"module %s: Value %lx does not fit relocation %s\n",
147 module
->name
, (unsigned long)relocation
, reloc_name
);
151 #define get_u16(loc) (*((uint16_t *)loc))
152 #define put_u16(loc, val) (*((uint16_t *)loc) = (val))
154 int apply_relocate_add(Elf32_Shdr
*sechdrs
, const char *strtab
,
155 unsigned int symindex
, unsigned int relindex
,
156 struct module
*module
)
158 Elf32_Shdr
*symsec
= sechdrs
+ symindex
;
159 Elf32_Shdr
*relsec
= sechdrs
+ relindex
;
160 Elf32_Shdr
*dstsec
= sechdrs
+ relsec
->sh_info
;
161 Elf32_Rela
*rel
= (void *)relsec
->sh_addr
;
165 for (i
= 0; i
< relsec
->sh_size
/ sizeof(Elf32_Rela
); i
++, rel
++) {
166 struct mod_arch_syminfo
*info
;
168 Elf32_Addr relocation
;
172 location
= (void *)dstsec
->sh_addr
+ rel
->r_offset
;
173 sym
= (Elf32_Sym
*)symsec
->sh_addr
+ ELF32_R_SYM(rel
->r_info
);
174 relocation
= sym
->st_value
+ rel
->r_addend
;
176 info
= module
->arch
.syminfo
+ ELF32_R_SYM(rel
->r_info
);
178 /* Initialize GOT entry if necessary */
179 switch (ELF32_R_TYPE(rel
->r_info
)) {
184 case R_AVR32_GOT18SW
:
186 if (!info
->got_initialized
) {
189 gotent
= (module
->module_core
190 + module
->arch
.got_offset
192 *gotent
= relocation
;
193 info
->got_initialized
= 1;
196 relocation
= info
->got_offset
;
200 switch (ELF32_R_TYPE(rel
->r_info
)) {
202 case R_AVR32_32_CPENT
:
203 *location
= relocation
;
205 case R_AVR32_22H_PCREL
:
206 relocation
-= (Elf32_Addr
)location
;
207 if ((relocation
& 0xffe00001) != 0
208 && (relocation
& 0xffc00001) != 0xffc00000)
209 return reloc_overflow(module
,
215 value
= ((value
& 0xe1ef0000)
216 | (relocation
& 0xffff)
217 | ((relocation
& 0x10000) << 4)
218 | ((relocation
& 0x1e0000) << 8));
221 case R_AVR32_11H_PCREL
:
222 relocation
-= (Elf32_Addr
)location
;
223 if ((relocation
& 0xfffffc01) != 0
224 && (relocation
& 0xfffff801) != 0xfffff800)
225 return reloc_overflow(module
,
228 value
= get_u16(location
);
229 value
= ((value
& 0xf00c)
230 | ((relocation
& 0x1fe) << 3)
231 | ((relocation
& 0x600) >> 9));
232 put_u16(location
, value
);
234 case R_AVR32_9H_PCREL
:
235 relocation
-= (Elf32_Addr
)location
;
236 if ((relocation
& 0xffffff01) != 0
237 && (relocation
& 0xfffffe01) != 0xfffffe00)
238 return reloc_overflow(module
,
241 value
= get_u16(location
);
242 value
= ((value
& 0xf00f)
243 | ((relocation
& 0x1fe) << 3));
244 put_u16(location
, value
);
246 case R_AVR32_9UW_PCREL
:
247 relocation
-= ((Elf32_Addr
)location
) & 0xfffffffc;
248 if ((relocation
& 0xfffffc03) != 0)
249 return reloc_overflow(module
,
252 value
= get_u16(location
);
253 value
= ((value
& 0xf80f)
254 | ((relocation
& 0x1fc) << 2));
255 put_u16(location
, value
);
259 * R6 = PC - (PC - GOT)
261 * At this point, relocation contains the
262 * value of PC. Just subtract the value of
263 * GOT, and we're done.
265 pr_debug("GOTPC: PC=0x%x, got_offset=0x%lx, core=0x%p\n",
266 relocation
, module
->arch
.got_offset
,
267 module
->module_core
);
268 relocation
-= ((unsigned long)module
->module_core
269 + module
->arch
.got_offset
);
270 *location
= relocation
;
272 case R_AVR32_GOT18SW
:
273 if ((relocation
& 0xfffe0003) != 0
274 && (relocation
& 0xfffc0003) != 0xffff0000)
275 return reloc_overflow(module
, "R_AVR32_GOT18SW",
280 if ((relocation
& 0xffff8000) != 0
281 && (relocation
& 0xffff0000) != 0xffff0000)
282 return reloc_overflow(module
, "R_AVR32_GOT16S",
284 pr_debug("GOT reloc @ 0x%x -> %u\n",
285 rel
->r_offset
, relocation
);
287 value
= ((value
& 0xffff0000)
288 | (relocation
& 0xffff));
293 printk(KERN_ERR
"module %s: Unknown relocation: %u\n",
294 module
->name
, ELF32_R_TYPE(rel
->r_info
));
302 int apply_relocate(Elf32_Shdr
*sechdrs
, const char *strtab
,
303 unsigned int symindex
, unsigned int relindex
,
304 struct module
*module
)
306 printk(KERN_ERR
"module %s: REL relocations are not supported\n",
311 int module_finalize(const Elf_Ehdr
*hdr
, const Elf_Shdr
*sechdrs
,
312 struct module
*module
)
314 vfree(module
->arch
.syminfo
);
315 module
->arch
.syminfo
= NULL
;
320 void module_arch_cleanup(struct module
*module
)