1 /* $OpenBSD: rtld_machine.c,v 1.15 2016/06/21 15:25:37 deraadt 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 #include <sys/syscall.h> 49 #include <sys/unistd.h> 50 51 #include <nlist.h> 52 #include <link.h> 53 54 #include "syscall.h" 55 #include "archdep.h" 56 #include "resolve.h" 57 58 Elf_Addr _dl_bind(elf_object_t *object, int reloff); 59 void _dl_md_reloc_gotp_ent(Elf_Addr, Elf_Addr, Elf_Addr); 60 61 int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden; 62 63 int 64 _dl_md_reloc(elf_object_t *object, int rel, int relasz) 65 { 66 int i; 67 int numrela; 68 int fails = 0; 69 struct load_list *llist; 70 Elf32_Addr loff; 71 Elf32_Rela *relas; 72 Elf32_Addr prev_value = 0, prev_ooff = 0; 73 const Elf32_Sym *prev_sym = NULL; 74 75 loff = object->obj_base; 76 numrela = object->Dyn.info[relasz] / sizeof(Elf32_Rela); 77 relas = (Elf32_Rela *)(object->Dyn.info[rel]); 78 79 #ifdef DL_PRINTF_DEBUG 80 _dl_printf("object relocation size %x, numrela %x\n", 81 object->Dyn.info[relasz], numrela); 82 #endif 83 84 if (relas == NULL) 85 return(0); 86 87 /* 88 * Change protection of all write protected segments in the object 89 * so we can do relocations such as DISP26. After relocation, 90 * restore protection. 91 */ 92 if (object->dyn.textrel == 1 && (rel == DT_REL || rel == DT_RELA)) { 93 for (llist = object->load_list; llist != NULL; 94 llist = llist->next) { 95 if (!(llist->prot & PROT_WRITE)) { 96 _dl_mprotect(llist->start, llist->size, 97 PROT_READ | PROT_WRITE); 98 } 99 } 100 } 101 102 for (i = 0; i < numrela; i++, relas++) { 103 Elf32_Addr *r_addr = (Elf32_Addr *)(relas->r_offset + loff); 104 Elf32_Addr ooff, addend, newval; 105 const Elf32_Sym *sym, *this; 106 const char *symn; 107 int type; 108 109 type = ELF32_R_TYPE(relas->r_info); 110 111 if (type == RELOC_GOTP_ENT && rel != DT_JMPREL) 112 continue; 113 114 if (type == RELOC_NONE) 115 continue; 116 117 sym = object->dyn.symtab; 118 sym += ELF32_R_SYM(relas->r_info); 119 symn = object->dyn.strtab + sym->st_name; 120 121 if (type == RELOC_COPY) { 122 /* 123 * we need to find a symbol, that is not in the current 124 * object, start looking at the beginning of the list, 125 * searching all objects but _not_ the current object, 126 * first one found wins. 127 */ 128 const Elf32_Sym *cpysrc = NULL; 129 Elf32_Addr src_loff; 130 int size; 131 132 src_loff = 0; 133 src_loff = _dl_find_symbol(symn, &cpysrc, 134 SYM_SEARCH_OTHER | SYM_WARNNOTFOUND | SYM_NOTPLT, 135 sym, object, NULL); 136 if (cpysrc != NULL) { 137 size = sym->st_size; 138 if (sym->st_size != cpysrc->st_size) { 139 /* _dl_find_symbol() has warned 140 about this already */ 141 size = sym->st_size < cpysrc->st_size ? 142 sym->st_size : cpysrc->st_size; 143 } 144 _dl_bcopy((void *)(src_loff + cpysrc->st_value), 145 r_addr, size); 146 } else 147 fails++; 148 149 continue; 150 } 151 152 if (ELF32_R_SYM(relas->r_info) && 153 !(ELF32_ST_BIND(sym->st_info) == STB_LOCAL && 154 ELF32_ST_TYPE (sym->st_info) == STT_NOTYPE) && 155 sym != prev_sym) { 156 if (ELF32_ST_BIND(sym->st_info) == STB_LOCAL && 157 ELF32_ST_TYPE(sym->st_info) == STT_SECTION) { 158 prev_sym = sym; 159 prev_value = 0; 160 prev_ooff = object->obj_base; 161 } else { 162 this = NULL; 163 ooff = _dl_find_symbol_bysym(object, 164 ELF32_R_SYM(relas->r_info), &this, 165 SYM_SEARCH_ALL | SYM_WARNNOTFOUND | 166 ((type == RELOC_GOTP_ENT) ? 167 SYM_PLT : SYM_NOTPLT), sym, NULL); 168 169 if (this == NULL) { 170 if (ELF_ST_BIND(sym->st_info) != 171 STB_WEAK) 172 fails++; 173 continue; 174 } 175 prev_sym = sym; 176 prev_value = this->st_value; 177 prev_ooff = ooff; 178 } 179 } 180 181 if (type == RELOC_GOTP_ENT) { 182 _dl_md_reloc_gotp_ent((Elf_Addr)r_addr, 183 relas->r_addend + loff, 184 prev_ooff + prev_value); 185 continue; 186 } 187 188 if (ELF32_ST_BIND(sym->st_info) == STB_LOCAL && 189 (ELF32_ST_TYPE(sym->st_info) == STT_SECTION || 190 ELF32_ST_TYPE(sym->st_info) == STT_NOTYPE)) 191 addend = relas->r_addend; 192 else 193 addend = prev_value + relas->r_addend; 194 195 switch (type) { 196 case RELOC_16L: 197 newval = prev_ooff + addend; 198 *(unsigned short *)r_addr = newval & 0xffff; 199 _dl_cacheflush((unsigned long)r_addr, 2); 200 break; 201 case RELOC_16H: 202 newval = prev_ooff + addend; 203 *(unsigned short *)r_addr = newval >> 16; 204 _dl_cacheflush((unsigned long)r_addr, 2); 205 break; 206 case RELOC_DISP26: 207 newval = prev_ooff + addend; 208 newval -= (Elf_Addr)r_addr; 209 if ((newval >> 28) != 0 && (newval >> 28) != 0x0f) { 210 _dl_printf("%s: %s: out of range DISP26" 211 " relocation to '%s' at %x\n", 212 __progname, object->load_name, symn, 213 r_addr); 214 _dl_exit(1); 215 } 216 *r_addr = (*r_addr & 0xfc000000) | 217 (((int32_t)newval >> 2) & 0x03ffffff); 218 _dl_cacheflush((unsigned long)r_addr, 4); 219 break; 220 case RELOC_32: 221 newval = prev_ooff + addend; 222 *r_addr = newval; 223 break; 224 case RELOC_BBASED_32: 225 newval = loff + addend; 226 *r_addr = newval; 227 break; 228 default: 229 _dl_printf("%s:" 230 " %s: unsupported relocation '%s' %d at %x\n", 231 __progname, object->load_name, symn, type, r_addr); 232 _dl_exit(1); 233 } 234 } 235 236 /* reprotect the unprotected segments */ 237 if (object->dyn.textrel == 1 && (rel == DT_REL || rel == DT_RELA)) { 238 for (llist = object->load_list; llist != NULL; 239 llist = llist->next) { 240 if (!(llist->prot & PROT_WRITE)) 241 _dl_mprotect(llist->start, llist->size, 242 llist->prot); 243 } 244 } 245 246 return(fails); 247 } 248 249 /* 250 * GOTP_ENT relocations are special in that they define both a .got and a 251 * .plt relocation. 252 */ 253 void 254 _dl_md_reloc_gotp_ent(Elf_Addr got_addr, Elf_Addr plt_addr, Elf_Addr val) 255 { 256 uint16_t *plt_entry = (uint16_t *)plt_addr; 257 258 /* .got update */ 259 *(Elf_Addr *)got_addr = val; 260 /* .plt update */ 261 plt_entry[1] = got_addr >> 16; 262 plt_entry[3] = got_addr & 0xffff; 263 } 264 265 /* 266 * Relocate the Global Offset Table (GOT). 267 * This is done by calling _dl_md_reloc on DT_JMPREL for DL_BIND_NOW, 268 * otherwise the lazy binding plt operation is preserved. 269 */ 270 int 271 _dl_md_reloc_got(elf_object_t *object, int lazy) 272 { 273 extern void _dl_bind_start(void); /* XXX */ 274 int fails = 0; 275 Elf_Addr *pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT]; 276 Elf_Addr ooff; 277 Elf_Addr plt_start, plt_end; 278 size_t plt_size; 279 const Elf_Sym *this; 280 281 if (pltgot == NULL) 282 return (0); 283 284 pltgot[1] = (Elf_Addr)object; 285 pltgot[2] = (Elf_Addr)_dl_bind_start; 286 287 if (object->Dyn.info[DT_PLTREL] != DT_RELA) 288 return (0); 289 290 if (object->traced) 291 lazy = 1; 292 293 /* 294 * Post-5.3 binaries use dynamic tags to provide the .plt boundaries. 295 * If the tags are missing, fall back to the special symbol search. 296 */ 297 plt_start = object->Dyn.info[DT_88K_PLTSTART - DT_LOPROC + DT_NUM]; 298 plt_end = object->Dyn.info[DT_88K_PLTEND - DT_LOPROC + DT_NUM]; 299 if (plt_start == 0 || plt_end == 0) { 300 this = NULL; 301 ooff = _dl_find_symbol("__plt_start", &this, 302 SYM_SEARCH_OBJ | SYM_NOWARNNOTFOUND | SYM_PLT, NULL, 303 object, NULL); 304 if (this != NULL) 305 plt_start = ooff + this->st_value; 306 else 307 plt_start = 0; 308 309 this = NULL; 310 ooff = _dl_find_symbol("__plt_end", &this, 311 SYM_SEARCH_OBJ | SYM_NOWARNNOTFOUND | SYM_PLT, NULL, 312 object, NULL); 313 if (this != NULL) 314 plt_end = ooff + this->st_value; 315 else 316 plt_start = 0; /* not enough to go on */ 317 } else { 318 plt_start += object->obj_base; 319 plt_end += object->obj_base; 320 } 321 322 if (plt_start == 0) 323 plt_size = 0; 324 else { 325 plt_start = ELF_TRUNC(plt_start, _dl_pagesz); 326 plt_size = ELF_ROUND(plt_end, _dl_pagesz) - plt_start; 327 328 /* 329 * GOT relocation will require PLT to be writeable. 330 */ 331 if (!lazy || object->obj_base != 0) 332 _dl_mprotect((void *)plt_start, plt_size, 333 PROT_READ | PROT_WRITE); 334 } 335 336 if (!lazy) { 337 fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ); 338 } else { 339 if (object->obj_base != 0) { 340 int cnt; 341 Elf_Addr *addr; 342 Elf_RelA *rela; 343 344 cnt = object->Dyn.info[DT_PLTRELSZ] / sizeof(Elf_RelA); 345 rela = (Elf_RelA *)object->Dyn.info[DT_JMPREL]; 346 347 for (; cnt != 0; cnt--, rela++) { 348 addr = (Elf_Addr *)(object->obj_base + 349 rela->r_offset); 350 _dl_md_reloc_gotp_ent((Elf_Addr)addr, 351 object->obj_base + rela->r_addend, 352 *addr + object->obj_base); 353 } 354 } 355 } 356 357 /* mprotect the GOT */ 358 _dl_protect_segment(object, 0, "__got_start", "__got_end", PROT_READ); 359 360 if (plt_size != 0) { 361 if (!lazy || object->obj_base != 0) { 362 /* 363 * Force a cache sync on the whole plt here, 364 * otherwise I$ might have stale information. 365 */ 366 _dl_cacheflush(plt_start, plt_size); 367 _dl_mprotect((void *)plt_start, plt_size, 368 PROT_READ | PROT_EXEC); 369 } 370 } 371 372 return (fails); 373 } 374 375 Elf_Addr 376 _dl_bind(elf_object_t *object, int reloff) 377 { 378 Elf_RelA *rel; 379 Elf_Addr ooff; 380 const Elf_Sym *sym, *this; 381 const char *symn; 382 const elf_object_t *sobj; 383 uint64_t cookie = pcookie; 384 struct { 385 struct __kbind param; 386 Elf_Addr newval; 387 } buf; 388 389 rel = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff); 390 391 sym = object->dyn.symtab; 392 sym += ELF_R_SYM(rel->r_info); 393 symn = object->dyn.strtab + sym->st_name; 394 395 this = NULL; 396 ooff = _dl_find_symbol(symn, &this, 397 SYM_SEARCH_ALL | SYM_WARNNOTFOUND | SYM_PLT, sym, object, &sobj); 398 if (this == NULL) { 399 _dl_printf("lazy binding failed!\n"); 400 *(volatile int *)0 = 0; /* XXX */ 401 } 402 403 buf.newval = ooff + this->st_value; 404 405 if (__predict_false(sobj->traced) && _dl_trace_plt(sobj, symn)) 406 return (buf.newval); 407 408 buf.param.kb_addr = (Elf_Addr *)(object->obj_base + rel->r_offset); 409 buf.param.kb_size = sizeof(Elf_Addr); 410 411 /* directly code the syscall, so that it's actually inline here */ 412 { 413 register long syscall_num __asm("r13") = SYS_kbind; 414 register void *arg1 __asm("r2") = &buf; 415 register long arg2 __asm("r3") = sizeof(buf); 416 register long arg3 __asm("r4") = 0xffffffff & (cookie >> 32); 417 register long arg4 __asm("r5") = 0xffffffff & cookie; 418 419 __asm volatile("tb0 0, %%r0, 450; or %%r0, %%r0, %%r0" 420 : "+r" (arg1), "+r" (arg2) : "r" (syscall_num), 421 "r" (arg3), "r" (arg4) : "cc", "memory"); 422 } 423 424 return (buf.newval); 425 } 426