xref: /onnv-gate/usr/src/cmd/sgs/libelf/common/input.c (revision 6812:febeba71273d)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*6812Sraf  * Common Development and Distribution License (the "License").
6*6812Sraf  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
21*6812Sraf 
22*6812Sraf /*
23*6812Sraf  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24*6812Sraf  * Use is subject to license terms.
25*6812Sraf  */
26*6812Sraf 
270Sstevel@tonic-gate /*	Copyright (c) 1988 AT&T	*/
280Sstevel@tonic-gate /*	  All Rights Reserved  	*/
290Sstevel@tonic-gate 
30*6812Sraf #pragma ident	"%Z%%M%	%I%	%E% SMI"
310Sstevel@tonic-gate 
320Sstevel@tonic-gate #include <unistd.h>
330Sstevel@tonic-gate #include <stdlib.h>
340Sstevel@tonic-gate #include <memory.h>
350Sstevel@tonic-gate #include <errno.h>
360Sstevel@tonic-gate #include <sys/mman.h>
370Sstevel@tonic-gate #include <sys/param.h>
380Sstevel@tonic-gate #include <libelf.h>
390Sstevel@tonic-gate #include "decl.h"
400Sstevel@tonic-gate #include "msg.h"
410Sstevel@tonic-gate 
420Sstevel@tonic-gate 
430Sstevel@tonic-gate /*
440Sstevel@tonic-gate  * File input
450Sstevel@tonic-gate  *	These functions read input files.
460Sstevel@tonic-gate  *	On SVR4 and newer systems use mmap(2).  On older systems (or on
470Sstevel@tonic-gate  *	file systems that don't support mmap, this code simulates mmap.
480Sstevel@tonic-gate  *	When reading a file, enough memory is allocated to hold the file's
490Sstevel@tonic-gate  *	image, and reads are delayed.  When another part of the library
500Sstevel@tonic-gate  *	wants to use a part of the file, it "fetches" the needed regions.
510Sstevel@tonic-gate  *
520Sstevel@tonic-gate  *	An elf descriptor has a bit array to manage this.  Each bit
530Sstevel@tonic-gate  *	represents one "page" of the file.  Pages are grouped into regions.
540Sstevel@tonic-gate  *	The page size is tunable.  Its value should be at least one disk
550Sstevel@tonic-gate  *	block and small enough to avoid superfluous traffic.
560Sstevel@tonic-gate  *
570Sstevel@tonic-gate  *	NBITS	The number of bits in an unsigned.  Each unsigned object
580Sstevel@tonic-gate  *		holds a "REGION."  A byte must have at least 8 bits;
590Sstevel@tonic-gate  *		it may have more, though the extra bits at the top of
600Sstevel@tonic-gate  *		the unsigned will be unused.  Thus, for 9-bit bytes and
610Sstevel@tonic-gate  *		36-bit words, 4 bits at the top will stay empty.
620Sstevel@tonic-gate  *
630Sstevel@tonic-gate  *	This mechanism gives significant performance gains for library
640Sstevel@tonic-gate  *	handling (among other things), because programs typically don't
650Sstevel@tonic-gate  *	need to look at entire libraries.  The fastest I/O is no I/O.
660Sstevel@tonic-gate  */
670Sstevel@tonic-gate 
680Sstevel@tonic-gate /*
690Sstevel@tonic-gate  * This global is used to hold the value of the PAGESIZE macro.
700Sstevel@tonic-gate  *
710Sstevel@tonic-gate  * This is because the PAGESIZE macro actually calls the
720Sstevel@tonic-gate  * sysconfig(_CONFIG_PAGESIZE) system call and we don't want
730Sstevel@tonic-gate  * to repeatedly call this through out libelf.
740Sstevel@tonic-gate  */
750Sstevel@tonic-gate static unsigned long	_elf_pagesize = 0;
760Sstevel@tonic-gate NOTE(SCHEME_PROTECTS_DATA("read only data", _elf_pagesize))
770Sstevel@tonic-gate 
780Sstevel@tonic-gate 
790Sstevel@tonic-gate #define	NBITS		(8 * sizeof (unsigned))
800Sstevel@tonic-gate #define	REGSZ		(NBITS * _elf_pagesize)
810Sstevel@tonic-gate #define	PGNUM(off)	((off % REGSZ) / _elf_pagesize)
820Sstevel@tonic-gate #define	REGNUM(off)	(off / REGSZ)
830Sstevel@tonic-gate 
840Sstevel@tonic-gate 
850Sstevel@tonic-gate 
860Sstevel@tonic-gate Okay
_elf_vm(Elf * elf,size_t base,size_t sz)870Sstevel@tonic-gate _elf_vm(Elf * elf, size_t base, size_t sz)
880Sstevel@tonic-gate {
890Sstevel@tonic-gate 	NOTE(ASSUMING_PROTECTED(*elf))
900Sstevel@tonic-gate 	register unsigned	*hdreg, hdbit;
910Sstevel@tonic-gate 	unsigned		*tlreg, tlbit;
920Sstevel@tonic-gate 	size_t			tail;
930Sstevel@tonic-gate 	off_t			off;
940Sstevel@tonic-gate 	Elf_Void		*iop;
950Sstevel@tonic-gate 
960Sstevel@tonic-gate 
970Sstevel@tonic-gate 	/*
980Sstevel@tonic-gate 	 * always validate region
990Sstevel@tonic-gate 	 */
1000Sstevel@tonic-gate 
1010Sstevel@tonic-gate 	if ((base + sz) > elf->ed_fsz) {
1020Sstevel@tonic-gate 		/*
1030Sstevel@tonic-gate 		 * range outside of file bounds.
1040Sstevel@tonic-gate 		 */
1050Sstevel@tonic-gate 		_elf_seterr(EFMT_VM, 0);
1060Sstevel@tonic-gate 		return (OK_NO);
1070Sstevel@tonic-gate 	}
1080Sstevel@tonic-gate 
1090Sstevel@tonic-gate 	/*
1100Sstevel@tonic-gate 	 * If file is mmap()'d and/or the read size is 0
1110Sstevel@tonic-gate 	 * their is nothing else for us to do.
1120Sstevel@tonic-gate 	 */
1130Sstevel@tonic-gate 	if (elf->ed_vm == 0 || sz == 0)
1140Sstevel@tonic-gate 		return (OK_YES);
1150Sstevel@tonic-gate 	/*
1160Sstevel@tonic-gate 	 * This uses arithmetic instead of masking because
1170Sstevel@tonic-gate 	 * sizeof (unsigned) might not be a power of 2.
1180Sstevel@tonic-gate 	 *
1190Sstevel@tonic-gate 	 * Tail gives one beyond the last offset that must be retrieved,
1200Sstevel@tonic-gate 	 * NOT the last in the region.
1210Sstevel@tonic-gate 	 */
1220Sstevel@tonic-gate 
1230Sstevel@tonic-gate 	if (elf->ed_parent && elf->ed_parent->ed_fd == -1)
1240Sstevel@tonic-gate 		elf->ed_fd = -1;
1250Sstevel@tonic-gate 
1260Sstevel@tonic-gate 	base += elf->ed_baseoff;
1270Sstevel@tonic-gate 	tail = base + sz + _elf_pagesize - 1;
1280Sstevel@tonic-gate 	off = base - base % _elf_pagesize;
1290Sstevel@tonic-gate 	hdbit = 1 << PGNUM(base);
1300Sstevel@tonic-gate 	tlbit = 1 << PGNUM(tail);
1310Sstevel@tonic-gate 	hdreg = &elf->ed_vm[REGNUM(base)];
1320Sstevel@tonic-gate 	tlreg = &elf->ed_vm[REGNUM(tail)];
1330Sstevel@tonic-gate 	sz = 0;
1340Sstevel@tonic-gate 
1350Sstevel@tonic-gate 	/*
1360Sstevel@tonic-gate 	 * Scan through the files 'page table' and make sure
1370Sstevel@tonic-gate 	 * that all of the pages in the specified range have been
1380Sstevel@tonic-gate 	 * loaded into memory.  As the pages are loaded the appropriate
1390Sstevel@tonic-gate 	 * bit in the 'page table' is set.
1400Sstevel@tonic-gate 	 *
1410Sstevel@tonic-gate 	 * Note: This loop will only read in those pages which havn't
1420Sstevel@tonic-gate 	 *	 been previously loaded into memory, if the page is
1430Sstevel@tonic-gate 	 *	 already present it will not be re-loaded.
1440Sstevel@tonic-gate 	 */
1450Sstevel@tonic-gate 	while ((hdreg != tlreg) || (hdbit != tlbit)) {
1460Sstevel@tonic-gate 		if (*hdreg & hdbit) {
1470Sstevel@tonic-gate 			if (sz != 0) {
1480Sstevel@tonic-gate 				/*
1490Sstevel@tonic-gate 				 * Read in a 'chunk' of the elf image.
1500Sstevel@tonic-gate 				 */
1510Sstevel@tonic-gate 				iop = (Elf_Void *)(elf->ed_image + off);
1520Sstevel@tonic-gate 				/*
1530Sstevel@tonic-gate 				 * do not read past the end of the file
1540Sstevel@tonic-gate 				 */
1550Sstevel@tonic-gate 				if (elf->ed_imagesz - off < sz)
1560Sstevel@tonic-gate 					sz = elf->ed_imagesz - off;
1570Sstevel@tonic-gate 				if ((lseek(elf->ed_fd, off,
1580Sstevel@tonic-gate 				    SEEK_SET) != off) ||
1590Sstevel@tonic-gate 				    (read(elf->ed_fd, iop, sz) != sz)) {
1600Sstevel@tonic-gate 					_elf_seterr(EIO_VM, errno);
1610Sstevel@tonic-gate 					return (OK_NO);
1620Sstevel@tonic-gate 				}
1630Sstevel@tonic-gate 				off += sz;
1640Sstevel@tonic-gate 				sz = 0;
1650Sstevel@tonic-gate 			}
1660Sstevel@tonic-gate 			off += _elf_pagesize;
1670Sstevel@tonic-gate 		} else {
1680Sstevel@tonic-gate 			if (elf->ed_fd < 0) {
1690Sstevel@tonic-gate 				_elf_seterr(EREQ_NOFD, 0);
1700Sstevel@tonic-gate 				return (OK_NO);
1710Sstevel@tonic-gate 			}
1720Sstevel@tonic-gate 			sz += _elf_pagesize;
1730Sstevel@tonic-gate 			*hdreg |= hdbit;
1740Sstevel@tonic-gate 		}
1750Sstevel@tonic-gate 		if (hdbit == ((unsigned)1 << (NBITS - 1))) {
1760Sstevel@tonic-gate 			hdbit = 1;
1770Sstevel@tonic-gate 			++hdreg;
1780Sstevel@tonic-gate 		} else
1790Sstevel@tonic-gate 			hdbit <<= 1;
1800Sstevel@tonic-gate 	}
1810Sstevel@tonic-gate 
1820Sstevel@tonic-gate 	if (sz != 0) {
1830Sstevel@tonic-gate 		iop = (Elf_Void *)(elf->ed_image + off);
1840Sstevel@tonic-gate 		/*
1850Sstevel@tonic-gate 		 * do not read past the end of the file
1860Sstevel@tonic-gate 		 */
1870Sstevel@tonic-gate 		if ((elf->ed_imagesz - off) < sz)
1880Sstevel@tonic-gate 			sz = elf->ed_imagesz - off;
1890Sstevel@tonic-gate 		if ((lseek(elf->ed_fd, off, SEEK_SET) != off) ||
1900Sstevel@tonic-gate 		    (read(elf->ed_fd, iop, sz) != sz)) {
1910Sstevel@tonic-gate 			_elf_seterr(EIO_VM, errno);
1920Sstevel@tonic-gate 			return (OK_NO);
1930Sstevel@tonic-gate 		}
1940Sstevel@tonic-gate 	}
1950Sstevel@tonic-gate 	return (OK_YES);
1960Sstevel@tonic-gate }
1970Sstevel@tonic-gate 
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate Okay
_elf_inmap(Elf * elf)2000Sstevel@tonic-gate _elf_inmap(Elf * elf)
2010Sstevel@tonic-gate {
2020Sstevel@tonic-gate 	int		fd = elf->ed_fd;
2030Sstevel@tonic-gate 	register size_t	sz;
2040Sstevel@tonic-gate 
2050Sstevel@tonic-gate 	{
2060Sstevel@tonic-gate 		register off_t	off = lseek(fd, (off_t)0, SEEK_END);
2070Sstevel@tonic-gate 
2080Sstevel@tonic-gate 		if (off == 0)
2090Sstevel@tonic-gate 			return (OK_YES);
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate 		if (off == -1) {
2120Sstevel@tonic-gate 			_elf_seterr(EIO_FSZ, errno);
2130Sstevel@tonic-gate 			return (OK_NO);
2140Sstevel@tonic-gate 		}
2150Sstevel@tonic-gate 
2160Sstevel@tonic-gate 		if ((sz = (size_t)off) != off) {
2170Sstevel@tonic-gate 			_elf_seterr(EIO_FBIG, 0);
2180Sstevel@tonic-gate 			return (OK_NO);
2190Sstevel@tonic-gate 		}
2200Sstevel@tonic-gate 	}
2210Sstevel@tonic-gate 	/*
2220Sstevel@tonic-gate 	 *	If the file is mapped, elf->ed_vm will stay null
2230Sstevel@tonic-gate 	 *	and elf->ed_image will need to be unmapped someday.
2240Sstevel@tonic-gate 	 *	If the file is read, elf->ed_vm and the file image
2250Sstevel@tonic-gate 	 *	are allocated together; free() elf->ed_vm.
2260Sstevel@tonic-gate 	 *
2270Sstevel@tonic-gate 	 *	If the file can be written, disallow mmap.
2280Sstevel@tonic-gate 	 *	Otherwise, the input mapping and the output mapping
2290Sstevel@tonic-gate 	 *	can collide.  Moreover, elf_update will truncate
2300Sstevel@tonic-gate 	 *	the file, possibly invalidating the input mapping.
2310Sstevel@tonic-gate 	 *	Disallowing input mmap forces the library to malloc
2320Sstevel@tonic-gate 	 *	and read the space, which will make output mmap safe.
2330Sstevel@tonic-gate 	 *	Using mmap for output reduces the swap space needed
2340Sstevel@tonic-gate 	 *	for the process, so that is given preference.
2350Sstevel@tonic-gate 	 */
2360Sstevel@tonic-gate 
2370Sstevel@tonic-gate 	{
2380Sstevel@tonic-gate 		register char	*p;
2390Sstevel@tonic-gate 
2400Sstevel@tonic-gate 		if ((elf->ed_myflags & EDF_WRITE) == 0 &&
2410Sstevel@tonic-gate 		    (p = mmap((char *)0, sz, PROT_READ,
2420Sstevel@tonic-gate 		    MAP_PRIVATE, fd, (off_t)0)) != (char *)-1) {
2430Sstevel@tonic-gate 			elf->ed_image = elf->ed_ident = p;
2440Sstevel@tonic-gate 			elf->ed_imagesz = elf->ed_fsz = elf->ed_identsz = sz;
2450Sstevel@tonic-gate 			return (OK_YES);
2460Sstevel@tonic-gate 		}
2470Sstevel@tonic-gate 	}
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate 	if (_elf_pagesize == 0)
2500Sstevel@tonic-gate 		_elf_pagesize = PAGESIZE;
2510Sstevel@tonic-gate 
2520Sstevel@tonic-gate 	/*
2530Sstevel@tonic-gate 	 * If mmap fails, try read.  Some file systems don't mmap
2540Sstevel@tonic-gate 	 */
2550Sstevel@tonic-gate 	{
2560Sstevel@tonic-gate 		register size_t	vmsz = sizeof (unsigned) * (REGNUM(sz) + 1);
2570Sstevel@tonic-gate 
2580Sstevel@tonic-gate 		if (vmsz % sizeof (Elf64) != 0)
2590Sstevel@tonic-gate 			vmsz += sizeof (Elf64) - vmsz % sizeof (Elf64);
2600Sstevel@tonic-gate 		if ((elf->ed_vm = (unsigned *)malloc(vmsz + sz)) == 0) {
2610Sstevel@tonic-gate 			_elf_seterr(EMEM_VM, errno);
2620Sstevel@tonic-gate 			return (OK_NO);
2630Sstevel@tonic-gate 		}
2640Sstevel@tonic-gate 		(void) memset(elf->ed_vm, 0, vmsz);
2650Sstevel@tonic-gate 		elf->ed_vmsz = vmsz / sizeof (unsigned);
2660Sstevel@tonic-gate 		elf->ed_image = elf->ed_ident = (char *)elf->ed_vm + vmsz;
2670Sstevel@tonic-gate 		elf->ed_imagesz = elf->ed_fsz = elf->ed_identsz = sz;
2680Sstevel@tonic-gate 	}
2690Sstevel@tonic-gate 	return (_elf_vm(elf, (size_t)0, (size_t)1));
2700Sstevel@tonic-gate }
2710Sstevel@tonic-gate 
2720Sstevel@tonic-gate 
2730Sstevel@tonic-gate void
_elf_unmap(char * p,size_t sz)274*6812Sraf _elf_unmap(char *p, size_t sz)
2750Sstevel@tonic-gate {
2760Sstevel@tonic-gate 	if (p == 0 || sz == 0)
2770Sstevel@tonic-gate 		return;
2780Sstevel@tonic-gate 	(void) munmap(p, sz);
2790Sstevel@tonic-gate }
280