| 1 | /* $NetBSD: headers.c,v 1.65 2018/12/30 11:55:15 martin Exp $ */ |
| 2 | |
| 3 | /* |
| 4 | * Copyright 1996 John D. Polstra. |
| 5 | * Copyright 1996 Matt Thomas <matt@3am-software.com> |
| 6 | * Copyright 2002 Charles M. Hannum <root@ihack.net> |
| 7 | * All rights reserved. |
| 8 | * |
| 9 | * Redistribution and use in source and binary forms, with or without |
| 10 | * modification, are permitted provided that the following conditions |
| 11 | * are met: |
| 12 | * 1. Redistributions of source code must retain the above copyright |
| 13 | * notice, this list of conditions and the following disclaimer. |
| 14 | * 2. Redistributions in binary form must reproduce the above copyright |
| 15 | * notice, this list of conditions and the following disclaimer in the |
| 16 | * documentation and/or other materials provided with the distribution. |
| 17 | * 3. All advertising materials mentioning features or use of this software |
| 18 | * must display the following acknowledgement: |
| 19 | * This product includes software developed by John Polstra. |
| 20 | * 4. The name of the author may not be used to endorse or promote products |
| 21 | * derived from this software without specific prior written permission. |
| 22 | * |
| 23 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| 24 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| 25 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| 26 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| 27 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| 28 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 30 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 31 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| 32 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 33 | */ |
| 34 | |
| 35 | /* |
| 36 | * Dynamic linker for ELF. |
| 37 | * |
| 38 | * John Polstra <jdp@polstra.com>. |
| 39 | */ |
| 40 | |
| 41 | #include <sys/cdefs.h> |
| 42 | #ifndef lint |
| 43 | __RCSID("$NetBSD: headers.c,v 1.65 2018/12/30 11:55:15 martin Exp $" ); |
| 44 | #endif /* not lint */ |
| 45 | |
| 46 | #include <err.h> |
| 47 | #include <errno.h> |
| 48 | #include <fcntl.h> |
| 49 | #include <stdarg.h> |
| 50 | #include <stdio.h> |
| 51 | #include <stdlib.h> |
| 52 | #include <string.h> |
| 53 | #include <unistd.h> |
| 54 | #include <sys/types.h> |
| 55 | #include <sys/mman.h> |
| 56 | #include <sys/bitops.h> |
| 57 | #include <dirent.h> |
| 58 | |
| 59 | #include "debug.h" |
| 60 | #include "rtld.h" |
| 61 | |
| 62 | /* |
| 63 | * Process a shared object's DYNAMIC section, and save the important |
| 64 | * information in its Obj_Entry structure. |
| 65 | */ |
| 66 | void |
| 67 | _rtld_digest_dynamic(const char *execname, Obj_Entry *obj) |
| 68 | { |
| 69 | Elf_Dyn *dynp; |
| 70 | Needed_Entry **needed_tail = &obj->needed; |
| 71 | const Elf_Dyn *dyn_soname = NULL; |
| 72 | const Elf_Dyn *dyn_rpath = NULL; |
| 73 | bool use_pltrel = false; |
| 74 | bool use_pltrela = false; |
| 75 | Elf_Addr relsz = 0, relasz = 0; |
| 76 | Elf_Addr pltrel = 0, pltrelsz = 0; |
| 77 | #ifdef RTLD_LOADER |
| 78 | Elf_Addr init = 0, fini = 0; |
| 79 | #endif |
| 80 | |
| 81 | dbg(("headers: digesting PT_DYNAMIC at %p" , obj->dynamic)); |
| 82 | for (dynp = obj->dynamic; dynp->d_tag != DT_NULL; ++dynp) { |
| 83 | dbg((" d_tag %ld at %p" , (long)dynp->d_tag, dynp)); |
| 84 | switch (dynp->d_tag) { |
| 85 | |
| 86 | case DT_REL: |
| 87 | obj->rel = (const Elf_Rel *) |
| 88 | (obj->relocbase + dynp->d_un.d_ptr); |
| 89 | break; |
| 90 | |
| 91 | case DT_RELSZ: |
| 92 | relsz = dynp->d_un.d_val; |
| 93 | break; |
| 94 | |
| 95 | case DT_RELENT: |
| 96 | assert(dynp->d_un.d_val == sizeof(Elf_Rel)); |
| 97 | break; |
| 98 | |
| 99 | case DT_JMPREL: |
| 100 | pltrel = dynp->d_un.d_ptr; |
| 101 | break; |
| 102 | |
| 103 | case DT_PLTRELSZ: |
| 104 | pltrelsz = dynp->d_un.d_val; |
| 105 | break; |
| 106 | |
| 107 | case DT_RELA: |
| 108 | obj->rela = (const Elf_Rela *) |
| 109 | (obj->relocbase + dynp->d_un.d_ptr); |
| 110 | break; |
| 111 | |
| 112 | case DT_RELASZ: |
| 113 | relasz = dynp->d_un.d_val; |
| 114 | break; |
| 115 | |
| 116 | case DT_RELAENT: |
| 117 | assert(dynp->d_un.d_val == sizeof(Elf_Rela)); |
| 118 | break; |
| 119 | |
| 120 | case DT_PLTREL: |
| 121 | use_pltrel = dynp->d_un.d_val == DT_REL; |
| 122 | use_pltrela = dynp->d_un.d_val == DT_RELA; |
| 123 | assert(use_pltrel || use_pltrela); |
| 124 | break; |
| 125 | |
| 126 | case DT_SYMTAB: |
| 127 | obj->symtab = (const Elf_Sym *) |
| 128 | (obj->relocbase + dynp->d_un.d_ptr); |
| 129 | break; |
| 130 | |
| 131 | case DT_SYMENT: |
| 132 | assert(dynp->d_un.d_val == sizeof(Elf_Sym)); |
| 133 | break; |
| 134 | |
| 135 | case DT_STRTAB: |
| 136 | obj->strtab = (const char *) |
| 137 | (obj->relocbase + dynp->d_un.d_ptr); |
| 138 | break; |
| 139 | |
| 140 | case DT_STRSZ: |
| 141 | obj->strsize = dynp->d_un.d_val; |
| 142 | break; |
| 143 | |
| 144 | case DT_VERNEED: |
| 145 | obj->verneed = (const Elf_Verneed *) |
| 146 | (obj->relocbase + dynp->d_un.d_ptr); |
| 147 | break; |
| 148 | |
| 149 | case DT_VERNEEDNUM: |
| 150 | obj->verneednum = dynp->d_un.d_val; |
| 151 | break; |
| 152 | |
| 153 | case DT_VERDEF: |
| 154 | obj->verdef = (const Elf_Verdef *) |
| 155 | (obj->relocbase + dynp->d_un.d_ptr); |
| 156 | break; |
| 157 | |
| 158 | case DT_VERDEFNUM: |
| 159 | obj->verdefnum = dynp->d_un.d_val; |
| 160 | break; |
| 161 | |
| 162 | case DT_VERSYM: |
| 163 | obj->versyms = (const Elf_Versym *) |
| 164 | (obj->relocbase + dynp->d_un.d_ptr); |
| 165 | break; |
| 166 | |
| 167 | case DT_HASH: |
| 168 | { |
| 169 | const Elf_Symindx *hashtab = (const Elf_Symindx *) |
| 170 | (obj->relocbase + dynp->d_un.d_ptr); |
| 171 | |
| 172 | if (hashtab[0] > UINT32_MAX) |
| 173 | obj->nbuckets = UINT32_MAX; |
| 174 | else |
| 175 | obj->nbuckets = hashtab[0]; |
| 176 | obj->nchains = hashtab[1]; |
| 177 | obj->buckets = hashtab + 2; |
| 178 | obj->chains = obj->buckets + obj->nbuckets; |
| 179 | /* |
| 180 | * Should really be in _rtld_relocate_objects, |
| 181 | * but _rtld_symlook_obj might be used before. |
| 182 | */ |
| 183 | if (obj->nbuckets) { |
| 184 | fast_divide32_prepare(obj->nbuckets, |
| 185 | &obj->nbuckets_m, |
| 186 | &obj->nbuckets_s1, |
| 187 | &obj->nbuckets_s2); |
| 188 | } |
| 189 | } |
| 190 | break; |
| 191 | |
| 192 | case DT_NEEDED: |
| 193 | { |
| 194 | Needed_Entry *nep = NEW(Needed_Entry); |
| 195 | |
| 196 | nep->name = dynp->d_un.d_val; |
| 197 | nep->obj = NULL; |
| 198 | nep->next = NULL; |
| 199 | |
| 200 | *needed_tail = nep; |
| 201 | needed_tail = &nep->next; |
| 202 | } |
| 203 | break; |
| 204 | |
| 205 | case DT_PLTGOT: |
| 206 | obj->pltgot = (Elf_Addr *) |
| 207 | (obj->relocbase + dynp->d_un.d_ptr); |
| 208 | break; |
| 209 | |
| 210 | case DT_TEXTREL: |
| 211 | obj->textrel = true; |
| 212 | break; |
| 213 | |
| 214 | case DT_SYMBOLIC: |
| 215 | obj->symbolic = true; |
| 216 | break; |
| 217 | |
| 218 | case DT_RPATH: |
| 219 | case DT_RUNPATH: |
| 220 | /* |
| 221 | * We have to wait until later to process this, because |
| 222 | * we might not have gotten the address of the string |
| 223 | * table yet. |
| 224 | */ |
| 225 | dyn_rpath = dynp; |
| 226 | break; |
| 227 | |
| 228 | case DT_SONAME: |
| 229 | dyn_soname = dynp; |
| 230 | break; |
| 231 | |
| 232 | case DT_INIT: |
| 233 | #ifdef RTLD_LOADER |
| 234 | init = dynp->d_un.d_ptr; |
| 235 | #endif |
| 236 | break; |
| 237 | |
| 238 | #ifdef HAVE_INITFINI_ARRAY |
| 239 | case DT_INIT_ARRAY: |
| 240 | obj->init_array = |
| 241 | (Elf_Addr *)(obj->relocbase + dynp->d_un.d_ptr); |
| 242 | dbg(("headers: DT_INIT_ARRAY at %p" , |
| 243 | obj->init_array)); |
| 244 | break; |
| 245 | |
| 246 | case DT_INIT_ARRAYSZ: |
| 247 | obj->init_arraysz = dynp->d_un.d_val / sizeof(fptr_t); |
| 248 | dbg(("headers: DT_INIT_ARRAYZ %zu" , |
| 249 | obj->init_arraysz)); |
| 250 | break; |
| 251 | #endif |
| 252 | |
| 253 | case DT_FINI: |
| 254 | #ifdef RTLD_LOADER |
| 255 | fini = dynp->d_un.d_ptr; |
| 256 | #endif |
| 257 | break; |
| 258 | |
| 259 | #ifdef HAVE_INITFINI_ARRAY |
| 260 | case DT_FINI_ARRAY: |
| 261 | obj->fini_array = |
| 262 | (Elf_Addr *)(obj->relocbase + dynp->d_un.d_ptr); |
| 263 | dbg(("headers: DT_FINI_ARRAY at %p" , |
| 264 | obj->fini_array)); |
| 265 | break; |
| 266 | |
| 267 | case DT_FINI_ARRAYSZ: |
| 268 | obj->fini_arraysz = dynp->d_un.d_val / sizeof(fptr_t); |
| 269 | dbg(("headers: DT_FINI_ARRAYZ %zu" , |
| 270 | obj->fini_arraysz)); |
| 271 | break; |
| 272 | #endif |
| 273 | |
| 274 | /* |
| 275 | * Don't process DT_DEBUG on MIPS as the dynamic section |
| 276 | * is mapped read-only. DT_MIPS_RLD_MAP is used instead. |
| 277 | * XXX: n32/n64 may use DT_DEBUG, not sure yet. |
| 278 | */ |
| 279 | #ifndef __mips__ |
| 280 | case DT_DEBUG: |
| 281 | #ifdef RTLD_LOADER |
| 282 | dynp->d_un.d_ptr = (Elf_Addr)&_rtld_debug; |
| 283 | #endif |
| 284 | break; |
| 285 | #endif |
| 286 | |
| 287 | #ifdef __mips__ |
| 288 | case DT_MIPS_LOCAL_GOTNO: |
| 289 | obj->local_gotno = dynp->d_un.d_val; |
| 290 | break; |
| 291 | |
| 292 | case DT_MIPS_SYMTABNO: |
| 293 | obj->symtabno = dynp->d_un.d_val; |
| 294 | break; |
| 295 | |
| 296 | case DT_MIPS_GOTSYM: |
| 297 | obj->gotsym = dynp->d_un.d_val; |
| 298 | break; |
| 299 | |
| 300 | case DT_MIPS_RLD_MAP: |
| 301 | #ifdef RTLD_LOADER |
| 302 | *((Elf_Addr *)(dynp->d_un.d_ptr)) = (Elf_Addr) |
| 303 | &_rtld_debug; |
| 304 | #endif |
| 305 | break; |
| 306 | #endif |
| 307 | #ifdef __powerpc__ |
| 308 | #ifdef _LP64 |
| 309 | case DT_PPC64_GLINK: |
| 310 | obj->glink = (Elf_Addr)(uintptr_t)obj->relocbase + dynp->d_un.d_ptr; |
| 311 | break; |
| 312 | #else |
| 313 | case DT_PPC_GOT: |
| 314 | obj->gotptr = (Elf_Addr *)(obj->relocbase + dynp->d_un.d_ptr); |
| 315 | break; |
| 316 | #endif |
| 317 | #endif |
| 318 | case DT_FLAGS_1: |
| 319 | obj->z_now = |
| 320 | ((dynp->d_un.d_val & DF_1_NOW) != 0); |
| 321 | obj->z_nodelete = |
| 322 | ((dynp->d_un.d_val & DF_1_NODELETE) != 0); |
| 323 | obj->z_initfirst = |
| 324 | ((dynp->d_un.d_val & DF_1_INITFIRST) != 0); |
| 325 | obj->z_noopen = |
| 326 | ((dynp->d_un.d_val & DF_1_NOOPEN) != 0); |
| 327 | break; |
| 328 | } |
| 329 | } |
| 330 | |
| 331 | obj->rellim = (const Elf_Rel *)((const uint8_t *)obj->rel + relsz); |
| 332 | obj->relalim = (const Elf_Rela *)((const uint8_t *)obj->rela + relasz); |
| 333 | if (use_pltrel) { |
| 334 | obj->pltrel = (const Elf_Rel *)(obj->relocbase + pltrel); |
| 335 | obj->pltrellim = (const Elf_Rel *)(obj->relocbase + pltrel + pltrelsz); |
| 336 | obj->pltrelalim = 0; |
| 337 | /* On PPC and SPARC, at least, REL(A)SZ may include JMPREL. |
| 338 | Trim rel(a)lim to save time later. */ |
| 339 | if (obj->rellim && obj->pltrel && |
| 340 | obj->rellim > obj->pltrel && |
| 341 | obj->rellim <= obj->pltrellim) |
| 342 | obj->rellim = obj->pltrel; |
| 343 | } else if (use_pltrela) { |
| 344 | obj->pltrela = (const Elf_Rela *)(obj->relocbase + pltrel); |
| 345 | obj->pltrellim = 0; |
| 346 | obj->pltrelalim = (const Elf_Rela *)(obj->relocbase + pltrel + pltrelsz); |
| 347 | /* On PPC and SPARC, at least, REL(A)SZ may include JMPREL. |
| 348 | Trim rel(a)lim to save time later. */ |
| 349 | if (obj->relalim && obj->pltrela && |
| 350 | obj->relalim > obj->pltrela && |
| 351 | obj->relalim <= obj->pltrelalim) |
| 352 | obj->relalim = obj->pltrela; |
| 353 | } |
| 354 | |
| 355 | #ifdef RTLD_LOADER |
| 356 | if (init != 0) |
| 357 | obj->init = (Elf_Addr) obj->relocbase + init; |
| 358 | if (fini != 0) |
| 359 | obj->fini = (Elf_Addr) obj->relocbase + fini; |
| 360 | #endif |
| 361 | |
| 362 | if (dyn_rpath != NULL) { |
| 363 | _rtld_add_paths(execname, &obj->rpaths, obj->strtab + |
| 364 | dyn_rpath->d_un.d_val); |
| 365 | } |
| 366 | if (dyn_soname != NULL) { |
| 367 | _rtld_object_add_name(obj, obj->strtab + |
| 368 | dyn_soname->d_un.d_val); |
| 369 | } |
| 370 | } |
| 371 | |
| 372 | /* |
| 373 | * Process a shared object's program header. This is used only for the |
| 374 | * main program, when the kernel has already loaded the main program |
| 375 | * into memory before calling the dynamic linker. It creates and |
| 376 | * returns an Obj_Entry structure. |
| 377 | */ |
| 378 | Obj_Entry * |
| 379 | _rtld_digest_phdr(const Elf_Phdr *phdr, int phnum, caddr_t entry) |
| 380 | { |
| 381 | Obj_Entry *obj; |
| 382 | const Elf_Phdr *phlimit = phdr + phnum; |
| 383 | const Elf_Phdr *ph; |
| 384 | bool first_seg = true; |
| 385 | Elf_Addr vaddr; |
| 386 | size_t size; |
| 387 | |
| 388 | obj = _rtld_obj_new(); |
| 389 | |
| 390 | for (ph = phdr; ph < phlimit; ++ph) { |
| 391 | if (ph->p_type != PT_PHDR) |
| 392 | continue; |
| 393 | |
| 394 | obj->relocbase = (caddr_t)((uintptr_t)phdr - (uintptr_t)ph->p_vaddr); |
| 395 | obj->phdr = phdr; /* Equivalent to relocbase + p_vaddr. */ |
| 396 | obj->phsize = ph->p_memsz; |
| 397 | dbg(("headers: phdr %p (%p) phsize %zu relocbase %p" , |
| 398 | obj->phdr, phdr, obj->phsize, obj->relocbase)); |
| 399 | break; |
| 400 | } |
| 401 | |
| 402 | for (ph = phdr; ph < phlimit; ++ph) { |
| 403 | vaddr = (Elf_Addr)(uintptr_t)(obj->relocbase + ph->p_vaddr); |
| 404 | switch (ph->p_type) { |
| 405 | |
| 406 | case PT_INTERP: |
| 407 | obj->interp = (const char *)(uintptr_t)vaddr; |
| 408 | dbg(("headers: %s %p phsize %" PRImemsz, |
| 409 | "PT_INTERP" , (void *)(uintptr_t)vaddr, |
| 410 | ph->p_memsz)); |
| 411 | break; |
| 412 | |
| 413 | case PT_LOAD: |
| 414 | size = round_up(vaddr + ph->p_memsz) - obj->vaddrbase; |
| 415 | if (first_seg) { /* First load segment */ |
| 416 | obj->vaddrbase = round_down(vaddr); |
| 417 | obj->mapbase = (caddr_t)(uintptr_t)obj->vaddrbase; |
| 418 | obj->textsize = size; |
| 419 | obj->mapsize = size; |
| 420 | first_seg = false; |
| 421 | } else { /* Last load segment */ |
| 422 | obj->mapsize = MAX(obj->mapsize, size); |
| 423 | } |
| 424 | dbg(("headers: %s %p phsize %" PRImemsz, |
| 425 | "PT_LOAD" , (void *)(uintptr_t)vaddr, |
| 426 | ph->p_memsz)); |
| 427 | break; |
| 428 | |
| 429 | case PT_DYNAMIC: |
| 430 | obj->dynamic = (Elf_Dyn *)(uintptr_t)vaddr; |
| 431 | dbg(("headers: %s %p phsize %" PRImemsz, |
| 432 | "PT_DYNAMIC" , (void *)(uintptr_t)vaddr, |
| 433 | ph->p_memsz)); |
| 434 | break; |
| 435 | |
| 436 | #ifdef GNU_RELRO |
| 437 | case PT_GNU_RELRO: |
| 438 | obj->relro_page = obj->relocbase |
| 439 | + round_down(ph->p_vaddr); |
| 440 | obj->relro_size = round_up(ph->p_memsz); |
| 441 | dbg(("headers: %s %p phsize %" PRImemsz, |
| 442 | "PT_GNU_RELRO" , (void *)(uintptr_t)vaddr, |
| 443 | ph->p_memsz)); |
| 444 | break; |
| 445 | #endif |
| 446 | |
| 447 | #if defined(__HAVE_TLS_VARIANT_I) || defined(__HAVE_TLS_VARIANT_II) |
| 448 | case PT_TLS: |
| 449 | obj->tlsindex = 1; |
| 450 | obj->tlssize = ph->p_memsz; |
| 451 | obj->tlsalign = ph->p_align; |
| 452 | obj->tlsinitsize = ph->p_filesz; |
| 453 | obj->tlsinit = (void *)(obj->relocbase + |
| 454 | (uintptr_t)ph->p_vaddr); |
| 455 | dbg(("headers: %s %p phsize %" PRImemsz, |
| 456 | "PT_TLS" , (void *)(uintptr_t)vaddr, |
| 457 | ph->p_memsz)); |
| 458 | break; |
| 459 | #endif |
| 460 | #ifdef __ARM_EABI__ |
| 461 | case PT_ARM_EXIDX: |
| 462 | obj->exidx_start = (void *)(uintptr_t)vaddr; |
| 463 | obj->exidx_sz = ph->p_memsz; |
| 464 | dbg(("headers: %s %p phsize %" PRImemsz, |
| 465 | "PT_ARM_EXIDX" , (void *)(uintptr_t)vaddr, |
| 466 | ph->p_memsz)); |
| 467 | break; |
| 468 | #endif |
| 469 | } |
| 470 | } |
| 471 | |
| 472 | obj->entry = entry; |
| 473 | return obj; |
| 474 | } |
| 475 | |