rtld_machine.c revision 1.8
1/* $OpenBSD: rtld_machine.c,v 1.8 2013/06/01 09:57:58 miod Exp $ */ 2 3/* 4 * Copyright (c) 2013 Miodrag Vallat. 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18/* 19 * Copyright (c) 1999 Dale Rahn 20 * 21 * Redistribution and use in source and binary forms, with or without 22 * modification, are permitted provided that the following conditions 23 * are met: 24 * 1. Redistributions of source code must retain the above copyright 25 * notice, this list of conditions and the following disclaimer. 26 * 2. Redistributions in binary form must reproduce the above copyright 27 * notice, this list of conditions and the following disclaimer in the 28 * documentation and/or other materials provided with the distribution. 29 * 30 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 31 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 32 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 34 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 36 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 40 * SUCH DAMAGE. 41 * 42 */ 43 44#define _DYN_LOADER 45 46#include <sys/types.h> 47#include <sys/mman.h> 48 49#include <nlist.h> 50#include <link.h> 51#include <signal.h> 52 53#include "syscall.h" 54#include "archdep.h" 55#include "resolve.h" 56 57Elf_Addr _dl_bind(elf_object_t *object, int reloff); 58void _dl_md_reloc_gotp_ent(Elf_Addr, Elf_Addr, Elf_Addr); 59 60int 61_dl_md_reloc(elf_object_t *object, int rel, int relasz) 62{ 63 int i; 64 int numrela; 65 int fails = 0; 66 struct load_list *llist; 67 Elf32_Addr loff; 68 Elf32_Rela *relas; 69 70 loff = object->obj_base; 71 numrela = object->Dyn.info[relasz] / sizeof(Elf32_Rela); 72 relas = (Elf32_Rela *)(object->Dyn.info[rel]); 73 74#ifdef DL_PRINTF_DEBUG 75 _dl_printf("object relocation size %x, numrela %x\n", 76 object->Dyn.info[relasz], numrela); 77#endif 78 79 if (relas == NULL) 80 return(0); 81 82 /* 83 * Change protection of all write protected segments in the object 84 * so we can do relocations such as DISP26. After relocation, 85 * restore protection. 86 */ 87 if (object->dyn.textrel == 1 && (rel == DT_REL || rel == DT_RELA)) { 88 for (llist = object->load_list; llist != NULL; 89 llist = llist->next) { 90 if (!(llist->prot & PROT_WRITE)) { 91 _dl_mprotect(llist->start, llist->size, 92 llist->prot|PROT_WRITE); 93 } 94 } 95 } 96 97 for (i = 0; i < numrela; i++, relas++) { 98 Elf32_Addr *r_addr = (Elf32_Addr *)(relas->r_offset + loff); 99 Elf32_Addr ooff, addend, newval; 100 const Elf32_Sym *sym, *this; 101 const char *symn; 102 int type; 103 Elf32_Addr prev_value = 0, prev_ooff = 0; 104 const Elf32_Sym *prev_sym = NULL; 105 106 type = ELF32_R_TYPE(relas->r_info); 107 108 if (type == RELOC_GOTP_ENT && rel != DT_JMPREL) 109 continue; 110 111 if (type == RELOC_NONE) 112 continue; 113 114 sym = object->dyn.symtab; 115 sym += ELF32_R_SYM(relas->r_info); 116 symn = object->dyn.strtab + sym->st_name; 117 118 if (type == RELOC_COPY) { 119 /* 120 * we need to find a symbol, that is not in the current 121 * object, start looking at the beginning of the list, 122 * searching all objects but _not_ the current object, 123 * first one found wins. 124 */ 125 const Elf32_Sym *cpysrc = NULL; 126 Elf32_Addr src_loff; 127 int size; 128 129 src_loff = 0; 130 src_loff = _dl_find_symbol(symn, &cpysrc, 131 SYM_SEARCH_OTHER | SYM_WARNNOTFOUND | SYM_NOTPLT, 132 sym, object, NULL); 133 if (cpysrc != NULL) { 134 size = sym->st_size; 135 if (sym->st_size != cpysrc->st_size) { 136 /* _dl_find_symbol() has warned 137 about this already */ 138 size = sym->st_size < cpysrc->st_size ? 139 sym->st_size : cpysrc->st_size; 140 } 141 _dl_bcopy((void *)(src_loff + cpysrc->st_value), 142 r_addr, size); 143 } else 144 fails++; 145 146 continue; 147 } 148 149 if (ELF32_R_SYM(relas->r_info) && 150 !(ELF32_ST_BIND(sym->st_info) == STB_LOCAL && 151 ELF32_ST_TYPE (sym->st_info) == STT_NOTYPE) && 152 sym != prev_sym) { 153 if (ELF32_ST_BIND(sym->st_info) == STB_LOCAL && 154 ELF32_ST_TYPE(sym->st_info) == STT_SECTION) { 155 prev_sym = sym; 156 prev_value = 0; 157 prev_ooff = object->obj_base; 158 } else { 159 this = NULL; 160 ooff = _dl_find_symbol_bysym(object, 161 ELF32_R_SYM(relas->r_info), &this, 162 SYM_SEARCH_ALL | SYM_WARNNOTFOUND | 163 ((type == RELOC_GOTP_ENT) ? 164 SYM_PLT : SYM_NOTPLT), sym, NULL); 165 166 if (this == NULL) { 167 if (ELF_ST_BIND(sym->st_info) != 168 STB_WEAK) 169 fails++; 170 continue; 171 } 172 prev_sym = sym; 173 prev_value = this->st_value; 174 prev_ooff = ooff; 175 } 176 } 177 178 if (type == RELOC_GOTP_ENT) { 179 _dl_md_reloc_gotp_ent((Elf_Addr)r_addr, 180 relas->r_addend + loff, 181 prev_ooff + prev_value); 182 continue; 183 } 184 185 if (ELF32_ST_BIND(sym->st_info) == STB_LOCAL && 186 (ELF32_ST_TYPE(sym->st_info) == STT_SECTION || 187 ELF32_ST_TYPE(sym->st_info) == STT_NOTYPE)) 188 addend = relas->r_addend; 189 else 190 addend = prev_value + relas->r_addend; 191 192 switch (type) { 193 case RELOC_16L: 194 newval = prev_ooff + addend; 195 *(unsigned short *)r_addr = newval & 0xffff; 196 _dl_cacheflush((unsigned long)r_addr, 2); 197 break; 198 case RELOC_16H: 199 newval = prev_ooff + addend; 200 *(unsigned short *)r_addr = newval >> 16; 201 _dl_cacheflush((unsigned long)r_addr, 2); 202 break; 203 case RELOC_DISP26: 204 newval = prev_ooff + addend; 205 newval -= (Elf_Addr)r_addr; 206 if ((newval >> 28) != 0 && (newval >> 28) != 0x0f) { 207 _dl_printf("%s: %s: out of range DISP26" 208 " relocation to '%s' at %x\n", 209 _dl_progname, object->load_name, symn, 210 r_addr); 211 _dl_exit(1); 212 } 213 *r_addr = (*r_addr & 0xfc000000) | 214 (((int32_t)newval >> 2) & 0x03ffffff); 215 _dl_cacheflush((unsigned long)r_addr, 4); 216 break; 217 case RELOC_32: 218 newval = prev_ooff + addend; 219 *r_addr = newval; 220 break; 221 case RELOC_BBASED_32: 222 newval = loff + addend; 223 *r_addr = newval; 224 break; 225 default: 226 _dl_printf("%s:" 227 " %s: unsupported relocation '%s' %d at %x\n", 228 _dl_progname, object->load_name, symn, type, 229 r_addr); 230 _dl_exit(1); 231 } 232 } 233 234 /* reprotect the unprotected segments */ 235 if (object->dyn.textrel == 1 && (rel == DT_REL || rel == DT_RELA)) { 236 for (llist = object->load_list; llist != NULL; 237 llist = llist->next) { 238 if (!(llist->prot & PROT_WRITE)) 239 _dl_mprotect(llist->start, llist->size, 240 llist->prot); 241 } 242 } 243 244 return(fails); 245} 246 247/* 248 * GOTP_ENT relocations are special in that they define both a .got and a 249 * .plt relocation. 250 */ 251void 252_dl_md_reloc_gotp_ent(Elf_Addr got_addr, Elf_Addr plt_addr, Elf_Addr val) 253{ 254 uint16_t *plt_entry = (uint16_t *)plt_addr; 255 256 /* .got update */ 257 *(Elf_Addr *)got_addr = val; 258 /* .plt update */ 259 plt_entry[1] = got_addr >> 16; 260 plt_entry[3] = got_addr & 0xffff; 261} 262 263/* 264 * Relocate the Global Offset Table (GOT). 265 * This is done by calling _dl_md_reloc on DT_JMPREL for DL_BIND_NOW, 266 * otherwise the lazy binding plt operation is preserved. 267 */ 268int 269_dl_md_reloc_got(elf_object_t *object, int lazy) 270{ 271 extern void _dl_bind_start(void); /* XXX */ 272 int fails = 0; 273 Elf_Addr *pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT]; 274 Elf_Addr ooff; 275 Elf_Addr plt_start, plt_end; 276 const Elf_Sym *this; 277 278 if (pltgot == NULL) 279 return (0); 280 281 pltgot[1] = (Elf_Addr)object; 282 pltgot[2] = (Elf_Addr)_dl_bind_start; 283 284 if (object->Dyn.info[DT_PLTREL] != DT_RELA) 285 return (0); 286 287 if (object->traced) 288 lazy = 1; 289 290 object->got_addr = 0; 291 object->got_size = 0; 292 this = NULL; 293 ooff = _dl_find_symbol("__got_start", &this, 294 SYM_SEARCH_OBJ | SYM_NOWARNNOTFOUND | SYM_PLT, NULL, object, NULL); 295 if (this != NULL) 296 object->got_addr = ooff + this->st_value; 297 298 this = NULL; 299 ooff = _dl_find_symbol("__got_end", &this, 300 SYM_SEARCH_OBJ | SYM_NOWARNNOTFOUND | SYM_PLT, NULL, object, NULL); 301 if (this != NULL) 302 object->got_size = ooff + this->st_value - object->got_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 312 /* 313 * Post-5.3 binaries use dynamic tags to provide the .plt boundaries. 314 * If the tags are missing, fall back to the special symbol search. 315 */ 316 plt_start = object->Dyn.info[DT_88K_PLTSTART - DT_LOPROC + DT_NUM]; 317 plt_end = object->Dyn.info[DT_88K_PLTEND - DT_LOPROC + DT_NUM]; 318 if (plt_start == 0 || plt_end == 0) { 319 this = NULL; 320 ooff = _dl_find_symbol("__plt_start", &this, 321 SYM_SEARCH_OBJ | SYM_NOWARNNOTFOUND | SYM_PLT, NULL, 322 object, NULL); 323 if (this != NULL) 324 plt_start = ooff + this->st_value; 325 else 326 plt_start = 0; 327 328 this = NULL; 329 ooff = _dl_find_symbol("__plt_end", &this, 330 SYM_SEARCH_OBJ | SYM_NOWARNNOTFOUND | SYM_PLT, NULL, 331 object, NULL); 332 if (this != NULL) 333 plt_end = ooff + this->st_value; 334 else 335 plt_end = 0; 336 } else { 337 plt_start += object->obj_base; 338 plt_end += object->obj_base; 339 } 340 341 if (plt_start == 0) { 342 object->plt_start = 0; 343 object->plt_size = 0; 344 } else { 345 object->plt_start = ELF_TRUNC(plt_start, _dl_pagesz); 346 object->plt_size = 347 ELF_ROUND(plt_end, _dl_pagesz) - object->plt_start; 348 349 /* 350 * GOT relocation will require PLT to be writeable. 351 */ 352 if (!lazy || object->obj_base != 0) 353 _dl_mprotect((void*)object->plt_start, object->plt_size, 354 PROT_READ | PROT_WRITE); 355 } 356 357 if (!lazy) { 358 fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ); 359 } else { 360 if (object->obj_base != 0) { 361 int cnt; 362 Elf_Addr *addr; 363 Elf_RelA *rela; 364 365 cnt = object->Dyn.info[DT_PLTRELSZ] / sizeof(Elf_RelA); 366 rela = (Elf_RelA *)object->Dyn.info[DT_JMPREL]; 367 368 for (; cnt != 0; cnt--, rela++) { 369 addr = (Elf_Addr *)(object->obj_base + 370 rela->r_offset); 371 _dl_md_reloc_gotp_ent((Elf_Addr)addr, 372 object->obj_base + rela->r_addend, 373 *addr + object->obj_base); 374 } 375 } 376 } 377 378 if (object->got_size != 0) { 379 _dl_mprotect((void*)object->got_start, object->got_size, 380 PROT_READ); 381 } 382 if (object->plt_size != 0) { 383 if (!lazy || object->obj_base != 0) { 384 /* 385 * Force a cache sync on the whole plt here, 386 * otherwise I$ might have stale information. 387 */ 388 _dl_cacheflush(object->plt_start, object->plt_size); 389 _dl_mprotect((void*)object->plt_start, object->plt_size, 390 PROT_READ | PROT_EXEC); 391 } 392 } 393 394 return (fails); 395} 396 397Elf_Addr 398_dl_bind(elf_object_t *object, int reloff) 399{ 400 Elf_RelA *rel; 401 Elf_Addr *r_addr, ooff, value; 402 const Elf_Sym *sym, *this; 403 const char *symn; 404 const elf_object_t *sobj; 405 sigset_t savedmask; 406 407 rel = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff); 408 409 sym = object->dyn.symtab; 410 sym += ELF_R_SYM(rel->r_info); 411 symn = object->dyn.strtab + sym->st_name; 412 413 r_addr = (Elf_Addr *)(object->obj_base + rel->r_offset); 414 this = NULL; 415 ooff = _dl_find_symbol(symn, &this, 416 SYM_SEARCH_ALL | SYM_WARNNOTFOUND | SYM_PLT, sym, object, &sobj); 417 if (this == NULL) { 418 _dl_printf("lazy binding failed!\n"); 419 *((int *)0) = 0; /* XXX */ 420 } 421 422 value = ooff + this->st_value; 423 424 if (sobj->traced && _dl_trace_plt(sobj, symn)) 425 return value; 426 427 /* if GOT is protected, allow the write */ 428 if (object->got_size != 0) { 429 _dl_thread_bind_lock(0, &savedmask); 430 _dl_mprotect((void*)object->got_start, object->got_size, 431 PROT_READ | PROT_WRITE); 432 } 433 434 *r_addr = value; 435 436 /* put the GOT back to RO */ 437 if (object->got_size != 0) { 438 _dl_mprotect((void*)object->got_start, object->got_size, 439 PROT_READ); 440 _dl_thread_bind_lock(1, &savedmask); 441 } 442 443 return (value); 444} 445