11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Kernel module help for PPC64. 21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds Copyright (C) 2001, 2003 Rusty Russell IBM Corporation. 31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds This program is free software; you can redistribute it and/or modify 51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds it under the terms of the GNU General Public License as published by 61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds the Free Software Foundation; either version 2 of the License, or 71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (at your option) any later version. 81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds This program is distributed in the hope that it will be useful, 101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds but WITHOUT ANY WARRANTY; without even the implied warranty of 111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds GNU General Public License for more details. 131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds You should have received a copy of the GNU General Public License 151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds along with this program; if not, write to the Free Software 161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/ 181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h> 191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/elf.h> 201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/moduleloader.h> 211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/err.h> 221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/vmalloc.h> 23f48cb8b48b0b10025ca9c451b9b32cac3fcd33baSteven Rostedt#include <linux/ftrace.h> 2473c9ceab40b1269d6195e556773167c078ac8311Jeremy Fitzhardinge#include <linux/bug.h> 251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/module.h> 2621c4ff80cba5e24932f3ef79c8482c0491630b2bBenjamin Herrenschmidt#include <asm/firmware.h> 27b7bcda631e87eb3466d0baa9885650ba7d7ed89dMichael Ellerman#include <asm/code-patching.h> 28eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve#include <linux/sort.h> 2921c4ff80cba5e24932f3ef79c8482c0491630b2bBenjamin Herrenschmidt 3021c4ff80cba5e24932f3ef79c8482c0491630b2bBenjamin Herrenschmidt#include "setup.h" 311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* FIXME: We don't do .init separately. To do this, we'd need to have 331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds a separate r2 value in the init and core section, and stub between 341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds them, too. 351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds Using a magic allocator which places modules within 32MB solves 371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this, and makes other things simpler. Anton? 381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds --RR. */ 391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0 401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DEBUGP printk 411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else 421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DEBUGP(fmt , ...) 431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Like PPC32, we need little trampolines to do > 24-bit jumps (into 461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds the kernel itself). But on PPC64, these need to be used for every 471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds jump, actually, to reset r2 (TOC+0x8000). */ 481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct ppc64_stub_entry 491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 28 byte jump instruction sequence (7 instructions) */ 511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned char jump[28]; 521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned char unused[4]; 531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Data for the above code */ 541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct ppc64_opd_entry opd; 551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* We use a stub to fix up r2 (TOC ptr) and to jump to the (external) 581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds function which may be more than 24-bits away. We could simply 591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds patch the new r2 value and function pointer into the stub, but it's 601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds significantly shorter to put these values at the end of the stub 611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds code, and patch the stub address (32-bits relative to the TOC ptr, 621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds r2) into the stub. */ 631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct ppc64_stub_entry ppc64_stub = 641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ .jump = { 651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 0x3d, 0x82, 0x00, 0x00, /* addis r12,r2, <high> */ 661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 0x39, 0x8c, 0x00, 0x00, /* addi r12,r12, <low> */ 671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Save current r2 value in magic place on the stack. */ 681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 0xf8, 0x41, 0x00, 0x28, /* std r2,40(r1) */ 691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 0xe9, 0x6c, 0x00, 0x20, /* ld r11,32(r12) */ 701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 0xe8, 0x4c, 0x00, 0x28, /* ld r2,40(r12) */ 711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 0x7d, 0x69, 0x03, 0xa6, /* mtctr r11 */ 721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 0x4e, 0x80, 0x04, 0x20 /* bctr */ 731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} }; 741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Count how many different 24-bit relocations (different symbol, 761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds different addend) */ 771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int count_relocs(const Elf64_Rela *rela, unsigned int num) 781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 79eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve unsigned int i, r_info, r_addend, _count_relocs; 801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* FIXME: Only count external ones --RR */ 82eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve _count_relocs = 0; 83eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve r_info = 0; 84eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve r_addend = 0; 85eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve for (i = 0; i < num; i++) 861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Only count 24-bit relocs, others don't need stubs */ 87eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve if (ELF64_R_TYPE(rela[i].r_info) == R_PPC_REL24 && 88eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve (r_info != ELF64_R_SYM(rela[i].r_info) || 89eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve r_addend != rela[i].r_addend)) { 90eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve _count_relocs++; 91eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve r_info = ELF64_R_SYM(rela[i].r_info); 92eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve r_addend = rela[i].r_addend; 931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 94eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve 95eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve return _count_relocs; 961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 98eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medvestatic int relacmp(const void *_x, const void *_y) 99eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve{ 100eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve const Elf64_Rela *x, *y; 101eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve 102eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve y = (Elf64_Rela *)_x; 103eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve x = (Elf64_Rela *)_y; 104eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve 105eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve /* Compare the entire r_info (as opposed to ELF64_R_SYM(r_info) only) to 106eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve * make the comparison cheaper/faster. It won't affect the sorting or 107eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve * the counting algorithms' performance 108eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve */ 109eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve if (x->r_info < y->r_info) 110eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve return -1; 111eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve else if (x->r_info > y->r_info) 112eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve return 1; 113eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve else if (x->r_addend < y->r_addend) 114eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve return -1; 115eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve else if (x->r_addend > y->r_addend) 116eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve return 1; 117eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve else 118eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve return 0; 119eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve} 120eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve 121eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medvestatic void relaswap(void *_x, void *_y, int size) 122eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve{ 123eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve uint64_t *x, *y, tmp; 124eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve int i; 125eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve 126eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve y = (uint64_t *)_x; 127eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve x = (uint64_t *)_y; 128eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve 129eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve for (i = 0; i < sizeof(Elf64_Rela) / sizeof(uint64_t); i++) { 130eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve tmp = x[i]; 131eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve x[i] = y[i]; 132eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve y[i] = tmp; 133eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve } 134eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve} 135eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve 1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Get size of potential trampolines required. */ 1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned long get_stubs_size(const Elf64_Ehdr *hdr, 1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds const Elf64_Shdr *sechdrs) 1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* One extra reloc so it's always 0-funcaddr terminated */ 1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long relocs = 1; 1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned i; 1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Every relocated section... */ 1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i = 1; i < hdr->e_shnum; i++) { 1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (sechdrs[i].sh_type == SHT_RELA) { 1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DEBUGP("Found relocations in section %u\n", i); 1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DEBUGP("Ptr: %p. Number: %lu\n", 1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (void *)sechdrs[i].sh_addr, 1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds sechdrs[i].sh_size / sizeof(Elf64_Rela)); 151eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve 152eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve /* Sort the relocation information based on a symbol and 153eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve * addend key. This is a stable O(n*log n) complexity 154eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve * alogrithm but it will reduce the complexity of 155eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve * count_relocs() to linear complexity O(n) 156eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve */ 157eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve sort((void *)sechdrs[i].sh_addr, 158eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve sechdrs[i].sh_size / sizeof(Elf64_Rela), 159eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve sizeof(Elf64_Rela), relacmp, relaswap); 160eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve 1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds relocs += count_relocs((void *)sechdrs[i].sh_addr, 1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds sechdrs[i].sh_size 1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds / sizeof(Elf64_Rela)); 1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 167f48cb8b48b0b10025ca9c451b9b32cac3fcd33baSteven Rostedt#ifdef CONFIG_DYNAMIC_FTRACE 168f48cb8b48b0b10025ca9c451b9b32cac3fcd33baSteven Rostedt /* make the trampoline to the ftrace_caller */ 169f48cb8b48b0b10025ca9c451b9b32cac3fcd33baSteven Rostedt relocs++; 170f48cb8b48b0b10025ca9c451b9b32cac3fcd33baSteven Rostedt#endif 171f48cb8b48b0b10025ca9c451b9b32cac3fcd33baSteven Rostedt 1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DEBUGP("Looks like a total of %lu stubs, max\n", relocs); 1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return relocs * sizeof(struct ppc64_stub_entry); 1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void dedotify_versions(struct modversion_info *vers, 1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long size) 1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct modversion_info *end; 1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (end = (void *)vers + size; vers < end; vers++) 1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (vers->name[0] == '.') 1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memmove(vers->name, vers->name+1, strlen(vers->name)); 1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Undefined symbols which refer to .funcname, hack to funcname */ 1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void dedotify(Elf64_Sym *syms, unsigned int numsyms, char *strtab) 1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int i; 1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i = 1; i < numsyms; i++) { 1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (syms[i].st_shndx == SHN_UNDEF) { 1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds char *name = strtab + syms[i].st_name; 1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (name[0] == '.') 1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memmove(name, name+1, strlen(name)); 1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint module_frob_arch_sections(Elf64_Ehdr *hdr, 2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds Elf64_Shdr *sechdrs, 2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds char *secstrings, 2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct module *me) 2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int i; 2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Find .toc and .stubs sections, symtab and strtab */ 2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i = 1; i < hdr->e_shnum; i++) { 2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds char *p; 2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (strcmp(secstrings + sechdrs[i].sh_name, ".stubs") == 0) 2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds me->arch.stubs_section = i; 2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds else if (strcmp(secstrings + sechdrs[i].sh_name, ".toc") == 0) 2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds me->arch.toc_section = i; 2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds else if (strcmp(secstrings+sechdrs[i].sh_name,"__versions")==0) 2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dedotify_versions((void *)hdr + sechdrs[i].sh_offset, 2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds sechdrs[i].sh_size); 2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* We don't handle .init for the moment: rename to _init */ 2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds while ((p = strstr(secstrings + sechdrs[i].sh_name, ".init"))) 2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds p[0] = '_'; 2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (sechdrs[i].sh_type == SHT_SYMTAB) 2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dedotify((void *)hdr + sechdrs[i].sh_offset, 2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds sechdrs[i].sh_size / sizeof(Elf64_Sym), 2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (void *)hdr 2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds + sechdrs[sechdrs[i].sh_link].sh_offset); 2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 228f749edae5ebd339eaf22508572233600f717424fAlan Modra 229f749edae5ebd339eaf22508572233600f717424fAlan Modra if (!me->arch.stubs_section) { 230f749edae5ebd339eaf22508572233600f717424fAlan Modra printk("%s: doesn't contain .stubs.\n", me->name); 2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOEXEC; 2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 234f749edae5ebd339eaf22508572233600f717424fAlan Modra /* If we don't have a .toc, just use .stubs. We need to set r2 235f749edae5ebd339eaf22508572233600f717424fAlan Modra to some reasonable value in case the module calls out to 236f749edae5ebd339eaf22508572233600f717424fAlan Modra other functions via a stub, or if a function pointer escapes 237f749edae5ebd339eaf22508572233600f717424fAlan Modra the module by some means. */ 238f749edae5ebd339eaf22508572233600f717424fAlan Modra if (!me->arch.toc_section) 239f749edae5ebd339eaf22508572233600f717424fAlan Modra me->arch.toc_section = me->arch.stubs_section; 240f749edae5ebd339eaf22508572233600f717424fAlan Modra 2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Override the stubs size */ 2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds sechdrs[me->arch.stubs_section].sh_size = get_stubs_size(hdr, sechdrs); 2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* r2 is the TOC pointer: it actually points 0x8000 into the TOC (this 2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds gives the value maximum span in an instruction which uses a signed 2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds offset) */ 2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline unsigned long my_r2(Elf64_Shdr *sechdrs, struct module *me) 2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return sechdrs[me->arch.toc_section].sh_addr + 0x8000; 2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Both low and high 16 bits are added as SIGNED additions, so if low 2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 16 bits has high bit set, high 16 bits must be adjusted. These 2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds macros do that (stolen from binutils). */ 2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define PPC_LO(v) ((v) & 0xffff) 2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define PPC_HI(v) (((v) >> 16) & 0xffff) 2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define PPC_HA(v) PPC_HI ((v) + 0x8000) 2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Patch stub to reference function and correct r2 value. */ 2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline int create_stub(Elf64_Shdr *sechdrs, 2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct ppc64_stub_entry *entry, 2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct ppc64_opd_entry *opd, 2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct module *me) 2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds Elf64_Half *loc1, *loc2; 2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds long reladdr; 2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *entry = ppc64_stub; 2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds loc1 = (Elf64_Half *)&entry->jump[2]; 2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds loc2 = (Elf64_Half *)&entry->jump[6]; 2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Stub uses address relative to r2. */ 2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds reladdr = (unsigned long)entry - my_r2(sechdrs, me); 2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (reladdr > 0x7FFFFFFF || reladdr < -(0x80000000L)) { 2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("%s: Address %p of stub out of range of %p.\n", 2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds me->name, (void *)reladdr, (void *)my_r2); 2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DEBUGP("Stub %p get data from reladdr %li\n", entry, reladdr); 2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *loc1 = PPC_HA(reladdr); 2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *loc2 = PPC_LO(reladdr); 2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds entry->opd.funcaddr = opd->funcaddr; 2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds entry->opd.r2 = opd->r2; 2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 1; 2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Create stub to jump to function described in this OPD: we need the 2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds stub to set up the TOC ptr (r2) for the function. */ 2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned long stub_for_addr(Elf64_Shdr *sechdrs, 2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long opdaddr, 2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct module *me) 2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct ppc64_stub_entry *stubs; 2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct ppc64_opd_entry *opd = (void *)opdaddr; 2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int i, num_stubs; 3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds num_stubs = sechdrs[me->arch.stubs_section].sh_size / sizeof(*stubs); 3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Find this stub, or if that fails, the next avail. entry */ 3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds stubs = (void *)sechdrs[me->arch.stubs_section].sh_addr; 3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i = 0; stubs[i].opd.funcaddr; i++) { 3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds BUG_ON(i >= num_stubs); 3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (stubs[i].opd.funcaddr == opd->funcaddr) 3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return (unsigned long)&stubs[i]; 3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!create_stub(sechdrs, &stubs[i], opd, me)) 3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return (unsigned long)&stubs[i]; 3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* We expect a noop next: if it is, replace it with instruction to 3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds restore r2. */ 3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int restore_r2(u32 *instruction, struct module *me) 3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 32216c57b3620d77e0bc981da5ef32beae730512684Kumar Gala if (*instruction != PPC_INST_NOP) { 3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("%s: Expect noop after relocate, got %08x\n", 3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds me->name, *instruction); 3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *instruction = 0xe8410028; /* ld r2,40(r1) */ 3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 1; 3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint apply_relocate_add(Elf64_Shdr *sechdrs, 3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds const char *strtab, 3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int symindex, 3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int relsec, 3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct module *me) 3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int i; 3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds Elf64_Rela *rela = (void *)sechdrs[relsec].sh_addr; 3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds Elf64_Sym *sym; 3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long *location; 3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long value; 3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DEBUGP("Applying ADD relocate section %u to %u\n", relsec, 3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds sechdrs[relsec].sh_info); 3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) { 3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* This is where to make the change */ 3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr 3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds + rela[i].r_offset; 3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* This is the symbol it is referring to */ 3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds sym = (Elf64_Sym *)sechdrs[symindex].sh_addr 3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds + ELF64_R_SYM(rela[i].r_info); 3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DEBUGP("RELOC at %p: %li-type as %s (%lu) + %li\n", 3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds location, (long)ELF64_R_TYPE(rela[i].r_info), 3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds strtab + sym->st_name, (unsigned long)sym->st_value, 3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (long)rela[i].r_addend); 3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* `Everything is relative'. */ 3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds value = sym->st_value + rela[i].r_addend; 3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (ELF64_R_TYPE(rela[i].r_info)) { 3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case R_PPC64_ADDR32: 3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Simply set it */ 3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *(u32 *)location = value; 3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 366eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve 3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case R_PPC64_ADDR64: 3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Simply set it */ 3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *(unsigned long *)location = value; 3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case R_PPC64_TOC: 3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *(unsigned long *)location = my_r2(sechdrs, me); 3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3769149ccfa3571eaa4a4b444777d67fc4ed3ebcf27Peter Bergner case R_PPC64_TOC16: 377f749edae5ebd339eaf22508572233600f717424fAlan Modra /* Subtract TOC pointer */ 3789149ccfa3571eaa4a4b444777d67fc4ed3ebcf27Peter Bergner value -= my_r2(sechdrs, me); 3799149ccfa3571eaa4a4b444777d67fc4ed3ebcf27Peter Bergner if (value + 0x8000 > 0xffff) { 3809149ccfa3571eaa4a4b444777d67fc4ed3ebcf27Peter Bergner printk("%s: bad TOC16 relocation (%lu)\n", 3819149ccfa3571eaa4a4b444777d67fc4ed3ebcf27Peter Bergner me->name, value); 3829149ccfa3571eaa4a4b444777d67fc4ed3ebcf27Peter Bergner return -ENOEXEC; 3839149ccfa3571eaa4a4b444777d67fc4ed3ebcf27Peter Bergner } 3849149ccfa3571eaa4a4b444777d67fc4ed3ebcf27Peter Bergner *((uint16_t *) location) 3859149ccfa3571eaa4a4b444777d67fc4ed3ebcf27Peter Bergner = (*((uint16_t *) location) & ~0xffff) 3869149ccfa3571eaa4a4b444777d67fc4ed3ebcf27Peter Bergner | (value & 0xffff); 3879149ccfa3571eaa4a4b444777d67fc4ed3ebcf27Peter Bergner break; 3889149ccfa3571eaa4a4b444777d67fc4ed3ebcf27Peter Bergner 3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case R_PPC64_TOC16_DS: 390f749edae5ebd339eaf22508572233600f717424fAlan Modra /* Subtract TOC pointer */ 3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds value -= my_r2(sechdrs, me); 3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ((value & 3) != 0 || value + 0x8000 > 0xffff) { 3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("%s: bad TOC16_DS relocation (%lu)\n", 3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds me->name, value); 3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOEXEC; 3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *((uint16_t *) location) 3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds = (*((uint16_t *) location) & ~0xfffc) 3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds | (value & 0xfffc); 4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case R_PPC_REL24: 4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* FIXME: Handle weak symbols here --RR */ 4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (sym->st_shndx == SHN_UNDEF) { 4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* External: go via stub */ 4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds value = stub_for_addr(sechdrs, value, me); 4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!value) 4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOENT; 4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!restore_r2((u32 *)location + 1, me)) 4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOEXEC; 4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Convert value to relative */ 4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds value -= (unsigned long)location; 4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (value + 0x2000000 > 0x3ffffff || (value & 3) != 0){ 4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("%s: REL24 %li out of range!\n", 4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds me->name, (long int)value); 4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOEXEC; 4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Only replace bits 2 through 26 */ 422eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5aEmil Medve *(uint32_t *)location 4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds = (*(uint32_t *)location & ~0x03fffffc) 4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds | (value & 0x03fffffc); 4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 42721c4ff80cba5e24932f3ef79c8482c0491630b2bBenjamin Herrenschmidt case R_PPC64_REL64: 42821c4ff80cba5e24932f3ef79c8482c0491630b2bBenjamin Herrenschmidt /* 64 bits relative (used by features fixups) */ 42921c4ff80cba5e24932f3ef79c8482c0491630b2bBenjamin Herrenschmidt *location = value - (unsigned long)location; 43021c4ff80cba5e24932f3ef79c8482c0491630b2bBenjamin Herrenschmidt break; 43121c4ff80cba5e24932f3ef79c8482c0491630b2bBenjamin Herrenschmidt 4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds default: 4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("%s: Unknown ADD relocation: %lu\n", 4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds me->name, 4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (unsigned long)ELF64_R_TYPE(rela[i].r_info)); 4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOEXEC; 4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 440f48cb8b48b0b10025ca9c451b9b32cac3fcd33baSteven Rostedt#ifdef CONFIG_DYNAMIC_FTRACE 441f48cb8b48b0b10025ca9c451b9b32cac3fcd33baSteven Rostedt me->arch.toc = my_r2(sechdrs, me); 442f48cb8b48b0b10025ca9c451b9b32cac3fcd33baSteven Rostedt me->arch.tramp = stub_for_addr(sechdrs, 443f48cb8b48b0b10025ca9c451b9b32cac3fcd33baSteven Rostedt (unsigned long)ftrace_caller, 444f48cb8b48b0b10025ca9c451b9b32cac3fcd33baSteven Rostedt me); 445f48cb8b48b0b10025ca9c451b9b32cac3fcd33baSteven Rostedt#endif 446f48cb8b48b0b10025ca9c451b9b32cac3fcd33baSteven Rostedt 4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 449