xref: /illumos-gate/usr/src/cmd/sgs/libelf/common/input.c (revision 257873cfc1dd3337766407f80397db60a56f2f5a)
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	"%Z%%M%	%I%	%E% 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 
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  */
75 static unsigned long	_elf_pagesize = 0;
76 NOTE(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 
86 Okay
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 
199 Okay
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 		if ((elf->ed_myflags & EDF_WRITE) == 0 &&
241 		    (p = mmap((char *)0, sz, PROT_READ,
242 		    MAP_PRIVATE, fd, (off_t)0)) != (char *)-1) {
243 			elf->ed_image = elf->ed_ident = p;
244 			elf->ed_imagesz = elf->ed_fsz = elf->ed_identsz = sz;
245 			return (OK_YES);
246 		}
247 	}
248 
249 	if (_elf_pagesize == 0)
250 		_elf_pagesize = PAGESIZE;
251 
252 	/*
253 	 * If mmap fails, try read.  Some file systems don't mmap
254 	 */
255 	{
256 		register size_t	vmsz = sizeof (unsigned) * (REGNUM(sz) + 1);
257 
258 		if (vmsz % sizeof (Elf64) != 0)
259 			vmsz += sizeof (Elf64) - vmsz % sizeof (Elf64);
260 		if ((elf->ed_vm = (unsigned *)malloc(vmsz + sz)) == 0) {
261 			_elf_seterr(EMEM_VM, errno);
262 			return (OK_NO);
263 		}
264 		(void) memset(elf->ed_vm, 0, vmsz);
265 		elf->ed_vmsz = vmsz / sizeof (unsigned);
266 		elf->ed_image = elf->ed_ident = (char *)elf->ed_vm + vmsz;
267 		elf->ed_imagesz = elf->ed_fsz = elf->ed_identsz = sz;
268 	}
269 	return (_elf_vm(elf, (size_t)0, (size_t)1));
270 }
271 
272 
273 void
274 _elf_unmap(char *p, size_t sz)
275 {
276 	if (p == 0 || sz == 0)
277 		return;
278 	(void) munmap(p, sz);
279 }
280