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