/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1988 AT&T */ /* All Rights Reserved */ #include #include #include #include #include #include #include #include "decl.h" #include "msg.h" /* * File input * These functions read input files. * On SVR4 and newer systems use mmap(2). On older systems (or on * file systems that don't support mmap, this code simulates mmap. * When reading a file, enough memory is allocated to hold the file's * image, and reads are delayed. When another part of the library * wants to use a part of the file, it "fetches" the needed regions. * * An elf descriptor has a bit array to manage this. Each bit * represents one "page" of the file. Pages are grouped into regions. * The page size is tunable. Its value should be at least one disk * block and small enough to avoid superfluous traffic. * * NBITS The number of bits in an unsigned. Each unsigned object * holds a "REGION." A byte must have at least 8 bits; * it may have more, though the extra bits at the top of * the unsigned will be unused. Thus, for 9-bit bytes and * 36-bit words, 4 bits at the top will stay empty. * * This mechanism gives significant performance gains for library * handling (among other things), because programs typically don't * need to look at entire libraries. The fastest I/O is no I/O. */ /* * This global is used to hold the value of the PAGESIZE macro. * * This is because the PAGESIZE macro actually calls the * sysconfig(_CONFIG_PAGESIZE) system call and we don't want * to repeatedly call this through out libelf. */ static unsigned long _elf_pagesize = 0; #define NBITS (8 * sizeof (unsigned)) #define REGSZ (NBITS * _elf_pagesize) #define PGNUM(off) ((off % REGSZ) / _elf_pagesize) #define REGNUM(off) (off / REGSZ) Okay _elf_vm(Elf * elf, size_t base, size_t sz) { register unsigned *hdreg, hdbit; unsigned *tlreg, tlbit; size_t tail; off_t off; Elf_Void *iop; /* * always validate region */ if ((base + sz) > elf->ed_fsz) { /* * range outside of file bounds. */ _elf_seterr(EFMT_VM, 0); return (OK_NO); } /* * If file is mmap()'d and/or the read size is 0 * their is nothing else for us to do. */ if (elf->ed_vm == 0 || sz == 0) return (OK_YES); /* * This uses arithmetic instead of masking because * sizeof (unsigned) might not be a power of 2. * * Tail gives one beyond the last offset that must be retrieved, * NOT the last in the region. */ if (elf->ed_parent && elf->ed_parent->ed_fd == -1) elf->ed_fd = -1; base += elf->ed_baseoff; tail = base + sz + _elf_pagesize - 1; off = base - base % _elf_pagesize; hdbit = 1 << PGNUM(base); tlbit = 1 << PGNUM(tail); hdreg = &elf->ed_vm[REGNUM(base)]; tlreg = &elf->ed_vm[REGNUM(tail)]; sz = 0; /* * Scan through the files 'page table' and make sure * that all of the pages in the specified range have been * loaded into memory. As the pages are loaded the appropriate * bit in the 'page table' is set. * * Note: This loop will only read in those pages which havn't * been previously loaded into memory, if the page is * already present it will not be re-loaded. */ while ((hdreg != tlreg) || (hdbit != tlbit)) { if (*hdreg & hdbit) { if (sz != 0) { /* * Read in a 'chunk' of the elf image. */ iop = (Elf_Void *)(elf->ed_image + off); /* * do not read past the end of the file */ if (elf->ed_imagesz - off < sz) sz = elf->ed_imagesz - off; if ((lseek(elf->ed_fd, off, SEEK_SET) != off) || (read(elf->ed_fd, iop, sz) != sz)) { _elf_seterr(EIO_VM, errno); return (OK_NO); } off += sz; sz = 0; } off += _elf_pagesize; } else { if (elf->ed_fd < 0) { _elf_seterr(EREQ_NOFD, 0); return (OK_NO); } sz += _elf_pagesize; *hdreg |= hdbit; } if (hdbit == ((unsigned)1 << (NBITS - 1))) { hdbit = 1; ++hdreg; } else hdbit <<= 1; } if (sz != 0) { iop = (Elf_Void *)(elf->ed_image + off); /* * do not read past the end of the file */ if ((elf->ed_imagesz - off) < sz) sz = elf->ed_imagesz - off; if ((lseek(elf->ed_fd, off, SEEK_SET) != off) || (read(elf->ed_fd, iop, sz) != sz)) { _elf_seterr(EIO_VM, errno); return (OK_NO); } } return (OK_YES); } Okay _elf_inmap(Elf * elf) { int fd = elf->ed_fd; register size_t sz; { register off_t off = lseek(fd, (off_t)0, SEEK_END); if (off == 0) return (OK_YES); if (off == -1) { _elf_seterr(EIO_FSZ, errno); return (OK_NO); } if ((sz = (size_t)off) != off) { _elf_seterr(EIO_FBIG, 0); return (OK_NO); } } /* * If the file is mapped, elf->ed_vm will stay null * and elf->ed_image will need to be unmapped someday. * If the file is read, elf->ed_vm and the file image * are allocated together; free() elf->ed_vm. * * If the file can be written, disallow mmap. * Otherwise, the input mapping and the output mapping * can collide. Moreover, elf_update will truncate * the file, possibly invalidating the input mapping. * Disallowing input mmap forces the library to malloc * and read the space, which will make output mmap safe. * Using mmap for output reduces the swap space needed * for the process, so that is given preference. */ { register char *p; if ((elf->ed_myflags & EDF_WRITE) == 0 && (p = mmap((char *)0, sz, PROT_READ, MAP_PRIVATE, fd, (off_t)0)) != (char *)-1) { elf->ed_image = elf->ed_ident = p; elf->ed_imagesz = elf->ed_fsz = elf->ed_identsz = sz; return (OK_YES); } } if (_elf_pagesize == 0) _elf_pagesize = PAGESIZE; /* * If mmap fails, try read. Some file systems don't mmap */ { register size_t vmsz = sizeof (unsigned) * (REGNUM(sz) + 1); if (vmsz % sizeof (Elf64) != 0) vmsz += sizeof (Elf64) - vmsz % sizeof (Elf64); if ((elf->ed_vm = (unsigned *)malloc(vmsz + sz)) == 0) { _elf_seterr(EMEM_VM, errno); return (OK_NO); } (void) memset(elf->ed_vm, 0, vmsz); elf->ed_vmsz = vmsz / sizeof (unsigned); elf->ed_image = elf->ed_ident = (char *)elf->ed_vm + vmsz; elf->ed_imagesz = elf->ed_fsz = elf->ed_identsz = sz; } return (_elf_vm(elf, (size_t)0, (size_t)1)); } void _elf_unmap(char *p, size_t sz) { if (p == 0 || sz == 0) return; (void) munmap(p, sz); }