xref: /freebsd/contrib/elftoolchain/libelf/libelf_open.c (revision 98e0ffaefb0f241cda3a72395d3be04192ae0d47)
12de3b87aSKai Wang /*-
22de3b87aSKai Wang  * Copyright (c) 2006,2008-2011 Joseph Koshy
32de3b87aSKai Wang  * All rights reserved.
42de3b87aSKai Wang  *
52de3b87aSKai Wang  * Redistribution and use in source and binary forms, with or without
62de3b87aSKai Wang  * modification, are permitted provided that the following conditions
72de3b87aSKai Wang  * are met:
82de3b87aSKai Wang  * 1. Redistributions of source code must retain the above copyright
92de3b87aSKai Wang  *    notice, this list of conditions and the following disclaimer.
102de3b87aSKai Wang  * 2. Redistributions in binary form must reproduce the above copyright
112de3b87aSKai Wang  *    notice, this list of conditions and the following disclaimer in the
122de3b87aSKai Wang  *    documentation and/or other materials provided with the distribution.
132de3b87aSKai Wang  *
142de3b87aSKai Wang  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
152de3b87aSKai Wang  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
162de3b87aSKai Wang  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
172de3b87aSKai Wang  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
182de3b87aSKai Wang  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
192de3b87aSKai Wang  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
202de3b87aSKai Wang  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
212de3b87aSKai Wang  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
222de3b87aSKai Wang  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
232de3b87aSKai Wang  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
242de3b87aSKai Wang  * SUCH DAMAGE.
252de3b87aSKai Wang  */
262de3b87aSKai Wang 
272de3b87aSKai Wang #include <sys/types.h>
282de3b87aSKai Wang #include <sys/stat.h>
292de3b87aSKai Wang 
302de3b87aSKai Wang #include <assert.h>
312de3b87aSKai Wang #include <errno.h>
322de3b87aSKai Wang #include <libelf.h>
332de3b87aSKai Wang #include <stdlib.h>
342de3b87aSKai Wang #include <unistd.h>
352de3b87aSKai Wang 
362de3b87aSKai Wang #include "_libelf.h"
372de3b87aSKai Wang 
382de3b87aSKai Wang #if	ELFTC_HAVE_MMAP
392de3b87aSKai Wang #include <sys/mman.h>
402de3b87aSKai Wang #endif
412de3b87aSKai Wang 
42*cf781b2eSEd Maste ELFTC_VCSID("$Id: libelf_open.c 3007 2014-03-22 08:10:14Z jkoshy $");
432de3b87aSKai Wang 
442de3b87aSKai Wang #define	_LIBELF_INITSIZE	(64*1024)
452de3b87aSKai Wang 
462de3b87aSKai Wang /*
472de3b87aSKai Wang  * Read from a device file, pipe or socket.
482de3b87aSKai Wang  */
492de3b87aSKai Wang static void *
_libelf_read_special_file(int fd,size_t * fsz)502de3b87aSKai Wang _libelf_read_special_file(int fd, size_t *fsz)
512de3b87aSKai Wang {
522de3b87aSKai Wang 	ssize_t readsz;
532de3b87aSKai Wang 	size_t bufsz, datasz;
542de3b87aSKai Wang 	unsigned char *buf, *t;
552de3b87aSKai Wang 
562de3b87aSKai Wang 	datasz = 0;
572de3b87aSKai Wang 	readsz = 0;
582de3b87aSKai Wang 	bufsz = _LIBELF_INITSIZE;
592de3b87aSKai Wang 	if ((buf = malloc(bufsz)) == NULL)
602de3b87aSKai Wang 		goto resourceerror;
612de3b87aSKai Wang 
622de3b87aSKai Wang 	/*
632de3b87aSKai Wang 	 * Read data from the file descriptor till we reach EOF, or
642de3b87aSKai Wang 	 * till an error is encountered.
652de3b87aSKai Wang 	 */
662de3b87aSKai Wang 	do {
672de3b87aSKai Wang 		/* Check if we need to expand the data buffer. */
682de3b87aSKai Wang 		if (datasz == bufsz) {
692de3b87aSKai Wang 			bufsz *= 2;
702de3b87aSKai Wang 			if ((t = realloc(buf, bufsz)) == NULL)
712de3b87aSKai Wang 				goto resourceerror;
722de3b87aSKai Wang 			buf = t;
732de3b87aSKai Wang 		}
742de3b87aSKai Wang 
752de3b87aSKai Wang 		do {
76*cf781b2eSEd Maste 			assert(bufsz - datasz > 0);
772de3b87aSKai Wang 			t = buf + datasz;
78*cf781b2eSEd Maste 			if ((readsz = read(fd, t, bufsz - datasz)) <= 0)
792de3b87aSKai Wang 				break;
80*cf781b2eSEd Maste 			datasz += (size_t) readsz;
812de3b87aSKai Wang 		} while (datasz < bufsz);
822de3b87aSKai Wang 
832de3b87aSKai Wang 	} while (readsz > 0);
842de3b87aSKai Wang 
852de3b87aSKai Wang 	if (readsz < 0) {
862de3b87aSKai Wang 		LIBELF_SET_ERROR(IO, errno);
872de3b87aSKai Wang 		goto error;
882de3b87aSKai Wang 	}
892de3b87aSKai Wang 
902de3b87aSKai Wang 	assert(readsz == 0);
912de3b87aSKai Wang 
922de3b87aSKai Wang 	/*
932de3b87aSKai Wang 	 * Free up extra buffer space.
942de3b87aSKai Wang 	 */
952de3b87aSKai Wang 	if (bufsz > datasz) {
962de3b87aSKai Wang 		if (datasz > 0) {
972de3b87aSKai Wang 			if ((t = realloc(buf, datasz)) == NULL)
982de3b87aSKai Wang 				goto resourceerror;
992de3b87aSKai Wang 			buf = t;
1002de3b87aSKai Wang 		} else {	/* Zero bytes read. */
1012de3b87aSKai Wang 			LIBELF_SET_ERROR(ARGUMENT, 0);
1022de3b87aSKai Wang 			free(buf);
1032de3b87aSKai Wang 			buf = NULL;
1042de3b87aSKai Wang 		}
1052de3b87aSKai Wang 	}
1062de3b87aSKai Wang 
1072de3b87aSKai Wang 	*fsz = datasz;
1082de3b87aSKai Wang 	return (buf);
1092de3b87aSKai Wang 
1102de3b87aSKai Wang resourceerror:
1112de3b87aSKai Wang 	LIBELF_SET_ERROR(RESOURCE, 0);
1122de3b87aSKai Wang error:
1132de3b87aSKai Wang 	if (buf != NULL)
1142de3b87aSKai Wang 		free(buf);
1152de3b87aSKai Wang 	return (NULL);
1162de3b87aSKai Wang }
1172de3b87aSKai Wang 
1182de3b87aSKai Wang /*
1192de3b87aSKai Wang  * Read the contents of the file referenced by the file descriptor
1202de3b87aSKai Wang  * 'fd'.
1212de3b87aSKai Wang  */
1222de3b87aSKai Wang 
1232de3b87aSKai Wang Elf *
_libelf_open_object(int fd,Elf_Cmd c,int reporterror)1242de3b87aSKai Wang _libelf_open_object(int fd, Elf_Cmd c, int reporterror)
1252de3b87aSKai Wang {
1262de3b87aSKai Wang 	Elf *e;
1272de3b87aSKai Wang 	void *m;
1282de3b87aSKai Wang 	mode_t mode;
1292de3b87aSKai Wang 	size_t fsize;
1302de3b87aSKai Wang 	struct stat sb;
1312de3b87aSKai Wang 	unsigned int flags;
1322de3b87aSKai Wang 
1332de3b87aSKai Wang 	assert(c == ELF_C_READ || c == ELF_C_RDWR || c == ELF_C_WRITE);
1342de3b87aSKai Wang 
1352de3b87aSKai Wang 	if (fstat(fd, &sb) < 0) {
1362de3b87aSKai Wang 		LIBELF_SET_ERROR(IO, errno);
1372de3b87aSKai Wang 		return (NULL);
1382de3b87aSKai Wang 	}
1392de3b87aSKai Wang 
1402de3b87aSKai Wang 	mode = sb.st_mode;
1412de3b87aSKai Wang 	fsize = (size_t) sb.st_size;
1422de3b87aSKai Wang 
1432de3b87aSKai Wang 	/*
1442de3b87aSKai Wang 	 * Reject unsupported file types.
1452de3b87aSKai Wang 	 */
1462de3b87aSKai Wang 	if (!S_ISREG(mode) && !S_ISCHR(mode) && !S_ISFIFO(mode) &&
1472de3b87aSKai Wang 	    !S_ISSOCK(mode)) {
1482de3b87aSKai Wang 		LIBELF_SET_ERROR(ARGUMENT, 0);
1492de3b87aSKai Wang 		return (NULL);
1502de3b87aSKai Wang 	}
1512de3b87aSKai Wang 
1522de3b87aSKai Wang 	/*
1532de3b87aSKai Wang 	 * For ELF_C_WRITE mode, allocate and return a descriptor.
1542de3b87aSKai Wang 	 */
1552de3b87aSKai Wang 	if (c == ELF_C_WRITE) {
1562de3b87aSKai Wang 		if ((e = _libelf_allocate_elf()) != NULL) {
1572de3b87aSKai Wang 			_libelf_init_elf(e, ELF_K_ELF);
1582de3b87aSKai Wang 			e->e_byteorder = LIBELF_PRIVATE(byteorder);
1592de3b87aSKai Wang 			e->e_fd = fd;
1602de3b87aSKai Wang 			e->e_cmd = c;
1612de3b87aSKai Wang 			if (!S_ISREG(mode))
1622de3b87aSKai Wang 				e->e_flags |= LIBELF_F_SPECIAL_FILE;
1632de3b87aSKai Wang 		}
1642de3b87aSKai Wang 
1652de3b87aSKai Wang 		return (e);
1662de3b87aSKai Wang 	}
1672de3b87aSKai Wang 
1682de3b87aSKai Wang 
1692de3b87aSKai Wang 	/*
1702de3b87aSKai Wang 	 * ELF_C_READ and ELF_C_RDWR mode.
1712de3b87aSKai Wang 	 */
1722de3b87aSKai Wang 	m = NULL;
1732de3b87aSKai Wang 	flags = 0;
1742de3b87aSKai Wang 	if (S_ISREG(mode)) {
1752de3b87aSKai Wang 
1762de3b87aSKai Wang 		/*
1772de3b87aSKai Wang 		 * Reject zero length files.
1782de3b87aSKai Wang 		 */
1792de3b87aSKai Wang 		if (fsize == 0) {
1802de3b87aSKai Wang 			LIBELF_SET_ERROR(ARGUMENT, 0);
1812de3b87aSKai Wang 			return (NULL);
1822de3b87aSKai Wang 		}
1832de3b87aSKai Wang 
1842de3b87aSKai Wang #if	ELFTC_HAVE_MMAP
1852de3b87aSKai Wang 		/*
1862de3b87aSKai Wang 		 * Always map regular files in with 'PROT_READ'
1872de3b87aSKai Wang 		 * permissions.
1882de3b87aSKai Wang 		 *
1892de3b87aSKai Wang 		 * For objects opened in ELF_C_RDWR mode, when
1902de3b87aSKai Wang 		 * elf_update(3) is called, we remove this mapping,
1912de3b87aSKai Wang 		 * write file data out using write(2), and map the new
1922de3b87aSKai Wang 		 * contents back.
1932de3b87aSKai Wang 		 */
1942de3b87aSKai Wang 		m = mmap(NULL, fsize, PROT_READ, MAP_PRIVATE, fd, (off_t) 0);
1952de3b87aSKai Wang 
1962de3b87aSKai Wang 		if (m == MAP_FAILED)
1972de3b87aSKai Wang 			m = NULL;
1982de3b87aSKai Wang 		else
1992de3b87aSKai Wang 			flags = LIBELF_F_RAWFILE_MMAP;
2002de3b87aSKai Wang #endif
2012de3b87aSKai Wang 
2022de3b87aSKai Wang 		/*
2032de3b87aSKai Wang 		 * Fallback to a read() if the call to mmap() failed,
2042de3b87aSKai Wang 		 * or if mmap() is not available.
2052de3b87aSKai Wang 		 */
2062de3b87aSKai Wang 		if (m == NULL) {
2072de3b87aSKai Wang 			if ((m = malloc(fsize)) == NULL) {
2082de3b87aSKai Wang 				LIBELF_SET_ERROR(RESOURCE, 0);
2092de3b87aSKai Wang 				return (NULL);
2102de3b87aSKai Wang 			}
2112de3b87aSKai Wang 
2122de3b87aSKai Wang 			if (read(fd, m, fsize) != (ssize_t) fsize) {
2132de3b87aSKai Wang 				LIBELF_SET_ERROR(IO, errno);
2142de3b87aSKai Wang 				free(m);
2152de3b87aSKai Wang 				return (NULL);
2162de3b87aSKai Wang 			}
2172de3b87aSKai Wang 
2182de3b87aSKai Wang 			flags = LIBELF_F_RAWFILE_MALLOC;
2192de3b87aSKai Wang 		}
2202de3b87aSKai Wang 	} else if ((m = _libelf_read_special_file(fd, &fsize)) != NULL)
2212de3b87aSKai Wang 		flags = LIBELF_F_RAWFILE_MALLOC | LIBELF_F_SPECIAL_FILE;
2222de3b87aSKai Wang 	else
2232de3b87aSKai Wang 		return (NULL);
2242de3b87aSKai Wang 
2252de3b87aSKai Wang 	if ((e = _libelf_memory(m, fsize, reporterror)) == NULL) {
2262de3b87aSKai Wang 		assert((flags & LIBELF_F_RAWFILE_MALLOC) ||
2272de3b87aSKai Wang 		    (flags & LIBELF_F_RAWFILE_MMAP));
2282de3b87aSKai Wang 		if (flags & LIBELF_F_RAWFILE_MALLOC)
2292de3b87aSKai Wang 			free(m);
2302de3b87aSKai Wang #if	ELFTC_HAVE_MMAP
2312de3b87aSKai Wang 		else
2322de3b87aSKai Wang 			(void) munmap(m, fsize);
2332de3b87aSKai Wang #endif
2342de3b87aSKai Wang 		return (NULL);
2352de3b87aSKai Wang 	}
2362de3b87aSKai Wang 
2372de3b87aSKai Wang 	/* ar(1) archives aren't supported in RDWR mode. */
2382de3b87aSKai Wang 	if (c == ELF_C_RDWR && e->e_kind == ELF_K_AR) {
2392de3b87aSKai Wang 		(void) elf_end(e);
2402de3b87aSKai Wang 		LIBELF_SET_ERROR(ARGUMENT, 0);
2412de3b87aSKai Wang 		return (NULL);
2422de3b87aSKai Wang 	}
2432de3b87aSKai Wang 
2442de3b87aSKai Wang 	e->e_flags |= flags;
2452de3b87aSKai Wang 	e->e_fd = fd;
2462de3b87aSKai Wang 	e->e_cmd = c;
2472de3b87aSKai Wang 
2482de3b87aSKai Wang 	return (e);
2492de3b87aSKai Wang }
250