Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
module.c
Go to the documentation of this file.
1 /* MN10300 Kernel module helper routines
2  *
3  * Copyright (C) 2007, 2008, 2009 Red Hat, Inc. All Rights Reserved.
4  * Written by Mark Salter ([email protected])
5  * - Derived from arch/i386/kernel/module.c
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public Licence as published by
9  * the Free Software Foundation; either version 2 of the Licence, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public Licence for more details.
16  *
17  * You should have received a copy of the GNU General Public Licence
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  */
21 #include <linux/moduleloader.h>
22 #include <linux/elf.h>
23 #include <linux/vmalloc.h>
24 #include <linux/fs.h>
25 #include <linux/string.h>
26 #include <linux/kernel.h>
27 #include <linux/bug.h>
28 
29 #if 0
30 #define DEBUGP printk
31 #else
32 #define DEBUGP(fmt, ...)
33 #endif
34 
35 static void reloc_put16(uint8_t *p, uint32_t val)
36 {
37  p[0] = val & 0xff;
38  p[1] = (val >> 8) & 0xff;
39 }
40 
41 static void reloc_put24(uint8_t *p, uint32_t val)
42 {
43  reloc_put16(p, val);
44  p[2] = (val >> 16) & 0xff;
45 }
46 
47 static void reloc_put32(uint8_t *p, uint32_t val)
48 {
49  reloc_put16(p, val);
50  reloc_put16(p+2, val >> 16);
51 }
52 
53 /*
54  * apply a RELA relocation
55  */
57  const char *strtab,
58  unsigned int symindex,
59  unsigned int relsec,
60  struct module *me)
61 {
62  unsigned int i, sym_diff_seen = 0;
63  Elf32_Rela *rel = (void *)sechdrs[relsec].sh_addr;
64  Elf32_Sym *sym;
65  Elf32_Addr relocation, sym_diff_val = 0;
68 
69  DEBUGP("Applying relocate section %u to %u\n",
70  relsec, sechdrs[relsec].sh_info);
71 
72  for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
73  /* this is where to make the change */
74  location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
75  + rel[i].r_offset;
76 
77  /* this is the symbol the relocation is referring to (note that
78  * all undefined symbols have been resolved by the caller) */
79  sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
80  + ELF32_R_SYM(rel[i].r_info);
81 
82  /* this is the adjustment to be made */
83  relocation = sym->st_value + rel[i].r_addend;
84 
85  if (sym_diff_seen) {
86  switch (ELF32_R_TYPE(rel[i].r_info)) {
87  case R_MN10300_32:
88  case R_MN10300_24:
89  case R_MN10300_16:
90  case R_MN10300_8:
91  relocation -= sym_diff_val;
92  sym_diff_seen = 0;
93  break;
94  default:
95  printk(KERN_ERR "module %s: Unexpected SYM_DIFF relocation: %u\n",
96  me->name, ELF32_R_TYPE(rel[i].r_info));
97  return -ENOEXEC;
98  }
99  }
100 
101  switch (ELF32_R_TYPE(rel[i].r_info)) {
102  /* for the first four relocation types, we simply
103  * store the adjustment at the location given */
104  case R_MN10300_32:
105  reloc_put32(location, relocation);
106  break;
107  case R_MN10300_24:
108  reloc_put24(location, relocation);
109  break;
110  case R_MN10300_16:
111  reloc_put16(location, relocation);
112  break;
113  case R_MN10300_8:
114  *location = relocation;
115  break;
116 
117  /* for the next three relocation types, we write the
118  * adjustment with the address subtracted over the
119  * value at the location given */
120  case R_MN10300_PCREL32:
121  value = relocation - (uint32_t) location;
122  reloc_put32(location, value);
123  break;
124  case R_MN10300_PCREL16:
125  value = relocation - (uint32_t) location;
126  reloc_put16(location, value);
127  break;
128  case R_MN10300_PCREL8:
129  *location = relocation - (uint32_t) location;
130  break;
131 
132  case R_MN10300_SYM_DIFF:
133  /* This is used to adjust the next reloc as required
134  * by relaxation. */
135  sym_diff_seen = 1;
136  sym_diff_val = sym->st_value;
137  break;
138 
139  case R_MN10300_ALIGN:
140  /* Just ignore the ALIGN relocs.
141  * Only interesting if kernel performed relaxation. */
142  continue;
143 
144  default:
145  printk(KERN_ERR "module %s: Unknown relocation: %u\n",
146  me->name, ELF32_R_TYPE(rel[i].r_info));
147  return -ENOEXEC;
148  }
149  }
150  if (sym_diff_seen) {
151  printk(KERN_ERR "module %s: Nothing follows SYM_DIFF relocation: %u\n",
152  me->name, ELF32_R_TYPE(rel[i].r_info));
153  return -ENOEXEC;
154  }
155  return 0;
156 }