1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*	Copyright (c) 1988 AT&T	*/
28/*	  All Rights Reserved  	*/
29
30#pragma ident	"@(#)input.c	1.19	08/05/31 SMI"
31
32#include <unistd.h>
33#include <stdlib.h>
34#include <memory.h>
35#include <errno.h>
36#include <sys/mman.h>
37#include <sys/param.h>
38#include <libelf.h>
39#include "decl.h"
40#include "msg.h"
41#include <string.h>
42
43/*
44 * File input
45 *	These functions read input files.
46 *	On SVR4 and newer systems use mmap(2).  On older systems (or on
47 *	file systems that don't support mmap, this code simulates mmap.
48 *	When reading a file, enough memory is allocated to hold the file's
49 *	image, and reads are delayed.  When another part of the library
50 *	wants to use a part of the file, it "fetches" the needed regions.
51 *
52 *	An elf descriptor has a bit array to manage this.  Each bit
53 *	represents one "page" of the file.  Pages are grouped into regions.
54 *	The page size is tunable.  Its value should be at least one disk
55 *	block and small enough to avoid superfluous traffic.
56 *
57 *	NBITS	The number of bits in an unsigned.  Each unsigned object
58 *		holds a "REGION."  A byte must have at least 8 bits;
59 *		it may have more, though the extra bits at the top of
60 *		the unsigned will be unused.  Thus, for 9-bit bytes and
61 *		36-bit words, 4 bits at the top will stay empty.
62 *
63 *	This mechanism gives significant performance gains for library
64 *	handling (among other things), because programs typically don't
65 *	need to look at entire libraries.  The fastest I/O is no I/O.
66 */
67
68/*
69 * This global is used to hold the value of the PAGESIZE macro.
70 *
71 * This is because the PAGESIZE macro actually calls the
72 * sysconfig(_CONFIG_PAGESIZE) system call and we don't want
73 * to repeatedly call this through out libelf.
74 */
75static unsigned long	_elf_pagesize = 0;
76NOTE(SCHEME_PROTECTS_DATA("read only data", _elf_pagesize))
77
78
79#define	NBITS		(8 * sizeof (unsigned))
80#define	REGSZ		(NBITS * _elf_pagesize)
81#define	PGNUM(off)	((off % REGSZ) / _elf_pagesize)
82#define	REGNUM(off)	(off / REGSZ)
83
84
85
86Okay
87_elf_vm(Elf * elf, size_t base, size_t sz)
88{
89	NOTE(ASSUMING_PROTECTED(*elf))
90	register unsigned	*hdreg, hdbit;
91	unsigned		*tlreg, tlbit;
92	size_t			tail;
93	off_t			off;
94	Elf_Void		*iop;
95
96
97	/*
98	 * always validate region
99	 */
100
101	if ((base + sz) > elf->ed_fsz) {
102		/*
103		 * range outside of file bounds.
104		 */
105		_elf_seterr(EFMT_VM, 0);
106		return (OK_NO);
107	}
108
109	/*
110	 * If file is mmap()'d and/or the read size is 0
111	 * their is nothing else for us to do.
112	 */
113	if (elf->ed_vm == 0 || sz == 0)
114		return (OK_YES);
115	/*
116	 * This uses arithmetic instead of masking because
117	 * sizeof (unsigned) might not be a power of 2.
118	 *
119	 * Tail gives one beyond the last offset that must be retrieved,
120	 * NOT the last in the region.
121	 */
122
123	if (elf->ed_parent && elf->ed_parent->ed_fd == -1)
124		elf->ed_fd = -1;
125
126	base += elf->ed_baseoff;
127	tail = base + sz + _elf_pagesize - 1;
128	off = base - base % _elf_pagesize;
129	hdbit = 1 << PGNUM(base);
130	tlbit = 1 << PGNUM(tail);
131	hdreg = &elf->ed_vm[REGNUM(base)];
132	tlreg = &elf->ed_vm[REGNUM(tail)];
133	sz = 0;
134
135	/*
136	 * Scan through the files 'page table' and make sure
137	 * that all of the pages in the specified range have been
138	 * loaded into memory.  As the pages are loaded the appropriate
139	 * bit in the 'page table' is set.
140	 *
141	 * Note: This loop will only read in those pages which havn't
142	 *	 been previously loaded into memory, if the page is
143	 *	 already present it will not be re-loaded.
144	 */
145	while ((hdreg != tlreg) || (hdbit != tlbit)) {
146		if (*hdreg & hdbit) {
147			if (sz != 0) {
148				/*
149				 * Read in a 'chunk' of the elf image.
150				 */
151				iop = (Elf_Void *)(elf->ed_image + off);
152				/*
153				 * do not read past the end of the file
154				 */
155				if (elf->ed_imagesz - off < sz)
156					sz = elf->ed_imagesz - off;
157				if ((lseek(elf->ed_fd, off,
158				    SEEK_SET) != off) ||
159				    (read(elf->ed_fd, iop, sz) != sz)) {
160					_elf_seterr(EIO_VM, errno);
161					return (OK_NO);
162				}
163				off += sz;
164				sz = 0;
165			}
166			off += _elf_pagesize;
167		} else {
168			if (elf->ed_fd < 0) {
169				_elf_seterr(EREQ_NOFD, 0);
170				return (OK_NO);
171			}
172			sz += _elf_pagesize;
173			*hdreg |= hdbit;
174		}
175		if (hdbit == ((unsigned)1 << (NBITS - 1))) {
176			hdbit = 1;
177			++hdreg;
178		} else
179			hdbit <<= 1;
180	}
181
182	if (sz != 0) {
183		iop = (Elf_Void *)(elf->ed_image + off);
184		/*
185		 * do not read past the end of the file
186		 */
187		if ((elf->ed_imagesz - off) < sz)
188			sz = elf->ed_imagesz - off;
189		if ((lseek(elf->ed_fd, off, SEEK_SET) != off) ||
190		    (read(elf->ed_fd, iop, sz) != sz)) {
191			_elf_seterr(EIO_VM, errno);
192			return (OK_NO);
193		}
194	}
195	return (OK_YES);
196}
197
198
199Okay
200_elf_inmap(Elf * elf)
201{
202	int		fd = elf->ed_fd;
203	register size_t	sz;
204
205	{
206		register off_t	off = lseek(fd, (off_t)0, SEEK_END);
207
208		if (off == 0)
209			return (OK_YES);
210
211		if (off == -1) {
212			_elf_seterr(EIO_FSZ, errno);
213			return (OK_NO);
214		}
215
216		if ((sz = (size_t)off) != off) {
217			_elf_seterr(EIO_FBIG, 0);
218			return (OK_NO);
219		}
220	}
221	/*
222	 *	If the file is mapped, elf->ed_vm will stay null
223	 *	and elf->ed_image will need to be unmapped someday.
224	 *	If the file is read, elf->ed_vm and the file image
225	 *	are allocated together; free() elf->ed_vm.
226	 *
227	 *	If the file can be written, disallow mmap.
228	 *	Otherwise, the input mapping and the output mapping
229	 *	can collide.  Moreover, elf_update will truncate
230	 *	the file, possibly invalidating the input mapping.
231	 *	Disallowing input mmap forces the library to malloc
232	 *	and read the space, which will make output mmap safe.
233	 *	Using mmap for output reduces the swap space needed
234	 *	for the process, so that is given preference.
235	 */
236
237	{
238		register char	*p;
239
240		/* The embedded build won't let us reprotect this memory with write
241		 * permissions later unless we give it write permissions now
242		 */
243		if ((elf->ed_myflags & EDF_WRITE) == 0 &&
244		    (p = mmap((char *)0, sz, PROT_READ|PROT_WRITE,
245		    MAP_PRIVATE, fd, (off_t)0)) != (char *)-1) {
246			elf->ed_image = elf->ed_ident = p;
247			elf->ed_imagesz = elf->ed_fsz = elf->ed_identsz = sz;
248			return (OK_YES);
249		}
250	}
251
252	if (_elf_pagesize == 0)
253		_elf_pagesize = PAGESIZE;
254
255	/*
256	 * If mmap fails, try read.  Some file systems don't mmap
257	 */
258	{
259		register size_t	vmsz = sizeof (unsigned) * (REGNUM(sz) + 1);
260
261		if (vmsz % sizeof (Elf64) != 0)
262			vmsz += sizeof (Elf64) - vmsz % sizeof (Elf64);
263		if ((elf->ed_vm = (unsigned *)malloc(vmsz + sz)) == 0) {
264			_elf_seterr(EMEM_VM, errno);
265			return (OK_NO);
266		}
267		(void) memset(elf->ed_vm, 0, vmsz);
268		elf->ed_vmsz = vmsz / sizeof (unsigned);
269		elf->ed_image = elf->ed_ident = (char *)elf->ed_vm + vmsz;
270		elf->ed_imagesz = elf->ed_fsz = elf->ed_identsz = sz;
271	}
272	return (_elf_vm(elf, (size_t)0, (size_t)1));
273}
274
275
276void
277_elf_unmap(char * p, size_t sz)
278{
279	if (p == 0 || sz == 0)
280		return;
281	(void) munmap(p, sz);
282}
283