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