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