1 /* $NetBSD: libelf_open.c,v 1.5 2024/03/03 17:37:34 christos 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 __RCSID("$NetBSD: libelf_open.c,v 1.5 2024/03/03 17:37:34 christos Exp $");
50 ELFTC_VCSID("Id: libelf_open.c 3977 2022-05-01 06:45:34Z jkoshy");
51
52 #define _LIBELF_INITSIZE (64*1024)
53
54 /*
55 * Read from a device file, pipe or socket.
56 */
57 static void *
_libelf_read_special_file(int fd,size_t * fsz)58 _libelf_read_special_file(int fd, size_t *fsz)
59 {
60 ssize_t readsz;
61 size_t bufsz, datasz;
62 unsigned char *buf, *t;
63
64 datasz = 0;
65 readsz = 0;
66 bufsz = _LIBELF_INITSIZE;
67 if ((buf = malloc(bufsz)) == NULL)
68 goto resourceerror;
69
70 /*
71 * Read data from the file descriptor till we reach EOF, or
72 * till an error is encountered.
73 */
74 do {
75 /* Check if we need to expand the data buffer. */
76 if (datasz == bufsz) {
77 bufsz *= 2;
78 if ((t = realloc(buf, bufsz)) == NULL)
79 goto resourceerror;
80 buf = t;
81 }
82
83 do {
84 assert(bufsz - datasz > 0);
85 t = buf + datasz;
86 if ((readsz = read(fd, t, bufsz - datasz)) <= 0)
87 break;
88 datasz += (size_t) readsz;
89 } while (datasz < bufsz);
90
91 } while (readsz > 0);
92
93 if (readsz < 0) {
94 LIBELF_SET_ERROR(IO, errno);
95 goto error;
96 }
97
98 assert(readsz == 0);
99
100 /*
101 * Free up extra buffer space.
102 */
103 if (bufsz > datasz) {
104 if (datasz > 0) {
105 if ((t = realloc(buf, datasz)) == NULL)
106 goto resourceerror;
107 buf = t;
108 } else { /* Zero bytes read. */
109 LIBELF_SET_ERROR(ARGUMENT, 0);
110 free(buf);
111 buf = NULL;
112 }
113 }
114
115 *fsz = datasz;
116 return (buf);
117
118 resourceerror:
119 LIBELF_SET_ERROR(RESOURCE, 0);
120 error:
121 if (buf != NULL)
122 free(buf);
123 return (NULL);
124 }
125
126 /*
127 * Read the contents of the file referenced by the file descriptor
128 * 'fd'.
129 */
130
131 Elf *
_libelf_open_object(int fd,Elf_Cmd c,int reporterror)132 _libelf_open_object(int fd, Elf_Cmd c, int reporterror)
133 {
134 Elf *e;
135 void *m;
136 mode_t mode;
137 size_t fsize;
138 struct stat sb;
139 unsigned int flags;
140
141 assert(c == ELF_C_READ || c == ELF_C_RDWR || c == ELF_C_WRITE);
142
143 if (fstat(fd, &sb) < 0) {
144 LIBELF_SET_ERROR(IO, errno);
145 return (NULL);
146 }
147
148 mode = sb.st_mode;
149 fsize = (size_t) sb.st_size;
150
151 /*
152 * Reject unsupported file types.
153 */
154 if (!S_ISREG(mode) && !S_ISCHR(mode) && !S_ISFIFO(mode) &&
155 !S_ISSOCK(mode)) {
156 LIBELF_SET_ERROR(ARGUMENT, 0);
157 return (NULL);
158 }
159
160 /*
161 * For ELF_C_WRITE mode, allocate and return a descriptor.
162 */
163 if (c == ELF_C_WRITE) {
164 if ((e = _libelf_allocate_elf()) != NULL) {
165 _libelf_init_elf(e, ELF_K_ELF);
166 e->e_byteorder = LIBELF_PRIVATE(byteorder);
167 e->e_fd = fd;
168 e->e_cmd = c;
169 if (!S_ISREG(mode))
170 e->e_flags |= LIBELF_F_SPECIAL_FILE;
171 }
172
173 return (e);
174 }
175
176
177 /*
178 * ELF_C_READ and ELF_C_RDWR mode.
179 */
180 m = NULL;
181 flags = 0;
182 if (S_ISREG(mode)) {
183
184 /*
185 * Reject zero length files.
186 */
187 if (fsize == 0) {
188 LIBELF_SET_ERROR(ARGUMENT, 0);
189 return (NULL);
190 }
191
192 #if ELFTC_HAVE_MMAP
193 /*
194 * Always map regular files in with 'PROT_READ'
195 * permissions.
196 *
197 * For objects opened in ELF_C_RDWR mode, when
198 * elf_update(3) is called, we remove this mapping,
199 * write file data out using write(2), and map the new
200 * contents back.
201 */
202 m = mmap(NULL, fsize, PROT_READ, MAP_PRIVATE, fd, (off_t) 0);
203
204 if (m == MAP_FAILED)
205 m = NULL;
206 else
207 flags = LIBELF_F_RAWFILE_MMAP;
208 #endif
209
210 /*
211 * Fallback to a read() if the call to mmap() failed,
212 * or if mmap() is not available.
213 */
214 if (m == NULL) {
215 if ((m = malloc(fsize)) == NULL) {
216 LIBELF_SET_ERROR(RESOURCE, 0);
217 return (NULL);
218 }
219
220 if (read(fd, m, fsize) != (ssize_t) fsize) {
221 LIBELF_SET_ERROR(IO, errno);
222 free(m);
223 return (NULL);
224 }
225
226 flags = LIBELF_F_RAWFILE_MALLOC;
227 }
228 } else if ((m = _libelf_read_special_file(fd, &fsize)) != NULL)
229 flags = LIBELF_F_RAWFILE_MALLOC | LIBELF_F_SPECIAL_FILE;
230 else
231 return (NULL);
232
233 if ((e = _libelf_memory(m, fsize, reporterror)) == NULL) {
234 assert((flags & LIBELF_F_RAWFILE_MALLOC) ||
235 (flags & LIBELF_F_RAWFILE_MMAP));
236 if (flags & LIBELF_F_RAWFILE_MALLOC)
237 free(m);
238 #if ELFTC_HAVE_MMAP
239 else
240 (void) munmap(m, fsize);
241 #endif
242 return (NULL);
243 }
244
245 /* ar(1) archives aren't supported in RDWR mode. */
246 if (c == ELF_C_RDWR && e->e_kind == ELF_K_AR) {
247 (void) elf_end(e);
248 LIBELF_SET_ERROR(ARGUMENT, 0);
249 return (NULL);
250 }
251
252 e->e_flags |= flags;
253 e->e_fd = fd;
254 e->e_cmd = c;
255
256 return (e);
257 }
258