1 /* $NetBSD: ppc_reloc.c,v 1.32 2002/10/05 11:59:06 mycroft Exp $ */ 2 3 /*- 4 * Copyright (C) 1998 Tsubai Masanari 5 * Portions copyright 2002 Charles M. Hannum <root@ihack.net> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. The name of the author may not be used to endorse or promote products 17 * derived from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include <stdarg.h> 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <sys/types.h> 36 #include <sys/stat.h> 37 #include <machine/cpu.h> 38 39 #include "debug.h" 40 #include "rtld.h" 41 42 void _rtld_powerpc_pltcall __P((Elf_Word)); 43 void _rtld_powerpc_pltresolve __P((Elf_Word, Elf_Word)); 44 45 #define ha(x) ((((u_int32_t)(x) & 0x8000) ? \ 46 ((u_int32_t)(x) + 0x10000) : (u_int32_t)(x)) >> 16) 47 #define l(x) ((u_int32_t)(x) & 0xffff) 48 49 void _rtld_bind_start(void); 50 void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr); 51 caddr_t _rtld_bind __P((const Obj_Entry *, Elf_Word)); 52 53 /* 54 * Setup the plt glue routines. 55 */ 56 #define PLTCALL_SIZE 20 57 #define PLTRESOLVE_SIZE 24 58 59 void 60 _rtld_setup_pltgot(obj) 61 const Obj_Entry *obj; 62 { 63 Elf_Word *pltcall, *pltresolve; 64 Elf_Word *jmptab; 65 int N = obj->pltrelalim - obj->pltrela; 66 67 /* Entries beyond 8192 take twice as much space. */ 68 if (N > 8192) 69 N += N-8192; 70 71 pltcall = obj->pltgot; 72 jmptab = pltcall + 18 + N * 2; 73 74 memcpy(pltcall, _rtld_powerpc_pltcall, PLTCALL_SIZE); 75 pltcall[1] |= ha(jmptab); 76 pltcall[2] |= l(jmptab); 77 78 pltresolve = obj->pltgot + 8; 79 80 memcpy(pltresolve, _rtld_powerpc_pltresolve, PLTRESOLVE_SIZE); 81 pltresolve[0] |= ha(_rtld_bind_start); 82 pltresolve[1] |= l(_rtld_bind_start); 83 pltresolve[3] |= ha(obj); 84 pltresolve[4] |= l(obj); 85 86 __syncicache(pltcall, 72 + N * 8); 87 } 88 89 void 90 _rtld_relocate_nonplt_self(dynp, relocbase) 91 Elf_Dyn *dynp; 92 Elf_Addr relocbase; 93 { 94 const Elf_Rela *rela = 0, *relalim; 95 Elf_Addr relasz = 0; 96 Elf_Addr *where; 97 98 for (; dynp->d_tag != DT_NULL; dynp++) { 99 switch (dynp->d_tag) { 100 case DT_RELA: 101 rela = (const Elf_Rela *)(relocbase + dynp->d_un.d_ptr); 102 break; 103 case DT_RELASZ: 104 relasz = dynp->d_un.d_val; 105 break; 106 } 107 } 108 relalim = (const Elf_Rela *)((caddr_t)rela + relasz); 109 for (; rela < relalim; rela++) { 110 where = (Elf_Addr *)(relocbase + rela->r_offset); 111 *where = (Elf_Addr)(relocbase + rela->r_addend); 112 } 113 } 114 115 int 116 _rtld_relocate_nonplt_objects(obj) 117 const Obj_Entry *obj; 118 { 119 const Elf_Rela *rela; 120 121 for (rela = obj->rela; rela < obj->relalim; rela++) { 122 Elf_Addr *where; 123 const Elf_Sym *def; 124 const Obj_Entry *defobj; 125 Elf_Addr tmp; 126 unsigned long symnum; 127 128 where = (Elf_Addr *)(obj->relocbase + rela->r_offset); 129 symnum = ELF_R_SYM(rela->r_info); 130 131 switch (ELF_R_TYPE(rela->r_info)) { 132 #if 1 /* XXX Should not be necessary. */ 133 case R_TYPE(JMP_SLOT): 134 #endif 135 case R_TYPE(NONE): 136 break; 137 138 case R_TYPE(32): /* word32 S + A */ 139 case R_TYPE(GLOB_DAT): /* word32 S + A */ 140 def = _rtld_find_symdef(symnum, obj, &defobj, false); 141 if (def == NULL) 142 return -1; 143 144 tmp = (Elf_Addr)(defobj->relocbase + def->st_value + 145 rela->r_addend); 146 if (*where != tmp) 147 *where = tmp; 148 rdbg(("32/GLOB_DAT %s in %s --> %p in %s", 149 obj->strtab + obj->symtab[symnum].st_name, 150 obj->path, (void *)*where, defobj->path)); 151 break; 152 153 case R_TYPE(RELATIVE): /* word32 B + A */ 154 *where = (Elf_Addr)(obj->relocbase + rela->r_addend); 155 rdbg(("RELATIVE in %s --> %p", obj->path, 156 (void *)*where)); 157 break; 158 159 case R_TYPE(COPY): 160 /* 161 * These are deferred until all other relocations have 162 * been done. All we do here is make sure that the 163 * COPY relocation is not in a shared library. They 164 * are allowed only in executable files. 165 */ 166 if (obj->isdynamic) { 167 _rtld_error( 168 "%s: Unexpected R_COPY relocation in shared library", 169 obj->path); 170 return -1; 171 } 172 rdbg(("COPY (avoid in main)")); 173 break; 174 175 default: 176 rdbg(("sym = %lu, type = %lu, offset = %p, " 177 "addend = %p, contents = %p, symbol = %s", 178 symnum, (u_long)ELF_R_TYPE(rela->r_info), 179 (void *)rela->r_offset, (void *)rela->r_addend, 180 (void *)*where, 181 obj->strtab + obj->symtab[symnum].st_name)); 182 _rtld_error("%s: Unsupported relocation type %ld " 183 "in non-PLT relocations\n", 184 obj->path, (u_long) ELF_R_TYPE(rela->r_info)); 185 return -1; 186 } 187 } 188 return 0; 189 } 190 191 int 192 _rtld_relocate_plt_lazy(obj) 193 const Obj_Entry *obj; 194 { 195 const Elf_Rela *rela; 196 int reloff; 197 198 for (rela = obj->pltrela, reloff = 0; rela < obj->pltrelalim; rela++, reloff++) { 199 Elf_Word *where = (Elf_Word *)(obj->relocbase + rela->r_offset); 200 int distance; 201 Elf_Addr *pltresolve; 202 203 assert(ELF_R_TYPE(rela->r_info) == R_TYPE(JMP_SLOT)); 204 205 pltresolve = obj->pltgot + 8; 206 207 if (reloff < 32768) { 208 /* li r11,reloff */ 209 *where++ = 0x39600000 | reloff; 210 } else { 211 /* lis r11,ha(reloff) */ 212 /* addi r11,l(reloff) */ 213 *where++ = 0x3d600000 | ha(reloff); 214 *where++ = 0x396b0000 | l(reloff); 215 } 216 /* b pltresolve */ 217 distance = (Elf_Addr)pltresolve - (Elf_Addr)where; 218 *where++ = 0x48000000 | (distance & 0x03fffffc); 219 /* __syncicache(where - 12, 12); */ 220 } 221 222 return 0; 223 } 224 225 caddr_t 226 _rtld_bind(obj, reloff) 227 const Obj_Entry *obj; 228 Elf_Word reloff; 229 { 230 const Elf_Rela *rela = obj->pltrela + reloff; 231 Elf_Word *where = (Elf_Word *)(obj->relocbase + rela->r_offset); 232 Elf_Addr value; 233 const Elf_Sym *def; 234 const Obj_Entry *defobj; 235 int distance; 236 237 assert(ELF_R_TYPE(rela->r_info) == R_TYPE(JMP_SLOT)); 238 239 def = _rtld_find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, true); 240 if (def == NULL) 241 _rtld_die(); 242 243 value = (Elf_Addr)(defobj->relocbase + def->st_value); 244 distance = value - (Elf_Addr)where; 245 rdbg(("bind now/fixup in %s --> new=%p", 246 defobj->strtab + def->st_name, (void *)value)); 247 248 if (abs(distance) < 32*1024*1024) { /* inside 32MB? */ 249 /* b value # branch directly */ 250 *where = 0x48000000 | (distance & 0x03fffffc); 251 __syncicache(where, 4); 252 } else { 253 Elf_Addr *pltcall, *jmptab; 254 int N = obj->pltrelalim - obj->pltrela; 255 256 /* Entries beyond 8192 take twice as much space. */ 257 if (N > 8192) 258 N += N-8192; 259 260 pltcall = obj->pltgot; 261 jmptab = pltcall + 18 + N * 2; 262 263 jmptab[reloff] = value; 264 265 if (reloff < 32768) { 266 /* li r11,reloff */ 267 *where++ = 0x39600000 | reloff; 268 } else { 269 /* lis r11,ha(reloff) */ 270 /* addi r11,l(reloff) */ 271 *where++ = 0x3d600000 | ha(reloff); 272 *where++ = 0x396b0000 | l(reloff); 273 } 274 /* b pltcall */ 275 distance = (Elf_Addr)pltcall - (Elf_Addr)where; 276 *where++ = 0x48000000 | (distance & 0x03fffffc); 277 __syncicache(where - 12, 12); 278 } 279 280 return (caddr_t)value; 281 } 282