a1bb70d6e97d1143606cba959c383d5079ea6582
[firefly-linux-kernel-4.4.55.git] / arch / arc / kernel / module.c
1 /*
2  * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  */
8
9 #include <linux/module.h>
10 #include <linux/moduleloader.h>
11 #include <linux/kernel.h>
12 #include <linux/elf.h>
13 #include <linux/vmalloc.h>
14 #include <linux/slab.h>
15 #include <linux/fs.h>
16 #include <linux/string.h>
17
18 static inline void arc_write_me(unsigned short *addr, unsigned long value)
19 {
20         *addr = (value & 0xffff0000) >> 16;
21         *(addr + 1) = (value & 0xffff);
22 }
23
24 int apply_relocate_add(Elf32_Shdr *sechdrs,
25                        const char *strtab,
26                        unsigned int symindex,   /* sec index for sym tbl */
27                        unsigned int relsec,     /* sec index for relo sec */
28                        struct module *module)
29 {
30         int i, n;
31         Elf32_Rela *rel_entry = (void *)sechdrs[relsec].sh_addr;
32         Elf32_Sym *sym_entry, *sym_sec;
33         Elf32_Addr relocation;
34         Elf32_Addr location;
35         Elf32_Addr sec_to_patch;
36         int relo_type;
37
38         sec_to_patch = sechdrs[sechdrs[relsec].sh_info].sh_addr;
39         sym_sec = (Elf32_Sym *) sechdrs[symindex].sh_addr;
40         n = sechdrs[relsec].sh_size / sizeof(*rel_entry);
41
42         pr_debug("\n========== Module Sym reloc ===========================\n");
43         pr_debug("Section to fixup %x\n", sec_to_patch);
44         pr_debug("=========================================================\n");
45         pr_debug("rela->r_off | rela->addend | sym->st_value | ADDR | VALUE\n");
46         pr_debug("=========================================================\n");
47
48         /* Loop thru entries in relocation section */
49         for (i = 0; i < n; i++) {
50
51                 /* This is where to make the change */
52                 location = sec_to_patch + rel_entry[i].r_offset;
53
54                 /* This is the symbol it is referring to.  Note that all
55                    undefined symbols have been resolved.  */
56                 sym_entry = sym_sec + ELF32_R_SYM(rel_entry[i].r_info);
57
58                 relocation = sym_entry->st_value + rel_entry[i].r_addend;
59
60                 pr_debug("\t%x\t\t%x\t\t%x  %x %x [%s]\n",
61                         rel_entry[i].r_offset, rel_entry[i].r_addend,
62                         sym_entry->st_value, location, relocation,
63                         strtab + sym_entry->st_name);
64
65                 /* This assumes modules are built with -mlong-calls
66                  * so any branches/jumps are absolute 32 bit jmps
67                  * global data access again is abs 32 bit.
68                  * Both of these are handled by same relocation type
69                  */
70                 relo_type = ELF32_R_TYPE(rel_entry[i].r_info);
71
72                 if (likely(R_ARC_32_ME == relo_type))
73                         arc_write_me((unsigned short *)location, relocation);
74                 else if (R_ARC_32 == relo_type)
75                         *((Elf32_Addr *) location) = relocation;
76                 else
77                         goto relo_err;
78
79         }
80         return 0;
81
82 relo_err:
83         pr_err("%s: unknown relocation: %u\n",
84                 module->name, ELF32_R_TYPE(rel_entry[i].r_info));
85         return -ENOEXEC;
86
87 }