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