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_arch_freeing_init(struct module
*mod
)
24 vfree(mod
->arch
.syminfo
);
25 mod
->arch
.syminfo
= NULL
;
28 static inline int check_rela(Elf32_Rela
*rela
, struct module
*module
,
29 char *strings
, Elf32_Sym
*symbols
)
31 struct mod_arch_syminfo
*info
;
33 info
= module
->arch
.syminfo
+ ELF32_R_SYM(rela
->r_info
);
34 switch (ELF32_R_TYPE(rela
->r_info
)) {
39 case R_AVR32_GOT18SW
: /* mcall */
40 case R_AVR32_GOT16S
: /* ld.w */
41 if (rela
->r_addend
!= 0) {
43 "GOT relocation against %s at offset %u with addend\n",
44 strings
+ symbols
[ELF32_R_SYM(rela
->r_info
)].st_name
,
48 if (info
->got_offset
== -1UL) {
49 info
->got_offset
= module
->arch
.got_size
;
50 module
->arch
.got_size
+= sizeof(void *);
52 pr_debug("GOT[%3lu] %s\n", info
->got_offset
,
53 strings
+ symbols
[ELF32_R_SYM(rela
->r_info
)].st_name
);
60 int module_frob_arch_sections(Elf_Ehdr
*hdr
, Elf_Shdr
*sechdrs
,
61 char *secstrings
, struct module
*module
)
70 /* Find the symbol table */
72 for (i
= 0; i
< hdr
->e_shnum
; i
++)
73 switch (sechdrs
[i
].sh_type
) {
79 printk(KERN_ERR
"module %s: no symbol table\n", module
->name
);
83 /* Allocate room for one syminfo structure per symbol. */
84 module
->arch
.nsyms
= symtab
->sh_size
/ sizeof(Elf_Sym
);
85 module
->arch
.syminfo
= vmalloc(module
->arch
.nsyms
86 * sizeof(struct mod_arch_syminfo
));
87 if (!module
->arch
.syminfo
)
90 symbols
= (void *)hdr
+ symtab
->sh_offset
;
91 strings
= (void *)hdr
+ sechdrs
[symtab
->sh_link
].sh_offset
;
92 for (i
= 0; i
< module
->arch
.nsyms
; i
++) {
93 if (symbols
[i
].st_shndx
== SHN_UNDEF
&&
94 strcmp(strings
+ symbols
[i
].st_name
,
95 "_GLOBAL_OFFSET_TABLE_") == 0)
96 /* "Define" it as absolute. */
97 symbols
[i
].st_shndx
= SHN_ABS
;
98 module
->arch
.syminfo
[i
].got_offset
= -1UL;
99 module
->arch
.syminfo
[i
].got_initialized
= 0;
102 /* Allocate GOT entries for symbols that need it. */
103 module
->arch
.got_size
= 0;
104 for (i
= 0; i
< hdr
->e_shnum
; i
++) {
105 if (sechdrs
[i
].sh_type
!= SHT_RELA
)
107 nrela
= sechdrs
[i
].sh_size
/ sizeof(Elf32_Rela
);
108 rela
= (void *)hdr
+ sechdrs
[i
].sh_offset
;
109 for (j
= 0; j
< nrela
; j
++) {
110 ret
= check_rela(rela
+ j
, module
,
113 goto out_free_syminfo
;
118 * Increase core size to make room for GOT and set start
121 module
->core_layout
.size
= ALIGN(module
->core_layout
.size
, 4);
122 module
->arch
.got_offset
= module
->core_layout
.size
;
123 module
->core_layout
.size
+= module
->arch
.got_size
;
128 vfree(module
->arch
.syminfo
);
129 module
->arch
.syminfo
= NULL
;
134 static inline int reloc_overflow(struct module
*module
, const char *reloc_name
,
135 Elf32_Addr relocation
)
137 printk(KERN_ERR
"module %s: Value %lx does not fit relocation %s\n",
138 module
->name
, (unsigned long)relocation
, reloc_name
);
142 #define get_u16(loc) (*((uint16_t *)loc))
143 #define put_u16(loc, val) (*((uint16_t *)loc) = (val))
145 int apply_relocate_add(Elf32_Shdr
*sechdrs
, const char *strtab
,
146 unsigned int symindex
, unsigned int relindex
,
147 struct module
*module
)
149 Elf32_Shdr
*symsec
= sechdrs
+ symindex
;
150 Elf32_Shdr
*relsec
= sechdrs
+ relindex
;
151 Elf32_Shdr
*dstsec
= sechdrs
+ relsec
->sh_info
;
152 Elf32_Rela
*rel
= (void *)relsec
->sh_addr
;
156 for (i
= 0; i
< relsec
->sh_size
/ sizeof(Elf32_Rela
); i
++, rel
++) {
157 struct mod_arch_syminfo
*info
;
159 Elf32_Addr relocation
;
163 location
= (void *)dstsec
->sh_addr
+ rel
->r_offset
;
164 sym
= (Elf32_Sym
*)symsec
->sh_addr
+ ELF32_R_SYM(rel
->r_info
);
165 relocation
= sym
->st_value
+ rel
->r_addend
;
167 info
= module
->arch
.syminfo
+ ELF32_R_SYM(rel
->r_info
);
169 /* Initialize GOT entry if necessary */
170 switch (ELF32_R_TYPE(rel
->r_info
)) {
175 case R_AVR32_GOT18SW
:
177 if (!info
->got_initialized
) {
180 gotent
= (module
->core_layout
.base
181 + module
->arch
.got_offset
183 *gotent
= relocation
;
184 info
->got_initialized
= 1;
187 relocation
= info
->got_offset
;
191 switch (ELF32_R_TYPE(rel
->r_info
)) {
193 case R_AVR32_32_CPENT
:
194 *location
= relocation
;
196 case R_AVR32_22H_PCREL
:
197 relocation
-= (Elf32_Addr
)location
;
198 if ((relocation
& 0xffe00001) != 0
199 && (relocation
& 0xffc00001) != 0xffc00000)
200 return reloc_overflow(module
,
206 value
= ((value
& 0xe1ef0000)
207 | (relocation
& 0xffff)
208 | ((relocation
& 0x10000) << 4)
209 | ((relocation
& 0x1e0000) << 8));
212 case R_AVR32_11H_PCREL
:
213 relocation
-= (Elf32_Addr
)location
;
214 if ((relocation
& 0xfffffc01) != 0
215 && (relocation
& 0xfffff801) != 0xfffff800)
216 return reloc_overflow(module
,
219 value
= get_u16(location
);
220 value
= ((value
& 0xf00c)
221 | ((relocation
& 0x1fe) << 3)
222 | ((relocation
& 0x600) >> 9));
223 put_u16(location
, value
);
225 case R_AVR32_9H_PCREL
:
226 relocation
-= (Elf32_Addr
)location
;
227 if ((relocation
& 0xffffff01) != 0
228 && (relocation
& 0xfffffe01) != 0xfffffe00)
229 return reloc_overflow(module
,
232 value
= get_u16(location
);
233 value
= ((value
& 0xf00f)
234 | ((relocation
& 0x1fe) << 3));
235 put_u16(location
, value
);
237 case R_AVR32_9UW_PCREL
:
238 relocation
-= ((Elf32_Addr
)location
) & 0xfffffffc;
239 if ((relocation
& 0xfffffc03) != 0)
240 return reloc_overflow(module
,
243 value
= get_u16(location
);
244 value
= ((value
& 0xf80f)
245 | ((relocation
& 0x1fc) << 2));
246 put_u16(location
, value
);
250 * R6 = PC - (PC - GOT)
252 * At this point, relocation contains the
253 * value of PC. Just subtract the value of
254 * GOT, and we're done.
256 pr_debug("GOTPC: PC=0x%x, got_offset=0x%lx, core=0x%p\n",
257 relocation
, module
->arch
.got_offset
,
258 module
->core_layout
.base
);
259 relocation
-= ((unsigned long)module
->core_layout
.base
260 + module
->arch
.got_offset
);
261 *location
= relocation
;
263 case R_AVR32_GOT18SW
:
264 if ((relocation
& 0xfffe0003) != 0
265 && (relocation
& 0xfffc0000) != 0xfffc0000)
266 return reloc_overflow(module
, "R_AVR32_GOT18SW",
271 if ((relocation
& 0xffff8000) != 0
272 && (relocation
& 0xffff0000) != 0xffff0000)
273 return reloc_overflow(module
, "R_AVR32_GOT16S",
275 pr_debug("GOT reloc @ 0x%x -> %u\n",
276 rel
->r_offset
, relocation
);
278 value
= ((value
& 0xffff0000)
279 | (relocation
& 0xffff));
284 printk(KERN_ERR
"module %s: Unknown relocation: %u\n",
285 module
->name
, ELF32_R_TYPE(rel
->r_info
));