1 /* $OpenBSD: rtld_machine.c,v 1.52 2014/12/27 13:13:25 kettenis Exp $ */ 2 3 /* 4 * Copyright (c) 1999 Dale Rahn 5 * Copyright (c) 2001 Niklas Hallqvist 6 * Copyright (c) 2001 Artur Grabowski 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 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 18 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 */ 30 31 #define _DYN_LOADER 32 33 #include <sys/types.h> 34 #include <sys/mman.h> 35 #include <sys/exec.h> 36 37 #include <nlist.h> 38 #include <link.h> 39 #include <signal.h> 40 41 #include "syscall.h" 42 #include "archdep.h" 43 #include "resolve.h" 44 45 int 46 _dl_md_reloc(elf_object_t *object, int rel, int relasz) 47 { 48 long i; 49 long numrela; 50 long relrel; 51 int fails = 0; 52 Elf64_Addr loff; 53 Elf64_Addr prev_value = 0; 54 const Elf_Sym *prev_sym = NULL; 55 Elf64_Rela *relas; 56 struct load_list *llist; 57 58 loff = object->obj_base; 59 numrela = object->Dyn.info[relasz] / sizeof(Elf64_Rela); 60 relrel = rel == DT_RELA ? object->relacount : 0; 61 relas = (Elf64_Rela *)(object->Dyn.info[rel]); 62 63 if (relas == NULL) 64 return(0); 65 66 if (relrel > numrela) { 67 _dl_printf("relacount > numrel: %ld > %ld\n", relrel, numrela); 68 _dl_exit(20); 69 } 70 71 /* 72 * unprotect some segments if we need it. 73 * XXX - we unprotect way to much. only the text can have cow 74 * relocations. 75 */ 76 if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) { 77 for (llist = object->load_list; llist != NULL; llist = llist->next) { 78 if (!(llist->prot & PROT_WRITE)) { 79 _dl_mprotect(llist->start, llist->size, 80 llist->prot|PROT_WRITE); 81 } 82 } 83 } 84 85 /* tight loop for leading RELATIVE relocs */ 86 for (i = 0; i < relrel; i++, relas++) { 87 Elf_Addr *r_addr; 88 89 #ifdef DEBUG 90 if (ELF64_R_TYPE(relas->r_info) != R_TYPE(RELATIVE)) { 91 _dl_printf("RELACOUNT wrong\n"); 92 _dl_exit(20); 93 } 94 #endif 95 96 r_addr = (Elf64_Addr *)(relas->r_offset + loff); 97 98 /* Handle unaligned RELATIVE relocs */ 99 if ((((Elf_Addr)r_addr) & 0x7) != 0) { 100 Elf_Addr tmp; 101 _dl_bcopy(r_addr, &tmp, sizeof(Elf_Addr)); 102 tmp += loff; 103 _dl_bcopy(&tmp, r_addr, sizeof(Elf_Addr)); 104 } else 105 *r_addr += loff; 106 } 107 for (; i < numrela; i++, relas++) { 108 Elf64_Addr *r_addr; 109 Elf64_Addr ooff; 110 const Elf64_Sym *sym, *this; 111 const char *symn; 112 113 r_addr = (Elf64_Addr *)(relas->r_offset + loff); 114 115 if (ELF64_R_SYM(relas->r_info) == 0xffffffff) 116 continue; 117 118 119 sym = object->dyn.symtab; 120 sym += ELF64_R_SYM(relas->r_info); 121 symn = object->dyn.strtab + sym->st_name; 122 123 this = NULL; 124 switch (ELF64_R_TYPE(relas->r_info)) { 125 case R_TYPE(REFQUAD): 126 ooff = _dl_find_symbol_bysym(object, 127 ELF64_R_SYM(relas->r_info), &this, 128 SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_NOTPLT, 129 sym, NULL); 130 if (this == NULL) 131 goto resolve_failed; 132 *r_addr += ooff + this->st_value + relas->r_addend; 133 break; 134 case R_TYPE(RELATIVE): 135 /* 136 * There is a lot of unaligned RELATIVE 137 * relocs generated by gcc in the exception handlers. 138 */ 139 if ((((Elf_Addr) r_addr) & 0x7) != 0) { 140 Elf_Addr tmp; 141 #if 0 142 _dl_printf("unaligned RELATIVE: %p type: %d %s 0x%lx -> 0x%lx\n", r_addr, 143 ELF_R_TYPE(relas->r_info), object->load_name, *r_addr, *r_addr+loff); 144 #endif 145 _dl_bcopy(r_addr, &tmp, sizeof(Elf_Addr)); 146 tmp += loff; 147 _dl_bcopy(&tmp, r_addr, sizeof(Elf_Addr)); 148 } else 149 *r_addr += loff; 150 break; 151 case R_TYPE(JMP_SLOT): 152 ooff = _dl_find_symbol(symn, &this, 153 SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, 154 sym, object, NULL); 155 if (this == NULL) 156 goto resolve_failed; 157 *r_addr = ooff + this->st_value + relas->r_addend; 158 break; 159 case R_TYPE(GLOB_DAT): 160 if (sym == prev_sym) { 161 *r_addr = prev_value + relas->r_addend; 162 break; 163 } 164 ooff = _dl_find_symbol_bysym(object, 165 ELF64_R_SYM(relas->r_info), &this, 166 SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_NOTPLT, 167 sym, NULL); 168 if (this == NULL) 169 goto resolve_failed; 170 prev_sym = sym; 171 prev_value = ooff + this->st_value; 172 *r_addr = prev_value + relas->r_addend; 173 break; 174 case R_TYPE(NONE): 175 break; 176 default: 177 _dl_printf("%s:" 178 " %s: unsupported relocation '%s' %d at %lx\n", 179 _dl_progname, object->load_name, symn, 180 ELF64_R_TYPE(relas->r_info), r_addr ); 181 _dl_exit(1); 182 } 183 continue; 184 resolve_failed: 185 if (ELF_ST_BIND(sym->st_info) != STB_WEAK) 186 fails++; 187 } 188 __asm volatile("imb" : : : "memory"); 189 190 /* reprotect the unprotected segments */ 191 if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) { 192 for (llist = object->load_list; llist != NULL; llist = llist->next) { 193 if (!(llist->prot & PROT_WRITE)) 194 _dl_mprotect(llist->start, llist->size, 195 llist->prot); 196 } 197 } 198 return (fails); 199 } 200 201 /* 202 * Resolve a symbol at run-time. 203 */ 204 Elf_Addr 205 _dl_bind(elf_object_t *object, int reloff) 206 { 207 Elf_RelA *rela; 208 Elf_Addr *addr, ooff; 209 const Elf_Sym *sym, *this; 210 const char *symn; 211 const elf_object_t *sobj; 212 sigset_t savedmask; 213 214 rela = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff); 215 216 addr = (Elf_Addr *)(object->obj_base + rela->r_offset); 217 if (object->plt_size != 0 && !(*addr >= object->plt_start && 218 *addr < (object->plt_start + object->plt_size ))) { 219 /* something is broken, relocation has already occurred */ 220 #if 0 221 DL_DEB(("*addr doesn't point into plt %p obj %s\n", 222 *addr, object->load_name)); 223 #endif 224 return *addr; 225 } 226 227 sym = object->dyn.symtab; 228 sym += ELF64_R_SYM(rela->r_info); 229 symn = object->dyn.strtab + sym->st_name; 230 231 this = NULL; 232 ooff = _dl_find_symbol(symn, &this, 233 SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym, object, &sobj); 234 if (this == NULL) { 235 _dl_printf("lazy binding failed!\n"); 236 *(volatile int *)0 = 0; /* XXX */ 237 } 238 239 if (sobj->traced && _dl_trace_plt(sobj, symn)) 240 return ooff + this->st_value + rela->r_addend; 241 242 /* if PLT is protected, allow the write */ 243 if (object->plt_size != 0) { 244 _dl_thread_bind_lock(0, &savedmask); 245 _dl_mprotect(addr, sizeof(Elf_Addr), 246 PROT_READ|PROT_WRITE); 247 } 248 249 *addr = ooff + this->st_value + rela->r_addend; 250 251 /* if PLT is (to be protected, change back to RO/X */ 252 if (object->plt_size != 0) { 253 _dl_mprotect(addr, sizeof(Elf_Addr), 254 PROT_READ); 255 _dl_thread_bind_lock(1, &savedmask); 256 } 257 258 return *addr; 259 } 260 261 /* 262 * Relocate the Global Offset Table (GOT). 263 */ 264 int 265 _dl_md_reloc_got(elf_object_t *object, int lazy) 266 { 267 int fails = 0; 268 Elf_Addr *pltgot; 269 extern void _dl_bind_start(void); /* XXX */ 270 Elf_Addr ooff; 271 Elf_Addr plt_addr; 272 const Elf_Sym *this; 273 274 pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT]; 275 276 object->got_addr = 0; 277 object->got_size = 0; 278 this = NULL; 279 ooff = _dl_find_symbol("__got_start", &this, 280 SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL); 281 if (this != NULL) 282 object->got_addr = ooff + this->st_value; 283 284 this = NULL; 285 ooff = _dl_find_symbol("__got_end", &this, 286 SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL); 287 if (this != NULL) 288 object->got_size = ooff + this->st_value - object->got_addr; 289 290 plt_addr = 0; 291 object->plt_size = 0; 292 this = NULL; 293 ooff = _dl_find_symbol("__plt_start", &this, 294 SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL); 295 if (this != NULL) 296 plt_addr = ooff + this->st_value; 297 298 this = NULL; 299 ooff = _dl_find_symbol("__plt_end", &this, 300 SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL); 301 if (this != NULL) 302 object->plt_size = ooff + this->st_value - plt_addr; 303 304 if (object->got_addr == 0) 305 object->got_start = 0; 306 else { 307 object->got_start = ELF_TRUNC(object->got_addr, _dl_pagesz); 308 object->got_size += object->got_addr - object->got_start; 309 object->got_size = ELF_ROUND(object->got_size, _dl_pagesz); 310 } 311 if (plt_addr == 0) 312 object->plt_start = 0; 313 else { 314 object->plt_start = ELF_TRUNC(plt_addr, _dl_pagesz); 315 object->plt_size += plt_addr - object->plt_start; 316 object->plt_size = ELF_ROUND(object->plt_size, _dl_pagesz); 317 } 318 319 if (object->traced) 320 lazy = 1; 321 322 if (object->obj_type == OBJTYPE_LDR || !lazy || pltgot == NULL) { 323 fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ); 324 } else { 325 if (object->obj_base != 0) { 326 int i, size; 327 Elf_Addr *addr; 328 Elf_RelA *rela; 329 330 size = object->Dyn.info[DT_PLTRELSZ] / 331 sizeof(Elf_RelA); 332 rela = (Elf_RelA *)(object->Dyn.info[DT_JMPREL]); 333 334 for (i = 0; i < size; i++) { 335 addr = (Elf_Addr *)(object->obj_base + 336 rela[i].r_offset); 337 *addr += object->obj_base; 338 } 339 } 340 } 341 if (pltgot != NULL) { 342 pltgot[2] = (Elf_Addr)_dl_bind_start; 343 pltgot[3] = (Elf_Addr)object; 344 } 345 if (object->got_size != 0) 346 _dl_mprotect((void*)object->got_start, object->got_size, 347 PROT_READ); 348 if (object->plt_size != 0) 349 _dl_mprotect((void*)object->plt_start, object->plt_size, 350 PROT_READ|PROT_EXEC); 351 352 return (fails); 353 } 354 355 /* relocate the GOT early */ 356 357 void _reloc_alpha_got(Elf_Dyn *dynp, Elf_Addr relocbase); 358 359 void 360 _reloc_alpha_got(Elf_Dyn *dynp, Elf_Addr relocbase) 361 { 362 const Elf_RelA *rela = 0, *relalim; 363 Elf_Addr relasz = 0; 364 Elf_Addr *where; 365 366 for (; dynp->d_tag != DT_NULL; dynp++) { 367 switch (dynp->d_tag) { 368 case DT_RELA: 369 rela = (const Elf_RelA *)(relocbase + dynp->d_un.d_ptr); 370 break; 371 case DT_RELASZ: 372 relasz = dynp->d_un.d_val; 373 break; 374 } 375 } 376 relalim = (const Elf_RelA *)((caddr_t)rela + relasz); 377 for (; rela < relalim; rela++) { 378 if (ELF64_R_TYPE(rela->r_info) != RELOC_RELATIVE) 379 continue; 380 where = (Elf_Addr *)(relocbase + rela->r_offset); 381 *where += (Elf_Addr)relocbase; 382 } 383 } 384