Home | History | Annotate | Line # | Download | only in libelf
      1 /*	$NetBSD: libelf_open.c,v 1.6 2025/12/25 18:58:13 jkoshy Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2006,2008-2011 Joseph Koshy
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 
     29 #if HAVE_NBTOOL_CONFIG_H
     30 # include "nbtool_config.h"
     31 #endif
     32 
     33 #include <sys/cdefs.h>
     34 #include <sys/types.h>
     35 #include <sys/stat.h>
     36 
     37 #include <assert.h>
     38 #include <errno.h>
     39 #include <libelf.h>
     40 #include <stdlib.h>
     41 #include <unistd.h>
     42 
     43 #include "_libelf.h"
     44 
     45 #if	ELFTC_HAVE_MMAP
     46 #include <sys/mman.h>
     47 #endif
     48 
     49 ELFTC_VCSID("Id: libelf_open.c 4242 2025-10-14 11:59:32Z jkoshy");
     50 
     51 __RCSID("$NetBSD: libelf_open.c,v 1.6 2025/12/25 18:58:13 jkoshy Exp $");
     52 
     53 #define	_LIBELF_INITSIZE	(64*1024)
     54 
     55 /*
     56  * Read from a device file, pipe or socket.
     57  */
     58 static void *
     59 _libelf_read_special_file(int fd, size_t *fsz)
     60 {
     61 	ssize_t readsz;
     62 	size_t bufsz, datasz;
     63 	unsigned char *buf, *t;
     64 
     65 	datasz = 0;
     66 	readsz = 0;
     67 	bufsz = _LIBELF_INITSIZE;
     68 	if ((buf = malloc(bufsz)) == NULL)
     69 		goto resourceerror;
     70 
     71 	/*
     72 	 * Read data from the file descriptor till we reach EOF, or
     73 	 * till an error is encountered.
     74 	 */
     75 	do {
     76 		/* Check if we need to expand the data buffer. */
     77 		if (datasz == bufsz) {
     78 			/*
     79 			 * Be canonically correct and avoid a possible
     80 			 * underflow on doubling 'bufsz'.
     81 			 */
     82 			if (bufsz > SIZE_MAX/2)
     83 				goto resourceerror;
     84 
     85 			bufsz *= 2;
     86 			if ((t = realloc(buf, bufsz)) == NULL)
     87 				goto resourceerror;
     88 			buf = t;
     89 		}
     90 
     91 		do {
     92 			assert(bufsz - datasz > 0);
     93 			t = buf + datasz;
     94 			if ((readsz = read(fd, t, bufsz - datasz)) <= 0)
     95 				break;
     96 			datasz += (size_t) readsz;
     97 		} while (datasz < bufsz);
     98 
     99 	} while (readsz > 0);
    100 
    101 	if (readsz < 0) {
    102 		LIBELF_SET_ERROR(IO, errno);
    103 		goto error;
    104 	}
    105 
    106 	assert(readsz == 0);
    107 
    108 	/*
    109 	 * Free up extra buffer space.
    110 	 */
    111 	if (bufsz > datasz) {
    112 		if (datasz > 0) {
    113 			if ((t = realloc(buf, datasz)) == NULL)
    114 				goto resourceerror;
    115 			buf = t;
    116 		} else {	/* Zero bytes read. */
    117 			LIBELF_SET_ERROR(ARGUMENT, 0);
    118 			free(buf);
    119 			buf = NULL;
    120 		}
    121 	}
    122 
    123 	*fsz = datasz;
    124 	return (buf);
    125 
    126 resourceerror:
    127 	LIBELF_SET_ERROR(RESOURCE, 0);
    128 error:
    129 	if (buf != NULL)
    130 		free(buf);
    131 	return (NULL);
    132 }
    133 
    134 /*
    135  * Read the contents of the file referenced by the file descriptor
    136  * 'fd'.
    137  */
    138 
    139 Elf *
    140 _libelf_open_object(int fd, Elf_Cmd c, int reporterror)
    141 {
    142 	Elf *e;
    143 	void *m;
    144 	mode_t mode;
    145 	size_t fsize;
    146 	struct stat sb;
    147 	unsigned int flags;
    148 
    149 	assert(c == ELF_C_READ || c == ELF_C_RDWR || c == ELF_C_WRITE);
    150 
    151 	if (fd < 0) {
    152 		LIBELF_SET_ERROR(ARGUMENT, 0);
    153 	        return (NULL);
    154 	}
    155 
    156 	if (fstat(fd, &sb) < 0) {
    157 		LIBELF_SET_ERROR(IO, errno);
    158 		return (NULL);
    159 	}
    160 
    161 	mode = sb.st_mode;
    162 	fsize = (size_t) sb.st_size;
    163 
    164 	/*
    165 	 * Reject unsupported file types.
    166 	 */
    167 	if (!S_ISREG(mode) && !S_ISCHR(mode) && !S_ISFIFO(mode) &&
    168 	    !S_ISSOCK(mode)) {
    169 		LIBELF_SET_ERROR(ARGUMENT, 0);
    170 		return (NULL);
    171 	}
    172 
    173 	/*
    174 	 * For ELF_C_WRITE mode, allocate and return a descriptor.
    175 	 */
    176 	if (c == ELF_C_WRITE) {
    177 		if ((e = _libelf_allocate_elf()) != NULL) {
    178 			_libelf_init_elf(e, ELF_K_ELF);
    179 			e->e_byteorder = LIBELF_PRIVATE(byteorder);
    180 			e->e_fd = fd;
    181 			e->e_cmd = c;
    182 			if (!S_ISREG(mode))
    183 				e->e_flags |= LIBELF_F_SPECIAL_FILE;
    184 		}
    185 
    186 		return (e);
    187 	}
    188 
    189 
    190 	/*
    191 	 * ELF_C_READ and ELF_C_RDWR mode.
    192 	 */
    193 	m = NULL;
    194 	flags = 0;
    195 	if (S_ISREG(mode)) {
    196 
    197 		/*
    198 		 * Reject zero length files.
    199 		 */
    200 		if (fsize == 0) {
    201 			LIBELF_SET_ERROR(ARGUMENT, 0);
    202 			return (NULL);
    203 		}
    204 
    205 #if	ELFTC_HAVE_MMAP
    206 		/*
    207 		 * Always map regular files in with 'PROT_READ'
    208 		 * permissions.
    209 		 *
    210 		 * For objects opened in ELF_C_RDWR mode, when
    211 		 * elf_update(3) is called, we remove this mapping,
    212 		 * write file data out using write(2), and map the new
    213 		 * contents back.
    214 		 */
    215 		m = mmap(NULL, fsize, PROT_READ, MAP_PRIVATE, fd, (off_t) 0);
    216 
    217 		if (m == MAP_FAILED)
    218 			m = NULL;
    219 		else
    220 			flags = LIBELF_F_RAWFILE_MMAP;
    221 #endif
    222 
    223 		/*
    224 		 * Fallback to a read() if the call to mmap() failed,
    225 		 * or if mmap() is not available.
    226 		 */
    227 		if (m == NULL) {
    228 			if ((m = malloc(fsize)) == NULL) {
    229 				LIBELF_SET_ERROR(RESOURCE, 0);
    230 				return (NULL);
    231 			}
    232 
    233 			if (read(fd, m, fsize) != (ssize_t) fsize) {
    234 				LIBELF_SET_ERROR(IO, errno);
    235 				free(m);
    236 				return (NULL);
    237 			}
    238 
    239 			flags = LIBELF_F_RAWFILE_MALLOC;
    240 		}
    241 	} else if ((m = _libelf_read_special_file(fd, &fsize)) != NULL)
    242 		flags = LIBELF_F_RAWFILE_MALLOC | LIBELF_F_SPECIAL_FILE;
    243 	else
    244 		return (NULL);
    245 
    246 	if ((e = _libelf_memory(m, fsize, reporterror)) == NULL) {
    247 		assert((flags & LIBELF_F_RAWFILE_MALLOC) ||
    248 		    (flags & LIBELF_F_RAWFILE_MMAP));
    249 		if (flags & LIBELF_F_RAWFILE_MALLOC)
    250 			free(m);
    251 #if	ELFTC_HAVE_MMAP
    252 		else
    253 			(void) munmap(m, fsize);
    254 #endif
    255 		return (NULL);
    256 	}
    257 
    258 	/* ar(1) archives aren't supported in RDWR mode. */
    259 	if (c == ELF_C_RDWR && e->e_kind == ELF_K_AR) {
    260 		(void) elf_end(e);
    261 		LIBELF_SET_ERROR(ARGUMENT, 0);
    262 		return (NULL);
    263 	}
    264 
    265 	e->e_flags |= flags;
    266 	e->e_fd = fd;
    267 	e->e_cmd = c;
    268 
    269 	return (e);
    270 }
    271