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