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