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 | |