root/usr/src/cmd/sgs/libelf/common/input.c
/*
 * 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 <unistd.h>
#include <stdlib.h>
#include <memory.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <libelf.h>
#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);
}