~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/powerpc/kernel/module_64.c

Version: ~ [ linux-5.12-rc7 ] ~ [ linux-5.11.13 ] ~ [ linux-5.10.29 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.111 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.186 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.230 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.266 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.266 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.18.140 ] ~ [ linux-3.16.85 ] ~ [ linux-3.14.79 ] ~ [ linux-3.12.74 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*  Kernel module help for PPC64.
  2     Copyright (C) 2001, 2003 Rusty Russell IBM Corporation.
  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 as published by
  6     the Free Software Foundation; either version 2 of the License, or
  7     (at your option) any later version.
  8 
  9     This program is distributed in the hope that it will be useful,
 10     but WITHOUT ANY WARRANTY; without even the implied warranty of
 11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 12     GNU General Public License for more details.
 13 
 14     You should have received a copy of the GNU General Public License
 15     along with this program; if not, write to the Free Software
 16     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 17 */
 18 #include <linux/module.h>
 19 #include <linux/elf.h>
 20 #include <linux/moduleloader.h>
 21 #include <linux/err.h>
 22 #include <linux/vmalloc.h>
 23 #include <linux/ftrace.h>
 24 #include <linux/bug.h>
 25 #include <asm/module.h>
 26 #include <asm/firmware.h>
 27 #include <asm/code-patching.h>
 28 #include <linux/sort.h>
 29 #include <asm/setup.h>
 30 
 31 /* FIXME: We don't do .init separately.  To do this, we'd need to have
 32    a separate r2 value in the init and core section, and stub between
 33    them, too.
 34 
 35    Using a magic allocator which places modules within 32MB solves
 36    this, and makes other things simpler.  Anton?
 37    --RR.  */
 38 #if 0
 39 #define DEBUGP printk
 40 #else
 41 #define DEBUGP(fmt , ...)
 42 #endif
 43 
 44 /* Like PPC32, we need little trampolines to do > 24-bit jumps (into
 45    the kernel itself).  But on PPC64, these need to be used for every
 46    jump, actually, to reset r2 (TOC+0x8000). */
 47 struct ppc64_stub_entry
 48 {
 49         /* 28 byte jump instruction sequence (7 instructions) */
 50         unsigned char jump[28];
 51         unsigned char unused[4];
 52         /* Data for the above code */
 53         struct ppc64_opd_entry opd;
 54 };
 55 
 56 /* We use a stub to fix up r2 (TOC ptr) and to jump to the (external)
 57    function which may be more than 24-bits away.  We could simply
 58    patch the new r2 value and function pointer into the stub, but it's
 59    significantly shorter to put these values at the end of the stub
 60    code, and patch the stub address (32-bits relative to the TOC ptr,
 61    r2) into the stub. */
 62 static struct ppc64_stub_entry ppc64_stub =
 63 { .jump = {
 64 #ifdef __LITTLE_ENDIAN__
 65         0x00, 0x00, 0x82, 0x3d, /* addis   r12,r2, <high> */
 66         0x00, 0x00, 0x8c, 0x39, /* addi    r12,r12, <low> */
 67         /* Save current r2 value in magic place on the stack. */
 68         0x28, 0x00, 0x41, 0xf8, /* std     r2,40(r1) */
 69         0x20, 0x00, 0x6c, 0xe9, /* ld      r11,32(r12) */
 70         0x28, 0x00, 0x4c, 0xe8, /* ld      r2,40(r12) */
 71         0xa6, 0x03, 0x69, 0x7d, /* mtctr   r11 */
 72         0x20, 0x04, 0x80, 0x4e  /* bctr */
 73 #else
 74         0x3d, 0x82, 0x00, 0x00, /* addis   r12,r2, <high> */
 75         0x39, 0x8c, 0x00, 0x00, /* addi    r12,r12, <low> */
 76         /* Save current r2 value in magic place on the stack. */
 77         0xf8, 0x41, 0x00, 0x28, /* std     r2,40(r1) */
 78         0xe9, 0x6c, 0x00, 0x20, /* ld      r11,32(r12) */
 79         0xe8, 0x4c, 0x00, 0x28, /* ld      r2,40(r12) */
 80         0x7d, 0x69, 0x03, 0xa6, /* mtctr   r11 */
 81         0x4e, 0x80, 0x04, 0x20  /* bctr */
 82 #endif
 83 } };
 84 
 85 /* Count how many different 24-bit relocations (different symbol,
 86    different addend) */
 87 static unsigned int count_relocs(const Elf64_Rela *rela, unsigned int num)
 88 {
 89         unsigned int i, r_info, r_addend, _count_relocs;
 90 
 91         /* FIXME: Only count external ones --RR */
 92         _count_relocs = 0;
 93         r_info = 0;
 94         r_addend = 0;
 95         for (i = 0; i < num; i++)
 96                 /* Only count 24-bit relocs, others don't need stubs */
 97                 if (ELF64_R_TYPE(rela[i].r_info) == R_PPC_REL24 &&
 98                     (r_info != ELF64_R_SYM(rela[i].r_info) ||
 99                      r_addend != rela[i].r_addend)) {
100                         _count_relocs++;
101                         r_info = ELF64_R_SYM(rela[i].r_info);
102                         r_addend = rela[i].r_addend;
103                 }
104 
105         return _count_relocs;
106 }
107 
108 static int relacmp(const void *_x, const void *_y)
109 {
110         const Elf64_Rela *x, *y;
111 
112         y = (Elf64_Rela *)_x;
113         x = (Elf64_Rela *)_y;
114 
115         /* Compare the entire r_info (as opposed to ELF64_R_SYM(r_info) only) to
116          * make the comparison cheaper/faster. It won't affect the sorting or
117          * the counting algorithms' performance
118          */
119         if (x->r_info < y->r_info)
120                 return -1;
121         else if (x->r_info > y->r_info)
122                 return 1;
123         else if (x->r_addend < y->r_addend)
124                 return -1;
125         else if (x->r_addend > y->r_addend)
126                 return 1;
127         else
128                 return 0;
129 }
130 
131 static void relaswap(void *_x, void *_y, int size)
132 {
133         uint64_t *x, *y, tmp;
134         int i;
135 
136         y = (uint64_t *)_x;
137         x = (uint64_t *)_y;
138 
139         for (i = 0; i < sizeof(Elf64_Rela) / sizeof(uint64_t); i++) {
140                 tmp = x[i];
141                 x[i] = y[i];
142                 y[i] = tmp;
143         }
144 }
145 
146 /* Get size of potential trampolines required. */
147 static unsigned long get_stubs_size(const Elf64_Ehdr *hdr,
148                                     const Elf64_Shdr *sechdrs)
149 {
150         /* One extra reloc so it's always 0-funcaddr terminated */
151         unsigned long relocs = 1;
152         unsigned i;
153 
154         /* Every relocated section... */
155         for (i = 1; i < hdr->e_shnum; i++) {
156                 if (sechdrs[i].sh_type == SHT_RELA) {
157                         DEBUGP("Found relocations in section %u\n", i);
158                         DEBUGP("Ptr: %p.  Number: %lu\n",
159                                (void *)sechdrs[i].sh_addr,
160                                sechdrs[i].sh_size / sizeof(Elf64_Rela));
161 
162                         /* Sort the relocation information based on a symbol and
163                          * addend key. This is a stable O(n*log n) complexity
164                          * alogrithm but it will reduce the complexity of
165                          * count_relocs() to linear complexity O(n)
166                          */
167                         sort((void *)sechdrs[i].sh_addr,
168                              sechdrs[i].sh_size / sizeof(Elf64_Rela),
169                              sizeof(Elf64_Rela), relacmp, relaswap);
170 
171                         relocs += count_relocs((void *)sechdrs[i].sh_addr,
172                                                sechdrs[i].sh_size
173                                                / sizeof(Elf64_Rela));
174                 }
175         }
176 
177 #ifdef CONFIG_DYNAMIC_FTRACE
178         /* make the trampoline to the ftrace_caller */
179         relocs++;
180 #endif
181 
182         DEBUGP("Looks like a total of %lu stubs, max\n", relocs);
183         return relocs * sizeof(struct ppc64_stub_entry);
184 }
185 
186 static void dedotify_versions(struct modversion_info *vers,
187                               unsigned long size)
188 {
189         struct modversion_info *end;
190 
191         for (end = (void *)vers + size; vers < end; vers++)
192                 if (vers->name[0] == '.')
193                         memmove(vers->name, vers->name+1, strlen(vers->name));
194 }
195 
196 /* Undefined symbols which refer to .funcname, hack to funcname */
197 static void dedotify(Elf64_Sym *syms, unsigned int numsyms, char *strtab)
198 {
199         unsigned int i;
200 
201         for (i = 1; i < numsyms; i++) {
202                 if (syms[i].st_shndx == SHN_UNDEF) {
203                         char *name = strtab + syms[i].st_name;
204                         if (name[0] == '.')
205                                 syms[i].st_name++;
206                 }
207         }
208 }
209 
210 int module_frob_arch_sections(Elf64_Ehdr *hdr,
211                               Elf64_Shdr *sechdrs,
212                               char *secstrings,
213                               struct module *me)
214 {
215         unsigned int i;
216 
217         /* Find .toc and .stubs sections, symtab and strtab */
218         for (i = 1; i < hdr->e_shnum; i++) {
219                 char *p;
220                 if (strcmp(secstrings + sechdrs[i].sh_name, ".stubs") == 0)
221                         me->arch.stubs_section = i;
222                 else if (strcmp(secstrings + sechdrs[i].sh_name, ".toc") == 0)
223                         me->arch.toc_section = i;
224                 else if (strcmp(secstrings+sechdrs[i].sh_name,"__versions")==0)
225                         dedotify_versions((void *)hdr + sechdrs[i].sh_offset,
226                                           sechdrs[i].sh_size);
227 
228                 /* We don't handle .init for the moment: rename to _init */
229                 while ((p = strstr(secstrings + sechdrs[i].sh_name, ".init")))
230                         p[0] = '_';
231 
232                 if (sechdrs[i].sh_type == SHT_SYMTAB)
233                         dedotify((void *)hdr + sechdrs[i].sh_offset,
234                                  sechdrs[i].sh_size / sizeof(Elf64_Sym),
235                                  (void *)hdr
236                                  + sechdrs[sechdrs[i].sh_link].sh_offset);
237         }
238 
239         if (!me->arch.stubs_section) {
240                 printk("%s: doesn't contain .stubs.\n", me->name);
241                 return -ENOEXEC;
242         }
243 
244         /* If we don't have a .toc, just use .stubs.  We need to set r2
245            to some reasonable value in case the module calls out to
246            other functions via a stub, or if a function pointer escapes
247            the module by some means.  */
248         if (!me->arch.toc_section)
249                 me->arch.toc_section = me->arch.stubs_section;
250 
251         /* Override the stubs size */
252         sechdrs[me->arch.stubs_section].sh_size = get_stubs_size(hdr, sechdrs);
253         return 0;
254 }
255 
256 /* r2 is the TOC pointer: it actually points 0x8000 into the TOC (this
257    gives the value maximum span in an instruction which uses a signed
258    offset) */
259 static inline unsigned long my_r2(Elf64_Shdr *sechdrs, struct module *me)
260 {
261         return sechdrs[me->arch.toc_section].sh_addr + 0x8000;
262 }
263 
264 /* Both low and high 16 bits are added as SIGNED additions, so if low
265    16 bits has high bit set, high 16 bits must be adjusted.  These
266    macros do that (stolen from binutils). */
267 #define PPC_LO(v) ((v) & 0xffff)
268 #define PPC_HI(v) (((v) >> 16) & 0xffff)
269 #define PPC_HA(v) PPC_HI ((v) + 0x8000)
270 
271 /* Patch stub to reference function and correct r2 value. */
272 static inline int create_stub(Elf64_Shdr *sechdrs,
273                               struct ppc64_stub_entry *entry,
274                               struct ppc64_opd_entry *opd,
275                               struct module *me)
276 {
277         Elf64_Half *loc1, *loc2;
278         long reladdr;
279 
280         *entry = ppc64_stub;
281 
282 #ifdef __LITTLE_ENDIAN__
283         loc1 = (Elf64_Half *)&entry->jump[0];
284         loc2 = (Elf64_Half *)&entry->jump[4];
285 #else
286         loc1 = (Elf64_Half *)&entry->jump[2];
287         loc2 = (Elf64_Half *)&entry->jump[6];
288 #endif
289 
290         /* Stub uses address relative to r2. */
291         reladdr = (unsigned long)entry - my_r2(sechdrs, me);
292         if (reladdr > 0x7FFFFFFF || reladdr < -(0x80000000L)) {
293                 printk("%s: Address %p of stub out of range of %p.\n",
294                        me->name, (void *)reladdr, (void *)my_r2);
295                 return 0;
296         }
297         DEBUGP("Stub %p get data from reladdr %li\n", entry, reladdr);
298 
299         *loc1 = PPC_HA(reladdr);
300         *loc2 = PPC_LO(reladdr);
301         entry->opd.funcaddr = opd->funcaddr;
302         entry->opd.r2 = opd->r2;
303         return 1;
304 }
305 
306 /* Create stub to jump to function described in this OPD: we need the
307    stub to set up the TOC ptr (r2) for the function. */
308 static unsigned long stub_for_addr(Elf64_Shdr *sechdrs,
309                                    unsigned long opdaddr,
310                                    struct module *me)
311 {
312         struct ppc64_stub_entry *stubs;
313         struct ppc64_opd_entry *opd = (void *)opdaddr;
314         unsigned int i, num_stubs;
315 
316         num_stubs = sechdrs[me->arch.stubs_section].sh_size / sizeof(*stubs);
317 
318         /* Find this stub, or if that fails, the next avail. entry */
319         stubs = (void *)sechdrs[me->arch.stubs_section].sh_addr;
320         for (i = 0; stubs[i].opd.funcaddr; i++) {
321                 BUG_ON(i >= num_stubs);
322 
323                 if (stubs[i].opd.funcaddr == opd->funcaddr)
324                         return (unsigned long)&stubs[i];
325         }
326 
327         if (!create_stub(sechdrs, &stubs[i], opd, me))
328                 return 0;
329 
330         return (unsigned long)&stubs[i];
331 }
332 
333 /* We expect a noop next: if it is, replace it with instruction to
334    restore r2. */
335 static int restore_r2(u32 *instruction, struct module *me)
336 {
337         if (*instruction != PPC_INST_NOP) {
338                 printk("%s: Expect noop after relocate, got %08x\n",
339                        me->name, *instruction);
340                 return 0;
341         }
342         *instruction = 0xe8410028;      /* ld r2,40(r1) */
343         return 1;
344 }
345 
346 int apply_relocate_add(Elf64_Shdr *sechdrs,
347                        const char *strtab,
348                        unsigned int symindex,
349                        unsigned int relsec,
350                        struct module *me)
351 {
352         unsigned int i;
353         Elf64_Rela *rela = (void *)sechdrs[relsec].sh_addr;
354         Elf64_Sym *sym;
355         unsigned long *location;
356         unsigned long value;
357 
358         DEBUGP("Applying ADD relocate section %u to %u\n", relsec,
359                sechdrs[relsec].sh_info);
360         for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) {
361                 /* This is where to make the change */
362                 location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
363                         + rela[i].r_offset;
364                 /* This is the symbol it is referring to */
365                 sym = (Elf64_Sym *)sechdrs[symindex].sh_addr
366                         + ELF64_R_SYM(rela[i].r_info);
367 
368                 DEBUGP("RELOC at %p: %li-type as %s (%lu) + %li\n",
369                        location, (long)ELF64_R_TYPE(rela[i].r_info),
370                        strtab + sym->st_name, (unsigned long)sym->st_value,
371                        (long)rela[i].r_addend);
372 
373                 /* `Everything is relative'. */
374                 value = sym->st_value + rela[i].r_addend;
375 
376                 switch (ELF64_R_TYPE(rela[i].r_info)) {
377                 case R_PPC64_ADDR32:
378                         /* Simply set it */
379                         *(u32 *)location = value;
380                         break;
381 
382                 case R_PPC64_ADDR64:
383                         /* Simply set it */
384                         *(unsigned long *)location = value;
385                         break;
386 
387                 case R_PPC64_TOC:
388                         *(unsigned long *)location = my_r2(sechdrs, me);
389                         break;
390 
391                 case R_PPC64_TOC16:
392                         /* Subtract TOC pointer */
393                         value -= my_r2(sechdrs, me);
394                         if (value + 0x8000 > 0xffff) {
395                                 printk("%s: bad TOC16 relocation (%lu)\n",
396                                        me->name, value);
397                                 return -ENOEXEC;
398                         }
399                         *((uint16_t *) location)
400                                 = (*((uint16_t *) location) & ~0xffff)
401                                 | (value & 0xffff);
402                         break;
403 
404                 case R_PPC64_TOC16_LO:
405                         /* Subtract TOC pointer */
406                         value -= my_r2(sechdrs, me);
407                         *((uint16_t *) location)
408                                 = (*((uint16_t *) location) & ~0xffff)
409                                 | (value & 0xffff);
410                         break;
411 
412                 case R_PPC64_TOC16_DS:
413                         /* Subtract TOC pointer */
414                         value -= my_r2(sechdrs, me);
415                         if ((value & 3) != 0 || value + 0x8000 > 0xffff) {
416                                 printk("%s: bad TOC16_DS relocation (%lu)\n",
417                                        me->name, value);
418                                 return -ENOEXEC;
419                         }
420                         *((uint16_t *) location)
421                                 = (*((uint16_t *) location) & ~0xfffc)
422                                 | (value & 0xfffc);
423                         break;
424 
425                 case R_PPC64_TOC16_LO_DS:
426                         /* Subtract TOC pointer */
427                         value -= my_r2(sechdrs, me);
428                         if ((value & 3) != 0) {
429                                 printk("%s: bad TOC16_LO_DS relocation (%lu)\n",
430                                        me->name, value);
431                                 return -ENOEXEC;
432                         }
433                         *((uint16_t *) location)
434                                 = (*((uint16_t *) location) & ~0xfffc)
435                                 | (value & 0xfffc);
436                         break;
437 
438                 case R_PPC64_TOC16_HA:
439                         /* Subtract TOC pointer */
440                         value -= my_r2(sechdrs, me);
441                         value = ((value + 0x8000) >> 16);
442                         *((uint16_t *) location)
443                                 = (*((uint16_t *) location) & ~0xffff)
444                                 | (value & 0xffff);
445                         break;
446 
447                 case R_PPC_REL24:
448                         /* FIXME: Handle weak symbols here --RR */
449                         if (sym->st_shndx == SHN_UNDEF) {
450                                 /* External: go via stub */
451                                 value = stub_for_addr(sechdrs, value, me);
452                                 if (!value)
453                                         return -ENOENT;
454                                 if (!restore_r2((u32 *)location + 1, me))
455                                         return -ENOEXEC;
456                         }
457 
458                         /* Convert value to relative */
459                         value -= (unsigned long)location;
460                         if (value + 0x2000000 > 0x3ffffff || (value & 3) != 0){
461                                 printk("%s: REL24 %li out of range!\n",
462                                        me->name, (long int)value);
463                                 return -ENOEXEC;
464                         }
465 
466                         /* Only replace bits 2 through 26 */
467                         *(uint32_t *)location
468                                 = (*(uint32_t *)location & ~0x03fffffc)
469                                 | (value & 0x03fffffc);
470                         break;
471 
472                 case R_PPC64_REL64:
473                         /* 64 bits relative (used by features fixups) */
474                         *location = value - (unsigned long)location;
475                         break;
476 
477                 default:
478                         printk("%s: Unknown ADD relocation: %lu\n",
479                                me->name,
480                                (unsigned long)ELF64_R_TYPE(rela[i].r_info));
481                         return -ENOEXEC;
482                 }
483         }
484 
485 #ifdef CONFIG_DYNAMIC_FTRACE
486         me->arch.toc = my_r2(sechdrs, me);
487         me->arch.tramp = stub_for_addr(sechdrs,
488                                        (unsigned long)ftrace_caller,
489                                        me);
490 #endif
491 
492         return 0;
493 }
494 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp