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