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_free(struct module
*mod
, void *module_region
)
24 vfree(mod
->arch
.syminfo
);
25 mod
->arch
.syminfo
= NULL
;
30 static inline int check_rela(Elf32_Rela
*rela
, struct module
*module
,
31 char *strings
, Elf32_Sym
*symbols
)
33 struct mod_arch_syminfo
*info
;
35 info
= module
->arch
.syminfo
+ ELF32_R_SYM(rela
->r_info
);
36 switch (ELF32_R_TYPE(rela
->r_info
)) {
41 case R_AVR32_GOT18SW
: /* mcall */
42 case R_AVR32_GOT16S
: /* ld.w */
43 if (rela
->r_addend
!= 0) {
45 "GOT relocation against %s at offset %u with addend\n",
46 strings
+ symbols
[ELF32_R_SYM(rela
->r_info
)].st_name
,
50 if (info
->got_offset
== -1UL) {
51 info
->got_offset
= module
->arch
.got_size
;
52 module
->arch
.got_size
+= sizeof(void *);
54 pr_debug("GOT[%3lu] %s\n", info
->got_offset
,
55 strings
+ symbols
[ELF32_R_SYM(rela
->r_info
)].st_name
);
62 int module_frob_arch_sections(Elf_Ehdr
*hdr
, Elf_Shdr
*sechdrs
,
63 char *secstrings
, struct module
*module
)
72 /* Find the symbol table */
74 for (i
= 0; i
< hdr
->e_shnum
; i
++)
75 switch (sechdrs
[i
].sh_type
) {
81 printk(KERN_ERR
"module %s: no symbol table\n", module
->name
);
85 /* Allocate room for one syminfo structure per symbol. */
86 module
->arch
.nsyms
= symtab
->sh_size
/ sizeof(Elf_Sym
);
87 module
->arch
.syminfo
= vmalloc(module
->arch
.nsyms
88 * sizeof(struct mod_arch_syminfo
));
89 if (!module
->arch
.syminfo
)
92 symbols
= (void *)hdr
+ symtab
->sh_offset
;
93 strings
= (void *)hdr
+ sechdrs
[symtab
->sh_link
].sh_offset
;
94 for (i
= 0; i
< module
->arch
.nsyms
; i
++) {
95 if (symbols
[i
].st_shndx
== SHN_UNDEF
&&
96 strcmp(strings
+ symbols
[i
].st_name
,
97 "_GLOBAL_OFFSET_TABLE_") == 0)
98 /* "Define" it as absolute. */
99 symbols
[i
].st_shndx
= SHN_ABS
;
100 module
->arch
.syminfo
[i
].got_offset
= -1UL;
101 module
->arch
.syminfo
[i
].got_initialized
= 0;
104 /* Allocate GOT entries for symbols that need it. */
105 module
->arch
.got_size
= 0;
106 for (i
= 0; i
< hdr
->e_shnum
; i
++) {
107 if (sechdrs
[i
].sh_type
!= SHT_RELA
)
109 nrela
= sechdrs
[i
].sh_size
/ sizeof(Elf32_Rela
);
110 rela
= (void *)hdr
+ sechdrs
[i
].sh_offset
;
111 for (j
= 0; j
< nrela
; j
++) {
112 ret
= check_rela(rela
+ j
, module
,
115 goto out_free_syminfo
;
120 * Increase core size to make room for GOT and set start
123 module
->core_size
= ALIGN(module
->core_size
, 4);
124 module
->arch
.got_offset
= module
->core_size
;
125 module
->core_size
+= module
->arch
.got_size
;
130 vfree(module
->arch
.syminfo
);
131 module
->arch
.syminfo
= NULL
;
136 static inline int reloc_overflow(struct module
*module
, const char *reloc_name
,
137 Elf32_Addr relocation
)
139 printk(KERN_ERR
"module %s: Value %lx does not fit relocation %s\n",
140 module
->name
, (unsigned long)relocation
, reloc_name
);
144 #define get_u16(loc) (*((uint16_t *)loc))
145 #define put_u16(loc, val) (*((uint16_t *)loc) = (val))
147 int apply_relocate_add(Elf32_Shdr
*sechdrs
, const char *strtab
,
148 unsigned int symindex
, unsigned int relindex
,
149 struct module
*module
)
151 Elf32_Shdr
*symsec
= sechdrs
+ symindex
;
152 Elf32_Shdr
*relsec
= sechdrs
+ relindex
;
153 Elf32_Shdr
*dstsec
= sechdrs
+ relsec
->sh_info
;
154 Elf32_Rela
*rel
= (void *)relsec
->sh_addr
;
158 for (i
= 0; i
< relsec
->sh_size
/ sizeof(Elf32_Rela
); i
++, rel
++) {
159 struct mod_arch_syminfo
*info
;
161 Elf32_Addr relocation
;
165 location
= (void *)dstsec
->sh_addr
+ rel
->r_offset
;
166 sym
= (Elf32_Sym
*)symsec
->sh_addr
+ ELF32_R_SYM(rel
->r_info
);
167 relocation
= sym
->st_value
+ rel
->r_addend
;
169 info
= module
->arch
.syminfo
+ ELF32_R_SYM(rel
->r_info
);
171 /* Initialize GOT entry if necessary */
172 switch (ELF32_R_TYPE(rel
->r_info
)) {
177 case R_AVR32_GOT18SW
:
179 if (!info
->got_initialized
) {
182 gotent
= (module
->module_core
183 + module
->arch
.got_offset
185 *gotent
= relocation
;
186 info
->got_initialized
= 1;
189 relocation
= info
->got_offset
;
193 switch (ELF32_R_TYPE(rel
->r_info
)) {
195 case R_AVR32_32_CPENT
:
196 *location
= relocation
;
198 case R_AVR32_22H_PCREL
:
199 relocation
-= (Elf32_Addr
)location
;
200 if ((relocation
& 0xffe00001) != 0
201 && (relocation
& 0xffc00001) != 0xffc00000)
202 return reloc_overflow(module
,
208 value
= ((value
& 0xe1ef0000)
209 | (relocation
& 0xffff)
210 | ((relocation
& 0x10000) << 4)
211 | ((relocation
& 0x1e0000) << 8));
214 case R_AVR32_11H_PCREL
:
215 relocation
-= (Elf32_Addr
)location
;
216 if ((relocation
& 0xfffffc01) != 0
217 && (relocation
& 0xfffff801) != 0xfffff800)
218 return reloc_overflow(module
,
221 value
= get_u16(location
);
222 value
= ((value
& 0xf00c)
223 | ((relocation
& 0x1fe) << 3)
224 | ((relocation
& 0x600) >> 9));
225 put_u16(location
, value
);
227 case R_AVR32_9H_PCREL
:
228 relocation
-= (Elf32_Addr
)location
;
229 if ((relocation
& 0xffffff01) != 0
230 && (relocation
& 0xfffffe01) != 0xfffffe00)
231 return reloc_overflow(module
,
234 value
= get_u16(location
);
235 value
= ((value
& 0xf00f)
236 | ((relocation
& 0x1fe) << 3));
237 put_u16(location
, value
);
239 case R_AVR32_9UW_PCREL
:
240 relocation
-= ((Elf32_Addr
)location
) & 0xfffffffc;
241 if ((relocation
& 0xfffffc03) != 0)
242 return reloc_overflow(module
,
245 value
= get_u16(location
);
246 value
= ((value
& 0xf80f)
247 | ((relocation
& 0x1fc) << 2));
248 put_u16(location
, value
);
252 * R6 = PC - (PC - GOT)
254 * At this point, relocation contains the
255 * value of PC. Just subtract the value of
256 * GOT, and we're done.
258 pr_debug("GOTPC: PC=0x%x, got_offset=0x%lx, core=0x%p\n",
259 relocation
, module
->arch
.got_offset
,
260 module
->module_core
);
261 relocation
-= ((unsigned long)module
->module_core
262 + module
->arch
.got_offset
);
263 *location
= relocation
;
265 case R_AVR32_GOT18SW
:
266 if ((relocation
& 0xfffe0003) != 0
267 && (relocation
& 0xfffc0003) != 0xffff0000)
268 return reloc_overflow(module
, "R_AVR32_GOT18SW",
273 if ((relocation
& 0xffff8000) != 0
274 && (relocation
& 0xffff0000) != 0xffff0000)
275 return reloc_overflow(module
, "R_AVR32_GOT16S",
277 pr_debug("GOT reloc @ 0x%x -> %u\n",
278 rel
->r_offset
, relocation
);
280 value
= ((value
& 0xffff0000)
281 | (relocation
& 0xffff));
286 printk(KERN_ERR
"module %s: Unknown relocation: %u\n",
287 module
->name
, ELF32_R_TYPE(rel
->r_info
));
295 int module_finalize(const Elf_Ehdr
*hdr
, const Elf_Shdr
*sechdrs
,
296 struct module
*module
)
298 vfree(module
->arch
.syminfo
);
299 module
->arch
.syminfo
= NULL
;