1ed981856SPaul Mackerras /* Kernel module help for PPC. 2ed981856SPaul Mackerras Copyright (C) 2001 Rusty Russell. 3ed981856SPaul Mackerras 4ed981856SPaul Mackerras This program is free software; you can redistribute it and/or modify 5ed981856SPaul Mackerras it under the terms of the GNU General Public License as published by 6ed981856SPaul Mackerras the Free Software Foundation; either version 2 of the License, or 7ed981856SPaul Mackerras (at your option) any later version. 8ed981856SPaul Mackerras 9ed981856SPaul Mackerras This program is distributed in the hope that it will be useful, 10ed981856SPaul Mackerras but WITHOUT ANY WARRANTY; without even the implied warranty of 11ed981856SPaul Mackerras MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12ed981856SPaul Mackerras GNU General Public License for more details. 13ed981856SPaul Mackerras 14ed981856SPaul Mackerras You should have received a copy of the GNU General Public License 15ed981856SPaul Mackerras along with this program; if not, write to the Free Software 16ed981856SPaul Mackerras Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17ed981856SPaul Mackerras */ 18ed981856SPaul Mackerras #include <linux/module.h> 19ed981856SPaul Mackerras #include <linux/moduleloader.h> 20ed981856SPaul Mackerras #include <linux/elf.h> 21ed981856SPaul Mackerras #include <linux/vmalloc.h> 22ed981856SPaul Mackerras #include <linux/fs.h> 23ed981856SPaul Mackerras #include <linux/string.h> 24ed981856SPaul Mackerras #include <linux/kernel.h> 257cc45e64SSteven Rostedt #include <linux/ftrace.h> 26ed981856SPaul Mackerras #include <linux/cache.h> 2773c9ceabSJeremy Fitzhardinge #include <linux/bug.h> 28eda09fbdSEmil Medve #include <linux/sort.h> 29ed981856SPaul Mackerras 3021c4ff80SBenjamin Herrenschmidt #include "setup.h" 3121c4ff80SBenjamin Herrenschmidt 32ed981856SPaul Mackerras #if 0 33ed981856SPaul Mackerras #define DEBUGP printk 34ed981856SPaul Mackerras #else 35ed981856SPaul Mackerras #define DEBUGP(fmt , ...) 36ed981856SPaul Mackerras #endif 37ed981856SPaul Mackerras 38ed981856SPaul Mackerras /* Count how many different relocations (different symbol, different 39ed981856SPaul Mackerras addend) */ 40ed981856SPaul Mackerras static unsigned int count_relocs(const Elf32_Rela *rela, unsigned int num) 41ed981856SPaul Mackerras { 42eda09fbdSEmil Medve unsigned int i, r_info, r_addend, _count_relocs; 43ed981856SPaul Mackerras 44eda09fbdSEmil Medve _count_relocs = 0; 45eda09fbdSEmil Medve r_info = 0; 46eda09fbdSEmil Medve r_addend = 0; 47eda09fbdSEmil Medve for (i = 0; i < num; i++) 48eda09fbdSEmil Medve /* Only count 24-bit relocs, others don't need stubs */ 49eda09fbdSEmil Medve if (ELF32_R_TYPE(rela[i].r_info) == R_PPC_REL24 && 50eda09fbdSEmil Medve (r_info != ELF32_R_SYM(rela[i].r_info) || 51eda09fbdSEmil Medve r_addend != rela[i].r_addend)) { 52eda09fbdSEmil Medve _count_relocs++; 53eda09fbdSEmil Medve r_info = ELF32_R_SYM(rela[i].r_info); 54eda09fbdSEmil Medve r_addend = rela[i].r_addend; 55ed981856SPaul Mackerras } 56eda09fbdSEmil Medve 577cc45e64SSteven Rostedt #ifdef CONFIG_DYNAMIC_FTRACE 587cc45e64SSteven Rostedt _count_relocs++; /* add one for ftrace_caller */ 597cc45e64SSteven Rostedt #endif 60eda09fbdSEmil Medve return _count_relocs; 61ed981856SPaul Mackerras } 62eda09fbdSEmil Medve 63eda09fbdSEmil Medve static int relacmp(const void *_x, const void *_y) 64eda09fbdSEmil Medve { 65eda09fbdSEmil Medve const Elf32_Rela *x, *y; 66eda09fbdSEmil Medve 67eda09fbdSEmil Medve y = (Elf32_Rela *)_x; 68eda09fbdSEmil Medve x = (Elf32_Rela *)_y; 69eda09fbdSEmil Medve 70eda09fbdSEmil Medve /* Compare the entire r_info (as opposed to ELF32_R_SYM(r_info) only) to 71eda09fbdSEmil Medve * make the comparison cheaper/faster. It won't affect the sorting or 72eda09fbdSEmil Medve * the counting algorithms' performance 73eda09fbdSEmil Medve */ 74eda09fbdSEmil Medve if (x->r_info < y->r_info) 75eda09fbdSEmil Medve return -1; 76eda09fbdSEmil Medve else if (x->r_info > y->r_info) 77eda09fbdSEmil Medve return 1; 78eda09fbdSEmil Medve else if (x->r_addend < y->r_addend) 79eda09fbdSEmil Medve return -1; 80eda09fbdSEmil Medve else if (x->r_addend > y->r_addend) 81eda09fbdSEmil Medve return 1; 82eda09fbdSEmil Medve else 83eda09fbdSEmil Medve return 0; 84eda09fbdSEmil Medve } 85eda09fbdSEmil Medve 86eda09fbdSEmil Medve static void relaswap(void *_x, void *_y, int size) 87eda09fbdSEmil Medve { 88eda09fbdSEmil Medve uint32_t *x, *y, tmp; 89eda09fbdSEmil Medve int i; 90eda09fbdSEmil Medve 91eda09fbdSEmil Medve y = (uint32_t *)_x; 92eda09fbdSEmil Medve x = (uint32_t *)_y; 93eda09fbdSEmil Medve 94eda09fbdSEmil Medve for (i = 0; i < sizeof(Elf32_Rela) / sizeof(uint32_t); i++) { 95eda09fbdSEmil Medve tmp = x[i]; 96eda09fbdSEmil Medve x[i] = y[i]; 97eda09fbdSEmil Medve y[i] = tmp; 98eda09fbdSEmil Medve } 99ed981856SPaul Mackerras } 100ed981856SPaul Mackerras 101ed981856SPaul Mackerras /* Get the potential trampolines size required of the init and 102ed981856SPaul Mackerras non-init sections */ 103ed981856SPaul Mackerras static unsigned long get_plt_size(const Elf32_Ehdr *hdr, 104ed981856SPaul Mackerras const Elf32_Shdr *sechdrs, 105ed981856SPaul Mackerras const char *secstrings, 106ed981856SPaul Mackerras int is_init) 107ed981856SPaul Mackerras { 108ed981856SPaul Mackerras unsigned long ret = 0; 109ed981856SPaul Mackerras unsigned i; 110ed981856SPaul Mackerras 111ed981856SPaul Mackerras /* Everything marked ALLOC (this includes the exported 112ed981856SPaul Mackerras symbols) */ 113ed981856SPaul Mackerras for (i = 1; i < hdr->e_shnum; i++) { 114ed981856SPaul Mackerras /* If it's called *.init*, and we're not init, we're 115ed981856SPaul Mackerras not interested */ 116ed981856SPaul Mackerras if ((strstr(secstrings + sechdrs[i].sh_name, ".init") != 0) 117ed981856SPaul Mackerras != is_init) 118ed981856SPaul Mackerras continue; 119ed981856SPaul Mackerras 120ed981856SPaul Mackerras /* We don't want to look at debug sections. */ 121ed981856SPaul Mackerras if (strstr(secstrings + sechdrs[i].sh_name, ".debug") != 0) 122ed981856SPaul Mackerras continue; 123ed981856SPaul Mackerras 124ed981856SPaul Mackerras if (sechdrs[i].sh_type == SHT_RELA) { 125ed981856SPaul Mackerras DEBUGP("Found relocations in section %u\n", i); 126ed981856SPaul Mackerras DEBUGP("Ptr: %p. Number: %u\n", 127ed981856SPaul Mackerras (void *)hdr + sechdrs[i].sh_offset, 128ed981856SPaul Mackerras sechdrs[i].sh_size / sizeof(Elf32_Rela)); 129eda09fbdSEmil Medve 130eda09fbdSEmil Medve /* Sort the relocation information based on a symbol and 131eda09fbdSEmil Medve * addend key. This is a stable O(n*log n) complexity 132eda09fbdSEmil Medve * alogrithm but it will reduce the complexity of 133eda09fbdSEmil Medve * count_relocs() to linear complexity O(n) 134eda09fbdSEmil Medve */ 135eda09fbdSEmil Medve sort((void *)hdr + sechdrs[i].sh_offset, 136eda09fbdSEmil Medve sechdrs[i].sh_size / sizeof(Elf32_Rela), 137eda09fbdSEmil Medve sizeof(Elf32_Rela), relacmp, relaswap); 138eda09fbdSEmil Medve 139ed981856SPaul Mackerras ret += count_relocs((void *)hdr 140ed981856SPaul Mackerras + sechdrs[i].sh_offset, 141ed981856SPaul Mackerras sechdrs[i].sh_size 142ed981856SPaul Mackerras / sizeof(Elf32_Rela)) 143ed981856SPaul Mackerras * sizeof(struct ppc_plt_entry); 144ed981856SPaul Mackerras } 145ed981856SPaul Mackerras } 146ed981856SPaul Mackerras 147ed981856SPaul Mackerras return ret; 148ed981856SPaul Mackerras } 149ed981856SPaul Mackerras 150ed981856SPaul Mackerras int module_frob_arch_sections(Elf32_Ehdr *hdr, 151ed981856SPaul Mackerras Elf32_Shdr *sechdrs, 152ed981856SPaul Mackerras char *secstrings, 153ed981856SPaul Mackerras struct module *me) 154ed981856SPaul Mackerras { 155ed981856SPaul Mackerras unsigned int i; 156ed981856SPaul Mackerras 157ed981856SPaul Mackerras /* Find .plt and .init.plt sections */ 158ed981856SPaul Mackerras for (i = 0; i < hdr->e_shnum; i++) { 159ed981856SPaul Mackerras if (strcmp(secstrings + sechdrs[i].sh_name, ".init.plt") == 0) 160ed981856SPaul Mackerras me->arch.init_plt_section = i; 161ed981856SPaul Mackerras else if (strcmp(secstrings + sechdrs[i].sh_name, ".plt") == 0) 162ed981856SPaul Mackerras me->arch.core_plt_section = i; 163ed981856SPaul Mackerras } 164ed981856SPaul Mackerras if (!me->arch.core_plt_section || !me->arch.init_plt_section) { 165ed981856SPaul Mackerras printk("Module doesn't contain .plt or .init.plt sections.\n"); 166ed981856SPaul Mackerras return -ENOEXEC; 167ed981856SPaul Mackerras } 168ed981856SPaul Mackerras 169ed981856SPaul Mackerras /* Override their sizes */ 170ed981856SPaul Mackerras sechdrs[me->arch.core_plt_section].sh_size 171ed981856SPaul Mackerras = get_plt_size(hdr, sechdrs, secstrings, 0); 172ed981856SPaul Mackerras sechdrs[me->arch.init_plt_section].sh_size 173ed981856SPaul Mackerras = get_plt_size(hdr, sechdrs, secstrings, 1); 174ed981856SPaul Mackerras return 0; 175ed981856SPaul Mackerras } 176ed981856SPaul Mackerras 177ed981856SPaul Mackerras int apply_relocate(Elf32_Shdr *sechdrs, 178ed981856SPaul Mackerras const char *strtab, 179ed981856SPaul Mackerras unsigned int symindex, 180ed981856SPaul Mackerras unsigned int relsec, 181ed981856SPaul Mackerras struct module *module) 182ed981856SPaul Mackerras { 183ed981856SPaul Mackerras printk(KERN_ERR "%s: Non-ADD RELOCATION unsupported\n", 184ed981856SPaul Mackerras module->name); 185ed981856SPaul Mackerras return -ENOEXEC; 186ed981856SPaul Mackerras } 187ed981856SPaul Mackerras 188ed981856SPaul Mackerras static inline int entry_matches(struct ppc_plt_entry *entry, Elf32_Addr val) 189ed981856SPaul Mackerras { 190ed981856SPaul Mackerras if (entry->jump[0] == 0x3d600000 + ((val + 0x8000) >> 16) 191ed981856SPaul Mackerras && entry->jump[1] == 0x396b0000 + (val & 0xffff)) 192ed981856SPaul Mackerras return 1; 193ed981856SPaul Mackerras return 0; 194ed981856SPaul Mackerras } 195ed981856SPaul Mackerras 196ed981856SPaul Mackerras /* Set up a trampoline in the PLT to bounce us to the distant function */ 197ed981856SPaul Mackerras static uint32_t do_plt_call(void *location, 198ed981856SPaul Mackerras Elf32_Addr val, 199ed981856SPaul Mackerras Elf32_Shdr *sechdrs, 200ed981856SPaul Mackerras struct module *mod) 201ed981856SPaul Mackerras { 202ed981856SPaul Mackerras struct ppc_plt_entry *entry; 203ed981856SPaul Mackerras 204ed981856SPaul Mackerras DEBUGP("Doing plt for call to 0x%x at 0x%x\n", val, (unsigned int)location); 205ed981856SPaul Mackerras /* Init, or core PLT? */ 206ed981856SPaul Mackerras if (location >= mod->module_core 207ed981856SPaul Mackerras && location < mod->module_core + mod->core_size) 208ed981856SPaul Mackerras entry = (void *)sechdrs[mod->arch.core_plt_section].sh_addr; 209ed981856SPaul Mackerras else 210ed981856SPaul Mackerras entry = (void *)sechdrs[mod->arch.init_plt_section].sh_addr; 211ed981856SPaul Mackerras 212ed981856SPaul Mackerras /* Find this entry, or if that fails, the next avail. entry */ 213ed981856SPaul Mackerras while (entry->jump[0]) { 214ed981856SPaul Mackerras if (entry_matches(entry, val)) return (uint32_t)entry; 215ed981856SPaul Mackerras entry++; 216ed981856SPaul Mackerras } 217ed981856SPaul Mackerras 218ed981856SPaul Mackerras /* Stolen from Paul Mackerras as well... */ 219ed981856SPaul Mackerras entry->jump[0] = 0x3d600000+((val+0x8000)>>16); /* lis r11,sym@ha */ 220ed981856SPaul Mackerras entry->jump[1] = 0x396b0000 + (val&0xffff); /* addi r11,r11,sym@l*/ 221ed981856SPaul Mackerras entry->jump[2] = 0x7d6903a6; /* mtctr r11 */ 222ed981856SPaul Mackerras entry->jump[3] = 0x4e800420; /* bctr */ 223ed981856SPaul Mackerras 224ed981856SPaul Mackerras DEBUGP("Initialized plt for 0x%x at %p\n", val, entry); 225ed981856SPaul Mackerras return (uint32_t)entry; 226ed981856SPaul Mackerras } 227ed981856SPaul Mackerras 228ed981856SPaul Mackerras int apply_relocate_add(Elf32_Shdr *sechdrs, 229ed981856SPaul Mackerras const char *strtab, 230ed981856SPaul Mackerras unsigned int symindex, 231ed981856SPaul Mackerras unsigned int relsec, 232ed981856SPaul Mackerras struct module *module) 233ed981856SPaul Mackerras { 234ed981856SPaul Mackerras unsigned int i; 235ed981856SPaul Mackerras Elf32_Rela *rela = (void *)sechdrs[relsec].sh_addr; 236ed981856SPaul Mackerras Elf32_Sym *sym; 237ed981856SPaul Mackerras uint32_t *location; 238ed981856SPaul Mackerras uint32_t value; 239ed981856SPaul Mackerras 240ed981856SPaul Mackerras DEBUGP("Applying ADD relocate section %u to %u\n", relsec, 241ed981856SPaul Mackerras sechdrs[relsec].sh_info); 242ed981856SPaul Mackerras for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) { 243ed981856SPaul Mackerras /* This is where to make the change */ 244ed981856SPaul Mackerras location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr 245ed981856SPaul Mackerras + rela[i].r_offset; 246ed981856SPaul Mackerras /* This is the symbol it is referring to. Note that all 247ed981856SPaul Mackerras undefined symbols have been resolved. */ 248ed981856SPaul Mackerras sym = (Elf32_Sym *)sechdrs[symindex].sh_addr 249ed981856SPaul Mackerras + ELF32_R_SYM(rela[i].r_info); 250ed981856SPaul Mackerras /* `Everything is relative'. */ 251ed981856SPaul Mackerras value = sym->st_value + rela[i].r_addend; 252ed981856SPaul Mackerras 253ed981856SPaul Mackerras switch (ELF32_R_TYPE(rela[i].r_info)) { 254ed981856SPaul Mackerras case R_PPC_ADDR32: 255ed981856SPaul Mackerras /* Simply set it */ 256ed981856SPaul Mackerras *(uint32_t *)location = value; 257ed981856SPaul Mackerras break; 258ed981856SPaul Mackerras 259ed981856SPaul Mackerras case R_PPC_ADDR16_LO: 260ed981856SPaul Mackerras /* Low half of the symbol */ 261ed981856SPaul Mackerras *(uint16_t *)location = value; 262ed981856SPaul Mackerras break; 263ed981856SPaul Mackerras 2649a3d6458SSimon Vallet case R_PPC_ADDR16_HI: 2659a3d6458SSimon Vallet /* Higher half of the symbol */ 2669a3d6458SSimon Vallet *(uint16_t *)location = (value >> 16); 2679a3d6458SSimon Vallet break; 2689a3d6458SSimon Vallet 269ed981856SPaul Mackerras case R_PPC_ADDR16_HA: 270ed981856SPaul Mackerras /* Sign-adjusted lower 16 bits: PPC ELF ABI says: 271ed981856SPaul Mackerras (((x >> 16) + ((x & 0x8000) ? 1 : 0))) & 0xFFFF. 272ed981856SPaul Mackerras This is the same, only sane. 273ed981856SPaul Mackerras */ 274ed981856SPaul Mackerras *(uint16_t *)location = (value + 0x8000) >> 16; 275ed981856SPaul Mackerras break; 276ed981856SPaul Mackerras 277ed981856SPaul Mackerras case R_PPC_REL24: 278ed981856SPaul Mackerras if ((int)(value - (uint32_t)location) < -0x02000000 279ed981856SPaul Mackerras || (int)(value - (uint32_t)location) >= 0x02000000) 280ed981856SPaul Mackerras value = do_plt_call(location, value, 281ed981856SPaul Mackerras sechdrs, module); 282ed981856SPaul Mackerras 283ed981856SPaul Mackerras /* Only replace bits 2 through 26 */ 284ed981856SPaul Mackerras DEBUGP("REL24 value = %08X. location = %08X\n", 285ed981856SPaul Mackerras value, (uint32_t)location); 286ed981856SPaul Mackerras DEBUGP("Location before: %08X.\n", 287ed981856SPaul Mackerras *(uint32_t *)location); 288ed981856SPaul Mackerras *(uint32_t *)location 289ed981856SPaul Mackerras = (*(uint32_t *)location & ~0x03fffffc) 290ed981856SPaul Mackerras | ((value - (uint32_t)location) 291ed981856SPaul Mackerras & 0x03fffffc); 292ed981856SPaul Mackerras DEBUGP("Location after: %08X.\n", 293ed981856SPaul Mackerras *(uint32_t *)location); 294ed981856SPaul Mackerras DEBUGP("ie. jump to %08X+%08X = %08X\n", 295ed981856SPaul Mackerras *(uint32_t *)location & 0x03fffffc, 296ed981856SPaul Mackerras (uint32_t)location, 297ed981856SPaul Mackerras (*(uint32_t *)location & 0x03fffffc) 298ed981856SPaul Mackerras + (uint32_t)location); 299ed981856SPaul Mackerras break; 300ed981856SPaul Mackerras 301ed981856SPaul Mackerras case R_PPC_REL32: 302ed981856SPaul Mackerras /* 32-bit relative jump. */ 303ed981856SPaul Mackerras *(uint32_t *)location = value - (uint32_t)location; 304ed981856SPaul Mackerras break; 305ed981856SPaul Mackerras 306ed981856SPaul Mackerras default: 307ed981856SPaul Mackerras printk("%s: unknown ADD relocation: %u\n", 308ed981856SPaul Mackerras module->name, 309ed981856SPaul Mackerras ELF32_R_TYPE(rela[i].r_info)); 310ed981856SPaul Mackerras return -ENOEXEC; 311ed981856SPaul Mackerras } 312ed981856SPaul Mackerras } 3137cc45e64SSteven Rostedt #ifdef CONFIG_DYNAMIC_FTRACE 3147cc45e64SSteven Rostedt module->arch.tramp = 3157cc45e64SSteven Rostedt do_plt_call(module->module_core, 3167cc45e64SSteven Rostedt (unsigned long)ftrace_caller, 3177cc45e64SSteven Rostedt sechdrs, module); 3187cc45e64SSteven Rostedt #endif 319ed981856SPaul Mackerras return 0; 320ed981856SPaul Mackerras } 321