17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
5*7257d1b4Sraf * Common Development and Distribution License (the "License").
6*7257d1b4Sraf * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
207c478bd9Sstevel@tonic-gate */
21*7257d1b4Sraf
22*7257d1b4Sraf /*
23*7257d1b4Sraf * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24*7257d1b4Sraf * Use is subject to license terms.
25*7257d1b4Sraf */
26*7257d1b4Sraf
277c478bd9Sstevel@tonic-gate /* Copyright (c) 1988 AT&T */
287c478bd9Sstevel@tonic-gate /* All Rights Reserved */
297c478bd9Sstevel@tonic-gate
30*7257d1b4Sraf #pragma ident "%Z%%M% %I% %E% SMI"
317c478bd9Sstevel@tonic-gate
327c478bd9Sstevel@tonic-gate #include <unistd.h>
337c478bd9Sstevel@tonic-gate #include <stdlib.h>
347c478bd9Sstevel@tonic-gate #include <memory.h>
357c478bd9Sstevel@tonic-gate #include <errno.h>
367c478bd9Sstevel@tonic-gate #include <sys/mman.h>
377c478bd9Sstevel@tonic-gate #include <sys/param.h>
387c478bd9Sstevel@tonic-gate #include <libelf.h>
397c478bd9Sstevel@tonic-gate #include "decl.h"
407c478bd9Sstevel@tonic-gate #include "msg.h"
417c478bd9Sstevel@tonic-gate
427c478bd9Sstevel@tonic-gate
437c478bd9Sstevel@tonic-gate /*
447c478bd9Sstevel@tonic-gate * File input
457c478bd9Sstevel@tonic-gate * These functions read input files.
467c478bd9Sstevel@tonic-gate * On SVR4 and newer systems use mmap(2). On older systems (or on
477c478bd9Sstevel@tonic-gate * file systems that don't support mmap, this code simulates mmap.
487c478bd9Sstevel@tonic-gate * When reading a file, enough memory is allocated to hold the file's
497c478bd9Sstevel@tonic-gate * image, and reads are delayed. When another part of the library
507c478bd9Sstevel@tonic-gate * wants to use a part of the file, it "fetches" the needed regions.
517c478bd9Sstevel@tonic-gate *
527c478bd9Sstevel@tonic-gate * An elf descriptor has a bit array to manage this. Each bit
537c478bd9Sstevel@tonic-gate * represents one "page" of the file. Pages are grouped into regions.
547c478bd9Sstevel@tonic-gate * The page size is tunable. Its value should be at least one disk
557c478bd9Sstevel@tonic-gate * block and small enough to avoid superfluous traffic.
567c478bd9Sstevel@tonic-gate *
577c478bd9Sstevel@tonic-gate * NBITS The number of bits in an unsigned. Each unsigned object
587c478bd9Sstevel@tonic-gate * holds a "REGION." A byte must have at least 8 bits;
597c478bd9Sstevel@tonic-gate * it may have more, though the extra bits at the top of
607c478bd9Sstevel@tonic-gate * the unsigned will be unused. Thus, for 9-bit bytes and
617c478bd9Sstevel@tonic-gate * 36-bit words, 4 bits at the top will stay empty.
627c478bd9Sstevel@tonic-gate *
637c478bd9Sstevel@tonic-gate * This mechanism gives significant performance gains for library
647c478bd9Sstevel@tonic-gate * handling (among other things), because programs typically don't
657c478bd9Sstevel@tonic-gate * need to look at entire libraries. The fastest I/O is no I/O.
667c478bd9Sstevel@tonic-gate */
677c478bd9Sstevel@tonic-gate
687c478bd9Sstevel@tonic-gate /*
697c478bd9Sstevel@tonic-gate * This global is used to hold the value of the PAGESIZE macro.
707c478bd9Sstevel@tonic-gate *
717c478bd9Sstevel@tonic-gate * This is because the PAGESIZE macro actually calls the
727c478bd9Sstevel@tonic-gate * sysconfig(_CONFIG_PAGESIZE) system call and we don't want
737c478bd9Sstevel@tonic-gate * to repeatedly call this through out libelf.
747c478bd9Sstevel@tonic-gate */
757c478bd9Sstevel@tonic-gate static unsigned long _elf_pagesize = 0;
767c478bd9Sstevel@tonic-gate NOTE(SCHEME_PROTECTS_DATA("read only data", _elf_pagesize))
777c478bd9Sstevel@tonic-gate
787c478bd9Sstevel@tonic-gate
797c478bd9Sstevel@tonic-gate #define NBITS (8 * sizeof (unsigned))
807c478bd9Sstevel@tonic-gate #define REGSZ (NBITS * _elf_pagesize)
817c478bd9Sstevel@tonic-gate #define PGNUM(off) ((off % REGSZ) / _elf_pagesize)
827c478bd9Sstevel@tonic-gate #define REGNUM(off) (off / REGSZ)
837c478bd9Sstevel@tonic-gate
847c478bd9Sstevel@tonic-gate
857c478bd9Sstevel@tonic-gate
867c478bd9Sstevel@tonic-gate Okay
_elf_vm(Elf * elf,size_t base,size_t sz)877c478bd9Sstevel@tonic-gate _elf_vm(Elf * elf, size_t base, size_t sz)
887c478bd9Sstevel@tonic-gate {
897c478bd9Sstevel@tonic-gate NOTE(ASSUMING_PROTECTED(*elf))
907c478bd9Sstevel@tonic-gate register unsigned *hdreg, hdbit;
917c478bd9Sstevel@tonic-gate unsigned *tlreg, tlbit;
927c478bd9Sstevel@tonic-gate size_t tail;
937c478bd9Sstevel@tonic-gate off_t off;
947c478bd9Sstevel@tonic-gate Elf_Void *iop;
957c478bd9Sstevel@tonic-gate
967c478bd9Sstevel@tonic-gate
977c478bd9Sstevel@tonic-gate /*
987c478bd9Sstevel@tonic-gate * always validate region
997c478bd9Sstevel@tonic-gate */
1007c478bd9Sstevel@tonic-gate
1017c478bd9Sstevel@tonic-gate if ((base + sz) > elf->ed_fsz) {
1027c478bd9Sstevel@tonic-gate /*
1037c478bd9Sstevel@tonic-gate * range outside of file bounds.
1047c478bd9Sstevel@tonic-gate */
1057c478bd9Sstevel@tonic-gate _elf_seterr(EFMT_VM, 0);
1067c478bd9Sstevel@tonic-gate return (OK_NO);
1077c478bd9Sstevel@tonic-gate }
1087c478bd9Sstevel@tonic-gate
1097c478bd9Sstevel@tonic-gate /*
1107c478bd9Sstevel@tonic-gate * If file is mmap()'d and/or the read size is 0
1117c478bd9Sstevel@tonic-gate * their is nothing else for us to do.
1127c478bd9Sstevel@tonic-gate */
1137c478bd9Sstevel@tonic-gate if (elf->ed_vm == 0 || sz == 0)
1147c478bd9Sstevel@tonic-gate return (OK_YES);
1157c478bd9Sstevel@tonic-gate /*
1167c478bd9Sstevel@tonic-gate * This uses arithmetic instead of masking because
1177c478bd9Sstevel@tonic-gate * sizeof (unsigned) might not be a power of 2.
1187c478bd9Sstevel@tonic-gate *
1197c478bd9Sstevel@tonic-gate * Tail gives one beyond the last offset that must be retrieved,
1207c478bd9Sstevel@tonic-gate * NOT the last in the region.
1217c478bd9Sstevel@tonic-gate */
1227c478bd9Sstevel@tonic-gate
1237c478bd9Sstevel@tonic-gate if (elf->ed_parent && elf->ed_parent->ed_fd == -1)
1247c478bd9Sstevel@tonic-gate elf->ed_fd = -1;
1257c478bd9Sstevel@tonic-gate
1267c478bd9Sstevel@tonic-gate base += elf->ed_baseoff;
1277c478bd9Sstevel@tonic-gate tail = base + sz + _elf_pagesize - 1;
1287c478bd9Sstevel@tonic-gate off = base - base % _elf_pagesize;
1297c478bd9Sstevel@tonic-gate hdbit = 1 << PGNUM(base);
1307c478bd9Sstevel@tonic-gate tlbit = 1 << PGNUM(tail);
1317c478bd9Sstevel@tonic-gate hdreg = &elf->ed_vm[REGNUM(base)];
1327c478bd9Sstevel@tonic-gate tlreg = &elf->ed_vm[REGNUM(tail)];
1337c478bd9Sstevel@tonic-gate sz = 0;
1347c478bd9Sstevel@tonic-gate
1357c478bd9Sstevel@tonic-gate /*
1367c478bd9Sstevel@tonic-gate * Scan through the files 'page table' and make sure
1377c478bd9Sstevel@tonic-gate * that all of the pages in the specified range have been
1387c478bd9Sstevel@tonic-gate * loaded into memory. As the pages are loaded the appropriate
1397c478bd9Sstevel@tonic-gate * bit in the 'page table' is set.
1407c478bd9Sstevel@tonic-gate *
1417c478bd9Sstevel@tonic-gate * Note: This loop will only read in those pages which havn't
1427c478bd9Sstevel@tonic-gate * been previously loaded into memory, if the page is
1437c478bd9Sstevel@tonic-gate * already present it will not be re-loaded.
1447c478bd9Sstevel@tonic-gate */
1457c478bd9Sstevel@tonic-gate while ((hdreg != tlreg) || (hdbit != tlbit)) {
1467c478bd9Sstevel@tonic-gate if (*hdreg & hdbit) {
1477c478bd9Sstevel@tonic-gate if (sz != 0) {
1487c478bd9Sstevel@tonic-gate /*
1497c478bd9Sstevel@tonic-gate * Read in a 'chunk' of the elf image.
1507c478bd9Sstevel@tonic-gate */
1517c478bd9Sstevel@tonic-gate iop = (Elf_Void *)(elf->ed_image + off);
1527c478bd9Sstevel@tonic-gate /*
1537c478bd9Sstevel@tonic-gate * do not read past the end of the file
1547c478bd9Sstevel@tonic-gate */
1557c478bd9Sstevel@tonic-gate if (elf->ed_imagesz - off < sz)
1567c478bd9Sstevel@tonic-gate sz = elf->ed_imagesz - off;
1577c478bd9Sstevel@tonic-gate if ((lseek(elf->ed_fd, off,
1587c478bd9Sstevel@tonic-gate SEEK_SET) != off) ||
1597c478bd9Sstevel@tonic-gate (read(elf->ed_fd, iop, sz) != sz)) {
1607c478bd9Sstevel@tonic-gate _elf_seterr(EIO_VM, errno);
1617c478bd9Sstevel@tonic-gate return (OK_NO);
1627c478bd9Sstevel@tonic-gate }
1637c478bd9Sstevel@tonic-gate off += sz;
1647c478bd9Sstevel@tonic-gate sz = 0;
1657c478bd9Sstevel@tonic-gate }
1667c478bd9Sstevel@tonic-gate off += _elf_pagesize;
1677c478bd9Sstevel@tonic-gate } else {
1687c478bd9Sstevel@tonic-gate if (elf->ed_fd < 0) {
1697c478bd9Sstevel@tonic-gate _elf_seterr(EREQ_NOFD, 0);
1707c478bd9Sstevel@tonic-gate return (OK_NO);
1717c478bd9Sstevel@tonic-gate }
1727c478bd9Sstevel@tonic-gate sz += _elf_pagesize;
1737c478bd9Sstevel@tonic-gate *hdreg |= hdbit;
1747c478bd9Sstevel@tonic-gate }
1757c478bd9Sstevel@tonic-gate if (hdbit == ((unsigned)1 << (NBITS - 1))) {
1767c478bd9Sstevel@tonic-gate hdbit = 1;
1777c478bd9Sstevel@tonic-gate ++hdreg;
1787c478bd9Sstevel@tonic-gate } else
1797c478bd9Sstevel@tonic-gate hdbit <<= 1;
1807c478bd9Sstevel@tonic-gate }
1817c478bd9Sstevel@tonic-gate
1827c478bd9Sstevel@tonic-gate if (sz != 0) {
1837c478bd9Sstevel@tonic-gate iop = (Elf_Void *)(elf->ed_image + off);
1847c478bd9Sstevel@tonic-gate /*
1857c478bd9Sstevel@tonic-gate * do not read past the end of the file
1867c478bd9Sstevel@tonic-gate */
1877c478bd9Sstevel@tonic-gate if ((elf->ed_imagesz - off) < sz)
1887c478bd9Sstevel@tonic-gate sz = elf->ed_imagesz - off;
1897c478bd9Sstevel@tonic-gate if ((lseek(elf->ed_fd, off, SEEK_SET) != off) ||
1907c478bd9Sstevel@tonic-gate (read(elf->ed_fd, iop, sz) != sz)) {
1917c478bd9Sstevel@tonic-gate _elf_seterr(EIO_VM, errno);
1927c478bd9Sstevel@tonic-gate return (OK_NO);
1937c478bd9Sstevel@tonic-gate }
1947c478bd9Sstevel@tonic-gate }
1957c478bd9Sstevel@tonic-gate return (OK_YES);
1967c478bd9Sstevel@tonic-gate }
1977c478bd9Sstevel@tonic-gate
1987c478bd9Sstevel@tonic-gate
1997c478bd9Sstevel@tonic-gate Okay
_elf_inmap(Elf * elf)2007c478bd9Sstevel@tonic-gate _elf_inmap(Elf * elf)
2017c478bd9Sstevel@tonic-gate {
2027c478bd9Sstevel@tonic-gate int fd = elf->ed_fd;
2037c478bd9Sstevel@tonic-gate register size_t sz;
2047c478bd9Sstevel@tonic-gate
2057c478bd9Sstevel@tonic-gate {
2067c478bd9Sstevel@tonic-gate register off_t off = lseek(fd, (off_t)0, SEEK_END);
2077c478bd9Sstevel@tonic-gate
2087c478bd9Sstevel@tonic-gate if (off == 0)
2097c478bd9Sstevel@tonic-gate return (OK_YES);
2107c478bd9Sstevel@tonic-gate
2117c478bd9Sstevel@tonic-gate if (off == -1) {
2127c478bd9Sstevel@tonic-gate _elf_seterr(EIO_FSZ, errno);
2137c478bd9Sstevel@tonic-gate return (OK_NO);
2147c478bd9Sstevel@tonic-gate }
2157c478bd9Sstevel@tonic-gate
2167c478bd9Sstevel@tonic-gate if ((sz = (size_t)off) != off) {
2177c478bd9Sstevel@tonic-gate _elf_seterr(EIO_FBIG, 0);
2187c478bd9Sstevel@tonic-gate return (OK_NO);
2197c478bd9Sstevel@tonic-gate }
2207c478bd9Sstevel@tonic-gate }
2217c478bd9Sstevel@tonic-gate /*
2227c478bd9Sstevel@tonic-gate * If the file is mapped, elf->ed_vm will stay null
2237c478bd9Sstevel@tonic-gate * and elf->ed_image will need to be unmapped someday.
2247c478bd9Sstevel@tonic-gate * If the file is read, elf->ed_vm and the file image
2257c478bd9Sstevel@tonic-gate * are allocated together; free() elf->ed_vm.
2267c478bd9Sstevel@tonic-gate *
2277c478bd9Sstevel@tonic-gate * If the file can be written, disallow mmap.
2287c478bd9Sstevel@tonic-gate * Otherwise, the input mapping and the output mapping
2297c478bd9Sstevel@tonic-gate * can collide. Moreover, elf_update will truncate
2307c478bd9Sstevel@tonic-gate * the file, possibly invalidating the input mapping.
2317c478bd9Sstevel@tonic-gate * Disallowing input mmap forces the library to malloc
2327c478bd9Sstevel@tonic-gate * and read the space, which will make output mmap safe.
2337c478bd9Sstevel@tonic-gate * Using mmap for output reduces the swap space needed
2347c478bd9Sstevel@tonic-gate * for the process, so that is given preference.
2357c478bd9Sstevel@tonic-gate */
2367c478bd9Sstevel@tonic-gate
2377c478bd9Sstevel@tonic-gate {
2387c478bd9Sstevel@tonic-gate register char *p;
2397c478bd9Sstevel@tonic-gate
2407c478bd9Sstevel@tonic-gate if ((elf->ed_myflags & EDF_WRITE) == 0 &&
2417c478bd9Sstevel@tonic-gate (p = mmap((char *)0, sz, PROT_READ,
2427c478bd9Sstevel@tonic-gate MAP_PRIVATE, fd, (off_t)0)) != (char *)-1) {
2437c478bd9Sstevel@tonic-gate elf->ed_image = elf->ed_ident = p;
2447c478bd9Sstevel@tonic-gate elf->ed_imagesz = elf->ed_fsz = elf->ed_identsz = sz;
2457c478bd9Sstevel@tonic-gate return (OK_YES);
2467c478bd9Sstevel@tonic-gate }
2477c478bd9Sstevel@tonic-gate }
2487c478bd9Sstevel@tonic-gate
2497c478bd9Sstevel@tonic-gate if (_elf_pagesize == 0)
2507c478bd9Sstevel@tonic-gate _elf_pagesize = PAGESIZE;
2517c478bd9Sstevel@tonic-gate
2527c478bd9Sstevel@tonic-gate /*
2537c478bd9Sstevel@tonic-gate * If mmap fails, try read. Some file systems don't mmap
2547c478bd9Sstevel@tonic-gate */
2557c478bd9Sstevel@tonic-gate {
2567c478bd9Sstevel@tonic-gate register size_t vmsz = sizeof (unsigned) * (REGNUM(sz) + 1);
2577c478bd9Sstevel@tonic-gate
2587c478bd9Sstevel@tonic-gate if (vmsz % sizeof (Elf64) != 0)
2597c478bd9Sstevel@tonic-gate vmsz += sizeof (Elf64) - vmsz % sizeof (Elf64);
2607c478bd9Sstevel@tonic-gate if ((elf->ed_vm = (unsigned *)malloc(vmsz + sz)) == 0) {
2617c478bd9Sstevel@tonic-gate _elf_seterr(EMEM_VM, errno);
2627c478bd9Sstevel@tonic-gate return (OK_NO);
2637c478bd9Sstevel@tonic-gate }
2647c478bd9Sstevel@tonic-gate (void) memset(elf->ed_vm, 0, vmsz);
2657c478bd9Sstevel@tonic-gate elf->ed_vmsz = vmsz / sizeof (unsigned);
2667c478bd9Sstevel@tonic-gate elf->ed_image = elf->ed_ident = (char *)elf->ed_vm + vmsz;
2677c478bd9Sstevel@tonic-gate elf->ed_imagesz = elf->ed_fsz = elf->ed_identsz = sz;
2687c478bd9Sstevel@tonic-gate }
2697c478bd9Sstevel@tonic-gate return (_elf_vm(elf, (size_t)0, (size_t)1));
2707c478bd9Sstevel@tonic-gate }
2717c478bd9Sstevel@tonic-gate
2727c478bd9Sstevel@tonic-gate
2737c478bd9Sstevel@tonic-gate void
_elf_unmap(char * p,size_t sz)2747c478bd9Sstevel@tonic-gate _elf_unmap(char *p, size_t sz)
2757c478bd9Sstevel@tonic-gate {
2767c478bd9Sstevel@tonic-gate if (p == 0 || sz == 0)
2777c478bd9Sstevel@tonic-gate return;
2787c478bd9Sstevel@tonic-gate (void) munmap(p, sz);
2797c478bd9Sstevel@tonic-gate }
280