1 /* $NetBSD: mips_reloc.c,v 1.49 2005/12/24 20:59:30 perry Exp $ */ 2 3 /* 4 * Copyright 1997 Michael L. Hitch <mhitch@montana.edu> 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 OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include <sys/cdefs.h> 32 #ifndef lint 33 __RCSID("$NetBSD: mips_reloc.c,v 1.49 2005/12/24 20:59:30 perry Exp $"); 34 #endif /* not lint */ 35 36 #include <sys/types.h> 37 #include <sys/stat.h> 38 39 #include <stdlib.h> 40 #include <string.h> 41 42 #include "debug.h" 43 #include "rtld.h" 44 45 #define SUPPORT_OLD_BROKEN_LD 46 47 void _rtld_bind_start(void); 48 void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr); 49 caddr_t _rtld_bind(Elf_Word, Elf_Addr, Elf_Addr, Elf_Addr); 50 51 /* 52 * It is possible for the compiler to emit relocations for unaligned data. 53 * We handle this situation with these inlines. 54 */ 55 #define RELOC_ALIGNED_P(x) \ 56 (((uintptr_t)(x) & (sizeof(void *) - 1)) == 0) 57 58 static inline Elf_Addr 59 load_ptr(void *where) 60 { 61 Elf_Addr res; 62 63 memcpy(&res, where, sizeof(res)); 64 65 return res; 66 } 67 68 static inline void 69 store_ptr(void *where, Elf_Addr val) 70 { 71 72 memcpy(where, &val, sizeof(val)); 73 } 74 75 76 void 77 _rtld_setup_pltgot(const Obj_Entry *obj) 78 { 79 obj->pltgot[0] = (Elf_Addr) &_rtld_bind_start; 80 /* XXX only if obj->pltgot[1] & 0x80000000 ?? */ 81 obj->pltgot[1] |= (Elf_Addr) obj; 82 } 83 84 void 85 _rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase) 86 { 87 const Elf_Rel *rel = 0, *rellim; 88 Elf_Addr relsz = 0; 89 Elf_Addr *where; 90 const Elf_Sym *symtab = NULL, *sym; 91 Elf_Addr *got = NULL; 92 Elf_Word local_gotno = 0, symtabno = 0, gotsym = 0; 93 int i; 94 95 for (; dynp->d_tag != DT_NULL; dynp++) { 96 switch (dynp->d_tag) { 97 case DT_REL: 98 rel = (const Elf_Rel *)(relocbase + dynp->d_un.d_ptr); 99 break; 100 case DT_RELSZ: 101 relsz = dynp->d_un.d_val; 102 break; 103 case DT_SYMTAB: 104 symtab = (const Elf_Sym *)(relocbase + dynp->d_un.d_ptr); 105 break; 106 case DT_PLTGOT: 107 got = (Elf_Addr *)(relocbase + dynp->d_un.d_ptr); 108 break; 109 case DT_MIPS_LOCAL_GOTNO: 110 local_gotno = dynp->d_un.d_val; 111 break; 112 case DT_MIPS_SYMTABNO: 113 symtabno = dynp->d_un.d_val; 114 break; 115 case DT_MIPS_GOTSYM: 116 gotsym = dynp->d_un.d_val; 117 break; 118 } 119 } 120 121 i = (got[1] & 0x80000000) ? 2 : 1; 122 /* Relocate the local GOT entries */ 123 got += i; 124 for (; i < local_gotno; i++) 125 *got++ += relocbase; 126 sym = symtab + gotsym; 127 /* Now do the global GOT entries */ 128 for (i = gotsym; i < symtabno; i++) { 129 *got = sym->st_value + relocbase; 130 ++sym; 131 ++got; 132 } 133 134 rellim = (const Elf_Rel *)((caddr_t)rel + relsz); 135 for (; rel < rellim; rel++) { 136 where = (Elf_Addr *)(relocbase + rel->r_offset); 137 138 switch (ELF_R_TYPE(rel->r_info)) { 139 case R_TYPE(NONE): 140 break; 141 142 case R_TYPE(REL32): 143 assert(ELF_R_SYM(rel->r_info) < gotsym); 144 sym = symtab + ELF_R_SYM(rel->r_info); 145 assert(ELF_ST_BIND(sym->st_info) == STB_LOCAL); 146 if (__predict_true(RELOC_ALIGNED_P(where))) 147 *where += relocbase; 148 else 149 store_ptr(where, load_ptr(where) + relocbase); 150 break; 151 152 default: 153 abort(); 154 } 155 } 156 } 157 158 int 159 _rtld_relocate_nonplt_objects(const Obj_Entry *obj) 160 { 161 const Elf_Rel *rel; 162 Elf_Addr *got = obj->pltgot; 163 const Elf_Sym *sym, *def; 164 const Obj_Entry *defobj; 165 int i; 166 #ifdef SUPPORT_OLD_BROKEN_LD 167 int broken; 168 #endif 169 170 #ifdef SUPPORT_OLD_BROKEN_LD 171 broken = 0; 172 sym = obj->symtab; 173 for (i = 1; i < 12; i++) 174 if (sym[i].st_info == ELF_ST_INFO(STB_LOCAL, STT_NOTYPE)) 175 broken = 1; 176 dbg(("%s: broken=%d", obj->path, broken)); 177 #endif 178 179 i = (got[1] & 0x80000000) ? 2 : 1; 180 /* Relocate the local GOT entries */ 181 got += i; 182 for (; i < obj->local_gotno; i++) 183 *got++ += (Elf_Addr)obj->relocbase; 184 sym = obj->symtab + obj->gotsym; 185 /* Now do the global GOT entries */ 186 for (i = obj->gotsym; i < obj->symtabno; i++) { 187 rdbg((" doing got %d sym %p (%s, %x)", i - obj->gotsym, sym, 188 sym->st_name + obj->strtab, *got)); 189 190 #ifdef SUPPORT_OLD_BROKEN_LD 191 if (ELF_ST_TYPE(sym->st_info) == STT_FUNC && 192 broken && sym->st_shndx == SHN_UNDEF) { 193 /* 194 * XXX DANGER WILL ROBINSON! 195 * You might think this is stupid, as it intentionally 196 * defeats lazy binding -- and you'd be right. 197 * Unfortunately, for lazy binding to work right, we 198 * need to a way to force the GOT slots used for 199 * function pointers to be resolved immediately. This 200 * is supposed to be done automatically by the linker, 201 * by not outputting a PLT slot and setting st_value 202 * to 0 if there are non-PLT references, but older 203 * versions of GNU ld do not do this. 204 */ 205 def = _rtld_find_symdef(i, obj, &defobj, false); 206 if (def == NULL) 207 return -1; 208 *got = def->st_value + (Elf_Addr)defobj->relocbase; 209 } else 210 #endif 211 if (ELF_ST_TYPE(sym->st_info) == STT_FUNC && 212 sym->st_value != 0 && sym->st_shndx == SHN_UNDEF) { 213 /* 214 * If there are non-PLT references to the function, 215 * st_value should be 0, forcing us to resolve the 216 * address immediately. 217 * 218 * XXX DANGER WILL ROBINSON! 219 * The linker is not outputting PLT slots for calls to 220 * functions that are defined in the same shared 221 * library. This is a bug, because it can screw up 222 * link ordering rules if the symbol is defined in 223 * more than one module. For now, if there is a 224 * definition, we fail the test above and force a full 225 * symbol lookup. This means that all intra-module 226 * calls are bound immediately. - mycroft, 2003/09/24 227 */ 228 *got = sym->st_value + (Elf_Addr)obj->relocbase; 229 } else if (sym->st_info == ELF_ST_INFO(STB_GLOBAL, STT_SECTION)) { 230 /* Symbols with index SHN_ABS are not relocated. */ 231 if (sym->st_shndx != SHN_ABS) 232 *got = sym->st_value + 233 (Elf_Addr)obj->relocbase; 234 } else { 235 def = _rtld_find_symdef(i, obj, &defobj, false); 236 if (def == NULL) 237 return -1; 238 *got = def->st_value + (Elf_Addr)defobj->relocbase; 239 } 240 241 rdbg((" --> now %x", *got)); 242 ++sym; 243 ++got; 244 } 245 246 got = obj->pltgot; 247 for (rel = obj->rel; rel < obj->rellim; rel++) { 248 Elf_Addr *where, tmp; 249 unsigned long symnum; 250 251 where = (Elf_Addr *)(obj->relocbase + rel->r_offset); 252 symnum = ELF_R_SYM(rel->r_info); 253 254 switch (ELF_R_TYPE(rel->r_info)) { 255 case R_TYPE(NONE): 256 break; 257 258 case R_TYPE(REL32): 259 /* 32-bit PC-relative reference */ 260 def = obj->symtab + symnum; 261 262 if (symnum >= obj->gotsym) { 263 if (__predict_true(RELOC_ALIGNED_P(where))) 264 tmp = *where; 265 else 266 tmp = load_ptr(where); 267 tmp += got[obj->local_gotno + symnum - obj->gotsym]; 268 if (__predict_true(RELOC_ALIGNED_P(where))) 269 *where = tmp; 270 else 271 store_ptr(where, tmp); 272 273 rdbg(("REL32/G %s in %s --> %p in %s", 274 obj->strtab + def->st_name, obj->path, 275 (void *)tmp, obj->path)); 276 break; 277 } else { 278 /* 279 * XXX: ABI DIFFERENCE! 280 * 281 * Old NetBSD binutils would generate shared 282 * libs with section-relative relocations being 283 * already adjusted for the start address of 284 * the section. 285 * 286 * New binutils, OTOH, generate shared libs 287 * with the same relocations being based at 288 * zero, so we need to add in the start address 289 * of the section. 290 * 291 * --rkb, Oct 6, 2001 292 */ 293 if (__predict_true(RELOC_ALIGNED_P(where))) 294 tmp = *where; 295 else 296 tmp = load_ptr(where); 297 298 if (def->st_info == 299 ELF_ST_INFO(STB_LOCAL, STT_SECTION) 300 #ifdef SUPPORT_OLD_BROKEN_LD 301 && !broken 302 #endif 303 ) 304 tmp += (Elf_Addr)def->st_value; 305 306 tmp += (Elf_Addr)obj->relocbase; 307 if (__predict_true(RELOC_ALIGNED_P(where))) 308 *where = tmp; 309 else 310 store_ptr(where, tmp); 311 312 rdbg(("REL32/L %s in %s --> %p in %s", 313 obj->strtab + def->st_name, obj->path, 314 (void *)tmp, obj->path)); 315 } 316 break; 317 318 default: 319 rdbg(("sym = %lu, type = %lu, offset = %p, " 320 "contents = %p, symbol = %s", 321 symnum, (u_long)ELF_R_TYPE(rel->r_info), 322 (void *)rel->r_offset, (void *)load_ptr(where), 323 obj->strtab + obj->symtab[symnum].st_name)); 324 _rtld_error("%s: Unsupported relocation type %ld " 325 "in non-PLT relocations\n", 326 obj->path, (u_long) ELF_R_TYPE(rel->r_info)); 327 return -1; 328 } 329 } 330 331 return 0; 332 } 333 334 int 335 _rtld_relocate_plt_lazy(const Obj_Entry *obj) 336 { 337 /* PLT fixups were done above in the GOT relocation. */ 338 return 0; 339 } 340 341 caddr_t 342 _rtld_bind(Elf_Word a0, Elf_Addr a1, Elf_Addr a2, Elf_Addr a3) 343 { 344 Elf_Addr *got = (Elf_Addr *)(a2 - 0x7ff0); 345 const Obj_Entry *obj = (Obj_Entry *)(got[1] & 0x7fffffff); 346 const Elf_Sym *def; 347 const Obj_Entry *defobj; 348 Elf_Addr new_value; 349 350 def = _rtld_find_symdef(a0, obj, &defobj, true); 351 if (def == NULL) 352 _rtld_die(); 353 354 new_value = (Elf_Addr)(defobj->relocbase + def->st_value); 355 rdbg(("bind now/fixup in %s --> new=%p", 356 defobj->strtab + def->st_name, (void *)new_value)); 357 got[obj->local_gotno + a0 - obj->gotsym] = new_value; 358 return ((caddr_t)new_value); 359 } 360