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