1 /* $NetBSD: mips_reloc.c,v 1.45 2003/11/19 19:41:57 simonb 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, *sym; 86 Elf_Addr *got; 87 Elf_Word local_gotno, symtabno, gotsym; 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(sym->st_info == 141 ELF_ST_INFO(STB_LOCAL, STT_SECTION)); 142 if (__predict_true(RELOC_ALIGNED_P(where))) 143 *where += (Elf_Addr)(sym->st_value + relocbase); 144 else 145 store_ptr(where, load_ptr(where) + 146 (Elf_Addr)(sym->st_value + relocbase)); 147 break; 148 149 default: 150 abort(); 151 } 152 } 153 } 154 155 int 156 _rtld_relocate_nonplt_objects(const Obj_Entry *obj) 157 { 158 const Elf_Rel *rel; 159 Elf_Addr *got = obj->pltgot; 160 const Elf_Sym *sym, *def; 161 const Obj_Entry *defobj; 162 int i; 163 #ifdef SUPPORT_OLD_BROKEN_LD 164 int broken; 165 #endif 166 167 #ifdef SUPPORT_OLD_BROKEN_LD 168 broken = 0; 169 sym = obj->symtab; 170 for (i = 1; i < 12; i++) 171 if (sym[i].st_info == ELF_ST_INFO(STB_LOCAL, STT_NOTYPE)) 172 broken = 1; 173 dbg(("%s: broken=%d", obj->path, broken)); 174 #endif 175 176 i = (got[1] & 0x80000000) ? 2 : 1; 177 /* Relocate the local GOT entries */ 178 got += i; 179 for (; i < obj->local_gotno; i++) 180 *got++ += (Elf_Addr)obj->relocbase; 181 sym = obj->symtab + obj->gotsym; 182 /* Now do the global GOT entries */ 183 for (i = obj->gotsym; i < obj->symtabno; i++) { 184 rdbg((" doing got %d sym %p (%s, %x)", i - obj->gotsym, sym, 185 sym->st_name + obj->strtab, *got)); 186 187 #ifdef SUPPORT_OLD_BROKEN_LD 188 if (ELF_ST_TYPE(sym->st_info) == STT_FUNC && 189 broken && sym->st_shndx == SHN_UNDEF) { 190 /* 191 * XXX DANGER WILL ROBINSON! 192 * You might think this is stupid, as it intentionally 193 * defeats lazy binding -- and you'd be right. 194 * Unfortunately, for lazy binding to work right, we 195 * need to a way to force the GOT slots used for 196 * function pointers to be resolved immediately. This 197 * is supposed to be done automatically by the linker, 198 * by not outputting a PLT slot and setting st_value 199 * to 0 if there are non-PLT references, but older 200 * versions of GNU ld do not do this. 201 */ 202 def = _rtld_find_symdef(i, obj, &defobj, false); 203 if (def == NULL) 204 return -1; 205 *got = def->st_value + (Elf_Addr)defobj->relocbase; 206 } else 207 #endif 208 if (ELF_ST_TYPE(sym->st_info) == STT_FUNC && 209 sym->st_value != 0 && sym->st_shndx == SHN_UNDEF) { 210 /* 211 * If there are non-PLT references to the function, 212 * st_value should be 0, forcing us to resolve the 213 * address immediately. 214 * 215 * XXX DANGER WILL ROBINSON! 216 * The linker is not outputting PLT slots for calls to 217 * functions that are defined in the same shared 218 * library. This is a bug, because it can screw up 219 * link ordering rules if the symbol is defined in 220 * more than one module. For now, if there is a 221 * definition, we fail the test above and force a full 222 * symbol lookup. This means that all intra-module 223 * calls are bound immediately. - mycroft, 2003/09/24 224 */ 225 *got = sym->st_value + (Elf_Addr)obj->relocbase; 226 } else if (sym->st_info == ELF_ST_INFO(STB_GLOBAL, STT_SECTION)) { 227 /* Symbols with index SHN_ABS are not relocated. */ 228 if (sym->st_shndx != SHN_ABS) 229 *got = sym->st_value + 230 (Elf_Addr)obj->relocbase; 231 } else { 232 def = _rtld_find_symdef(i, obj, &defobj, false); 233 if (def == NULL) 234 return -1; 235 *got = def->st_value + (Elf_Addr)defobj->relocbase; 236 } 237 238 rdbg((" --> now %x", *got)); 239 ++sym; 240 ++got; 241 } 242 243 got = obj->pltgot; 244 for (rel = obj->rel; rel < obj->rellim; rel++) { 245 Elf_Addr *where, tmp; 246 unsigned long symnum; 247 248 where = (Elf_Addr *)(obj->relocbase + rel->r_offset); 249 symnum = ELF_R_SYM(rel->r_info); 250 251 switch (ELF_R_TYPE(rel->r_info)) { 252 case R_TYPE(NONE): 253 break; 254 255 case R_TYPE(REL32): 256 /* 32-bit PC-relative reference */ 257 def = obj->symtab + symnum; 258 259 if (symnum >= obj->gotsym) { 260 if (__predict_true(RELOC_ALIGNED_P(where))) 261 tmp = *where; 262 else 263 tmp = load_ptr(where); 264 tmp += got[obj->local_gotno + symnum - obj->gotsym]; 265 if (__predict_true(RELOC_ALIGNED_P(where))) 266 *where = tmp; 267 else 268 store_ptr(where, tmp); 269 270 rdbg(("REL32/G %s in %s --> %p in %s", 271 obj->strtab + def->st_name, obj->path, 272 (void *)tmp, obj->path)); 273 break; 274 } else { 275 /* 276 * XXX: ABI DIFFERENCE! 277 * 278 * Old NetBSD binutils would generate shared 279 * libs with section-relative relocations being 280 * already adjusted for the start address of 281 * the section. 282 * 283 * New binutils, OTOH, generate shared libs 284 * with the same relocations being based at 285 * zero, so we need to add in the start address 286 * of the section. 287 * 288 * --rkb, Oct 6, 2001 289 */ 290 if (__predict_true(RELOC_ALIGNED_P(where))) 291 tmp = *where; 292 else 293 tmp = load_ptr(where); 294 295 if (def->st_info == 296 ELF_ST_INFO(STB_LOCAL, STT_SECTION) 297 #ifdef SUPPORT_OLD_BROKEN_LD 298 && !broken 299 #endif 300 ) 301 tmp += (Elf_Addr)def->st_value; 302 303 tmp += (Elf_Addr)obj->relocbase; 304 if (__predict_true(RELOC_ALIGNED_P(where))) 305 *where = tmp; 306 else 307 store_ptr(where, tmp); 308 309 rdbg(("REL32/L %s in %s --> %p in %s", 310 obj->strtab + def->st_name, obj->path, 311 (void *)tmp, obj->path)); 312 } 313 break; 314 315 default: 316 rdbg(("sym = %lu, type = %lu, offset = %p, " 317 "contents = %p, symbol = %s", 318 symnum, (u_long)ELF_R_TYPE(rel->r_info), 319 (void *)rel->r_offset, (void *)load_ptr(where), 320 obj->strtab + obj->symtab[symnum].st_name)); 321 _rtld_error("%s: Unsupported relocation type %ld " 322 "in non-PLT relocations\n", 323 obj->path, (u_long) ELF_R_TYPE(rel->r_info)); 324 return -1; 325 } 326 } 327 328 return 0; 329 } 330 331 int 332 _rtld_relocate_plt_lazy(const Obj_Entry *obj) 333 { 334 /* PLT fixups were done above in the GOT relocation. */ 335 return 0; 336 } 337 338 caddr_t 339 _rtld_bind(Elf_Word a0, Elf_Addr a1, Elf_Addr a2, Elf_Addr a3) 340 { 341 Elf_Addr *got = (Elf_Addr *)(a2 - 0x7ff0); 342 const Obj_Entry *obj = (Obj_Entry *)(got[1] & 0x7fffffff); 343 const Elf_Sym *def; 344 const Obj_Entry *defobj; 345 Elf_Addr new_value; 346 347 def = _rtld_find_symdef(a0, obj, &defobj, true); 348 if (def == NULL) 349 _rtld_die(); 350 351 new_value = (Elf_Addr)(defobj->relocbase + def->st_value); 352 rdbg(("bind now/fixup in %s --> new=%p", 353 defobj->strtab + def->st_name, (void *)new_value)); 354 got[obj->local_gotno + a0 - obj->gotsym] = new_value; 355 return ((caddr_t)new_value); 356 } 357