Annotation of src/libexec/ld.elf_so/map_object.c, Revision 1.41
1.41 ! skrll 1: /* $NetBSD: map_object.c,v 1.40 2010/09/11 11:11:52 skrll Exp $ */
1.1 cgd 2:
3: /*
4: * Copyright 1996 John D. Polstra.
5: * Copyright 1996 Matt Thomas <matt@3am-software.com>
1.24 mycroft 6: * Copyright 2002 Charles M. Hannum <root@ihack.net>
1.1 cgd 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:
1.31 skrll 35: #include <sys/cdefs.h>
36: #ifndef lint
1.41 ! skrll 37: __RCSID("$NetBSD: map_object.c,v 1.40 2010/09/11 11:11:52 skrll Exp $");
1.31 skrll 38: #endif /* not lint */
39:
1.1 cgd 40: #include <errno.h>
41: #include <stddef.h>
1.10 mycroft 42: #include <stdlib.h>
1.1 cgd 43: #include <string.h>
44: #include <unistd.h>
1.10 mycroft 45: #include <sys/stat.h>
1.1 cgd 46: #include <sys/types.h>
47: #include <sys/mman.h>
48:
1.41 ! skrll 49: #include "debug.h"
1.1 cgd 50: #include "rtld.h"
1.7 hannken 51:
1.30 skrll 52: static int protflags(int); /* Elf flags -> mmap protection */
1.1 cgd 53:
1.41 ! skrll 54: #define EA_UNDEF (~(Elf_Addr)0)
! 55:
1.1 cgd 56: /*
57: * Map a shared object into memory. The argument is a file descriptor,
58: * which must be open on the object and positioned at its beginning.
59: *
60: * The return value is a pointer to a newly-allocated Obj_Entry structure
61: * for the shared object. Returns NULL on failure.
62: */
63: Obj_Entry *
1.34 christos 64: _rtld_map_object(const char *path, int fd, const struct stat *sb)
1.1 cgd 65: {
1.18 junyoung 66: Obj_Entry *obj;
67: Elf_Ehdr *ehdr;
68: Elf_Phdr *phdr;
1.41 ! skrll 69: size_t phsize;
1.18 junyoung 70: Elf_Phdr *phlimit;
71: Elf_Phdr *segs[2];
72: int nsegs;
1.22 mycroft 73: caddr_t mapbase = MAP_FAILED;
1.32 lukem 74: size_t mapsize = 0;
1.27 matt 75: int mapflags;
1.18 junyoung 76: Elf_Off base_offset;
1.28 taca 77: #ifdef MAP_ALIGNED
1.27 matt 78: Elf_Addr base_alignment;
1.28 taca 79: #endif
1.18 junyoung 80: Elf_Addr base_vaddr;
81: Elf_Addr base_vlimit;
82: Elf_Addr text_vlimit;
1.22 mycroft 83: int text_flags;
1.18 junyoung 84: caddr_t base_addr;
85: Elf_Off data_offset;
86: Elf_Addr data_vaddr;
87: Elf_Addr data_vlimit;
1.22 mycroft 88: int data_flags;
1.18 junyoung 89: caddr_t data_addr;
1.41 ! skrll 90: Elf_Addr phdr_vaddr;
! 91: size_t phdr_memsz;
1.18 junyoung 92: caddr_t gap_addr;
93: size_t gap_size;
1.41 ! skrll 94: int i;
1.1 cgd 95: #ifdef RTLD_LOADER
1.18 junyoung 96: Elf_Addr clear_vaddr;
97: caddr_t clear_addr;
98: size_t nclear;
1.1 cgd 99: #endif
1.26 fvdl 100:
1.38 christos 101: if (sb != NULL && sb->st_size < (off_t)sizeof (Elf_Ehdr)) {
1.37 mrg 102: _rtld_error("%s: unrecognized file format1", path);
1.26 fvdl 103: return NULL;
104: }
1.1 cgd 105:
1.22 mycroft 106: obj = _rtld_obj_new();
1.34 christos 107: obj->path = xstrdup(path);
1.25 junyoung 108: obj->pathlen = strlen(path);
1.22 mycroft 109: if (sb != NULL) {
110: obj->dev = sb->st_dev;
111: obj->ino = sb->st_ino;
112: }
113:
1.20 mycroft 114: ehdr = mmap(NULL, _rtld_pagesz, PROT_READ, MAP_FILE | MAP_SHARED, fd,
1.16 mycroft 115: (off_t)0);
1.36 ad 116: obj->ehdr = ehdr;
1.20 mycroft 117: if (ehdr == MAP_FAILED) {
1.4 christos 118: _rtld_error("%s: read error: %s", path, xstrerror(errno));
1.22 mycroft 119: goto bad;
1.4 christos 120: }
121: /* Make sure the file is valid */
1.17 junyoung 122: if (memcmp(ELFMAG, ehdr->e_ident, SELFMAG) != 0 ||
123: ehdr->e_ident[EI_CLASS] != ELFCLASS) {
1.40 skrll 124: _rtld_error("%s: unrecognized file format2 [%x != %x]", path,
125: ehdr->e_ident[EI_CLASS], ELFCLASS);
1.16 mycroft 126: goto bad;
1.4 christos 127: }
128: /* Elf_e_ident includes class */
1.17 junyoung 129: if (ehdr->e_ident[EI_VERSION] != EV_CURRENT ||
130: ehdr->e_version != EV_CURRENT ||
131: ehdr->e_ident[EI_DATA] != ELFDEFNNAME(MACHDEP_ENDIANNESS)) {
1.16 mycroft 132: _rtld_error("%s: unsupported file version", path);
133: goto bad;
134: }
1.17 junyoung 135: if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) {
1.16 mycroft 136: _rtld_error("%s: unsupported file type", path);
137: goto bad;
1.4 christos 138: }
1.17 junyoung 139: switch (ehdr->e_machine) {
1.4 christos 140: ELFDEFNNAME(MACHDEP_ID_CASES)
141: default:
1.16 mycroft 142: _rtld_error("%s: unsupported machine", path);
143: goto bad;
1.4 christos 144: }
145:
146: /*
147: * We rely on the program header being in the first page. This is
148: * not strictly required by the ABI specification, but it seems to
149: * always true in practice. And, it simplifies things considerably.
150: */
1.17 junyoung 151: assert(ehdr->e_phentsize == sizeof(Elf_Phdr));
1.19 junyoung 152: assert(ehdr->e_phoff + ehdr->e_phnum * sizeof(Elf_Phdr) <=
153: _rtld_pagesz);
1.4 christos 154:
155: /*
156: * Scan the program header entries, and save key information.
157: *
158: * We rely on there being exactly two load segments, text and data,
159: * in that order.
160: */
1.17 junyoung 161: phdr = (Elf_Phdr *) ((caddr_t)ehdr + ehdr->e_phoff);
1.41 ! skrll 162: phsize = ehdr->e_phnum * sizeof(phdr[0]);
! 163: obj->phdr = NULL;
! 164: phdr_vaddr = EA_UNDEF;
! 165: phdr_memsz = 0;
1.17 junyoung 166: phlimit = phdr + ehdr->e_phnum;
1.4 christos 167: nsegs = 0;
168: while (phdr < phlimit) {
169: switch (phdr->p_type) {
1.10 mycroft 170: case PT_INTERP:
1.37 mrg 171: obj->interp = (void *)(uintptr_t)phdr->p_vaddr;
1.41 ! skrll 172: dbg(("%s: PT_INTERP %p", obj->path, obj->interp));
1.10 mycroft 173: break;
1.1 cgd 174:
1.8 kleink 175: case PT_LOAD:
1.12 mycroft 176: if (nsegs < 2)
177: segs[nsegs] = phdr;
1.4 christos 178: ++nsegs;
1.41 ! skrll 179: dbg(("%s: PT_LOAD %p", obj->path, phdr));
1.4 christos 180: break;
181:
1.41 ! skrll 182: case PT_PHDR:
! 183: phdr_vaddr = phdr->p_vaddr;
! 184: phdr_memsz = phdr->p_memsz;
! 185: dbg(("%s: PT_PHDR %p phsize %zu", obj->path,
! 186: (void *)(uintptr_t)phdr_vaddr, phdr_memsz));
! 187: break;
! 188:
1.8 kleink 189: case PT_DYNAMIC:
1.37 mrg 190: obj->dynamic = (void *)(uintptr_t)phdr->p_vaddr;
1.41 ! skrll 191: dbg(("%s: PT_DYNAMIC %p", obj->path, obj->dynamic));
1.4 christos 192: break;
193: }
1.1 cgd 194:
1.4 christos 195: ++phdr;
196: }
1.41 ! skrll 197: phdr = (Elf_Phdr *) ((caddr_t)ehdr + ehdr->e_phoff);
1.37 mrg 198: obj->entry = (void *)(uintptr_t)ehdr->e_entry;
1.22 mycroft 199: if (!obj->dynamic) {
1.12 mycroft 200: _rtld_error("%s: not dynamically linked", path);
1.16 mycroft 201: goto bad;
1.12 mycroft 202: }
203: if (nsegs != 2) {
204: _rtld_error("%s: wrong number of segments (%d != 2)", path,
205: nsegs);
1.16 mycroft 206: goto bad;
1.4 christos 207: }
1.1 cgd 208:
1.4 christos 209: /*
1.11 chs 210: * Map the entire address space of the object as a file
1.5 thorpej 211: * region to stake out our contiguous region and establish a
1.11 chs 212: * base for relocation. We use a file mapping so that
213: * the kernel will give us whatever alignment is appropriate
214: * for the platform we're running on.
1.5 thorpej 215: *
1.11 chs 216: * We map it using the text protection, map the data segment
217: * into the right place, then map an anon segment for the bss
218: * and unmap the gaps left by padding to alignment.
1.5 thorpej 219: */
1.11 chs 220:
1.27 matt 221: #ifdef MAP_ALIGNED
222: base_alignment = segs[0]->p_align;
223: #endif
1.4 christos 224: base_offset = round_down(segs[0]->p_offset);
225: base_vaddr = round_down(segs[0]->p_vaddr);
226: base_vlimit = round_up(segs[1]->p_vaddr + segs[1]->p_memsz);
1.11 chs 227: text_vlimit = round_up(segs[0]->p_vaddr + segs[0]->p_memsz);
1.22 mycroft 228: text_flags = protflags(segs[0]->p_flags);
229: data_offset = round_down(segs[1]->p_offset);
230: data_vaddr = round_down(segs[1]->p_vaddr);
231: data_vlimit = round_up(segs[1]->p_vaddr + segs[1]->p_filesz);
232: data_flags = protflags(segs[1]->p_flags);
1.23 mycroft 233: #ifdef RTLD_LOADER
1.22 mycroft 234: clear_vaddr = segs[1]->p_vaddr + segs[1]->p_filesz;
1.23 mycroft 235: #endif
1.22 mycroft 236:
237: obj->textsize = text_vlimit - base_vaddr;
238: obj->vaddrbase = base_vaddr;
239: obj->isdynamic = ehdr->e_type == ET_DYN;
240:
1.41 ! skrll 241: obj->phdr_loaded = false;
! 242: for (i = 0; i < nsegs; i++) {
! 243: if (phdr_vaddr != EA_UNDEF &&
! 244: segs[i]->p_vaddr <= phdr_vaddr &&
! 245: segs[i]->p_memsz >= phdr_memsz) {
! 246: obj->phdr_loaded = true;
! 247: break;
! 248: }
! 249: if (segs[i]->p_offset <= ehdr->e_phoff &&
! 250: segs[i]->p_memsz >= phsize) {
! 251: phdr_vaddr = segs[i]->p_vaddr + ehdr->e_phoff;
! 252: phdr_memsz = phsize;
! 253: obj->phdr_loaded = true;
! 254: break;
! 255: }
! 256: }
! 257: if (obj->phdr_loaded) {
! 258: obj->phdr = (void *)(uintptr_t)phdr_vaddr;
! 259: obj->phsize = phdr_memsz;
! 260: } else {
! 261: Elf_Phdr *buf;
! 262: buf = xmalloc(phsize);
! 263: if (buf == NULL) {
! 264: _rtld_error("%s: cannot allocate program header", path);
! 265: goto bad;
! 266: }
! 267: memcpy(buf, phdr, phsize);
! 268: obj->phdr = buf;
! 269: obj->phsize = phsize;
! 270: }
! 271: dbg(("%s: phdr %p phsize %zu (%s)", obj->path, obj->phdr, obj->phsize,
! 272: obj->phdr_loaded ? "loaded" : "allocated"));
! 273:
1.36 ad 274: /* Unmap header if it overlaps the first load section. */
275: if (base_offset < _rtld_pagesz) {
276: munmap(ehdr, _rtld_pagesz);
277: obj->ehdr = MAP_FAILED;
278: }
1.11 chs 279:
1.27 matt 280: /*
281: * Calculate log2 of the base section alignment.
282: */
283: mapflags = 0;
284: #ifdef MAP_ALIGNED
285: if (base_alignment > _rtld_pagesz) {
286: unsigned int log2 = 0;
287: for (; base_alignment > 1; base_alignment >>= 1)
288: log2++;
289: mapflags = MAP_ALIGNED(log2);
290: }
291: #endif
292:
1.1 cgd 293: #ifdef RTLD_LOADER
1.22 mycroft 294: base_addr = obj->isdynamic ? NULL : (caddr_t)base_vaddr;
1.1 cgd 295: #else
1.4 christos 296: base_addr = NULL;
1.1 cgd 297: #endif
1.22 mycroft 298: mapsize = base_vlimit - base_vaddr;
1.27 matt 299: mapbase = mmap(base_addr, mapsize, text_flags,
300: mapflags | MAP_FILE | MAP_PRIVATE, fd, base_offset);
1.5 thorpej 301: if (mapbase == MAP_FAILED) {
1.4 christos 302: _rtld_error("mmap of entire address space failed: %s",
303: xstrerror(errno));
1.16 mycroft 304: goto bad;
1.4 christos 305: }
1.11 chs 306:
1.4 christos 307: /* Overlay the data segment onto the proper region. */
308: data_addr = mapbase + (data_vaddr - base_vaddr);
1.22 mycroft 309: if (mmap(data_addr, data_vlimit - data_vaddr, data_flags,
310: MAP_FILE | MAP_PRIVATE | MAP_FIXED, fd, data_offset) ==
311: MAP_FAILED) {
1.4 christos 312: _rtld_error("mmap of data failed: %s", xstrerror(errno));
1.22 mycroft 313: goto bad;
1.11 chs 314: }
315:
316: /* Overlay the bss segment onto the proper region. */
317: if (mmap(mapbase + data_vlimit - base_vaddr, base_vlimit - data_vlimit,
1.22 mycroft 318: data_flags, MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0) ==
319: MAP_FAILED) {
1.11 chs 320: _rtld_error("mmap of bss failed: %s", xstrerror(errno));
1.22 mycroft 321: goto bad;
1.4 christos 322: }
1.5 thorpej 323:
324: /* Unmap the gap between the text and data. */
1.22 mycroft 325: gap_addr = mapbase + round_up(text_vlimit - base_vaddr);
1.5 thorpej 326: gap_size = data_addr - gap_addr;
1.21 mycroft 327: if (gap_size != 0 && mprotect(gap_addr, gap_size, PROT_NONE) == -1) {
328: _rtld_error("mprotect of text -> data gap failed: %s",
1.5 thorpej 329: xstrerror(errno));
1.22 mycroft 330: goto bad;
1.5 thorpej 331: }
332:
1.1 cgd 333: #ifdef RTLD_LOADER
1.4 christos 334: /* Clear any BSS in the last page of the data segment. */
335: clear_addr = mapbase + (clear_vaddr - base_vaddr);
336: if ((nclear = data_vlimit - clear_vaddr) > 0)
337: memset(clear_addr, 0, nclear);
338:
1.5 thorpej 339: /* Non-file portion of BSS mapped above. */
1.1 cgd 340: #endif
341:
1.4 christos 342: obj->mapbase = mapbase;
343: obj->mapsize = mapsize;
344: obj->relocbase = mapbase - base_vaddr;
1.10 mycroft 345:
1.22 mycroft 346: if (obj->dynamic)
1.37 mrg 347: obj->dynamic = (void *)(obj->relocbase + (Elf_Addr)(uintptr_t)obj->dynamic);
1.22 mycroft 348: if (obj->entry)
1.37 mrg 349: obj->entry = (void *)(obj->relocbase + (Elf_Addr)(uintptr_t)obj->entry);
1.22 mycroft 350: if (obj->interp)
1.37 mrg 351: obj->interp = (void *)(obj->relocbase + (Elf_Addr)(uintptr_t)obj->interp);
1.41 ! skrll 352: if (obj->phdr_loaded)
! 353: obj->phdr = (void *)(obj->relocbase + (Elf_Addr)(uintptr_t)obj->phdr);
1.22 mycroft 354:
1.10 mycroft 355: return obj;
1.16 mycroft 356:
357: bad:
1.36 ad 358: if (obj->ehdr != MAP_FAILED)
359: munmap(obj->ehdr, _rtld_pagesz);
1.22 mycroft 360: if (mapbase != MAP_FAILED)
361: munmap(mapbase, mapsize);
362: _rtld_obj_free(obj);
1.16 mycroft 363: return NULL;
1.10 mycroft 364: }
365:
366: void
1.30 skrll 367: _rtld_obj_free(Obj_Entry *obj)
1.10 mycroft 368: {
369: Objlist_Entry *elm;
370:
1.35 ad 371: xfree(obj->path);
1.10 mycroft 372: while (obj->needed != NULL) {
373: Needed_Entry *needed = obj->needed;
374: obj->needed = needed->next;
1.35 ad 375: xfree(needed);
1.10 mycroft 376: }
1.13 lukem 377: while ((elm = SIMPLEQ_FIRST(&obj->dldags)) != NULL) {
378: SIMPLEQ_REMOVE_HEAD(&obj->dldags, link);
1.35 ad 379: xfree(elm);
1.10 mycroft 380: }
1.13 lukem 381: while ((elm = SIMPLEQ_FIRST(&obj->dagmembers)) != NULL) {
382: SIMPLEQ_REMOVE_HEAD(&obj->dagmembers, link);
1.35 ad 383: xfree(elm);
1.10 mycroft 384: }
1.41 ! skrll 385: if (!obj->phdr_loaded)
! 386: xfree((void *)(uintptr_t)obj->phdr);
1.35 ad 387: xfree(obj);
1.39 skrll 388: #ifdef COMBRELOC
389: _rtld_combreloc_reset(obj);
390: #endif
1.10 mycroft 391: }
392:
393: Obj_Entry *
394: _rtld_obj_new(void)
395: {
396: Obj_Entry *obj;
397:
398: obj = CNEW(Obj_Entry);
399: SIMPLEQ_INIT(&obj->dldags);
400: SIMPLEQ_INIT(&obj->dagmembers);
1.4 christos 401: return obj;
1.1 cgd 402: }
403:
404: /*
405: * Given a set of ELF protection flags, return the corresponding protection
406: * flags for MMAP.
407: */
408: static int
1.30 skrll 409: protflags(int elfflags)
1.1 cgd 410: {
1.4 christos 411: int prot = 0;
1.29 simonb 412:
1.8 kleink 413: if (elfflags & PF_R)
1.4 christos 414: prot |= PROT_READ;
1.1 cgd 415: #ifdef RTLD_LOADER
1.8 kleink 416: if (elfflags & PF_W)
1.4 christos 417: prot |= PROT_WRITE;
1.1 cgd 418: #endif
1.8 kleink 419: if (elfflags & PF_X)
1.4 christos 420: prot |= PROT_EXEC;
421: return prot;
1.1 cgd 422: }
CVSweb <webmaster@jp.NetBSD.org>