/ external / libelf / libelf_ehdr.c
libelf_ehdr.c
  1  /*-
  2   * Copyright (c) 2006,2008 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/cdefs.h>
 28  
 29  #include <assert.h>
 30  #include <gelf.h>
 31  #include <libelf.h>
 32  #include <stdlib.h>
 33  
 34  #include "_libelf.h"
 35  
 36  ELFTC_VCSID("$Id$");
 37  
 38  /*
 39   * Retrieve counts for sections, phdrs and the section string table index
 40   * from section header #0 of the ELF object.
 41   */
 42  static int
 43  _libelf_load_extended(Elf *e, int ec, uint64_t shoff, uint16_t phnum,
 44      uint16_t strndx)
 45  {
 46  	Elf_Scn *scn;
 47  	size_t fsz;
 48  	int (*xlator)(unsigned char *_d, size_t _dsz, unsigned char *_s,
 49  	    size_t _c, int _swap);
 50  	uint32_t shtype;
 51  
 52  	assert(STAILQ_EMPTY(&e->e_u.e_elf.e_scn));
 53  
 54  	fsz = _libelf_fsize(ELF_T_SHDR, ec, e->e_version, 1);
 55  	assert(fsz > 0);
 56  
 57  	if (e->e_rawsize < shoff + fsz) { /* raw file too small */
 58  		LIBELF_SET_ERROR(HEADER, 0);
 59  		return (0);
 60  	}
 61  
 62  	if ((scn = _libelf_allocate_scn(e, (size_t) 0)) == NULL)
 63  		return (0);
 64  
 65  	xlator = _libelf_get_translator(ELF_T_SHDR, ELF_TOMEMORY, ec);
 66  	(*xlator)((unsigned char *) &scn->s_shdr, sizeof(scn->s_shdr),
 67  	    (unsigned char *) e->e_rawfile + shoff, (size_t) 1,
 68  	    e->e_byteorder != LIBELF_PRIVATE(byteorder));
 69  
 70  #define	GET_SHDR_MEMBER(M) ((ec == ELFCLASS32) ? scn->s_shdr.s_shdr32.M : \
 71  		scn->s_shdr.s_shdr64.M)
 72  
 73  	if ((shtype = GET_SHDR_MEMBER(sh_type)) != SHT_NULL) {
 74  		LIBELF_SET_ERROR(SECTION, 0);
 75  		return (0);
 76  	}
 77  
 78  	e->e_u.e_elf.e_nscn = (size_t) GET_SHDR_MEMBER(sh_size);
 79  	e->e_u.e_elf.e_nphdr = (phnum != PN_XNUM) ? phnum :
 80  	    GET_SHDR_MEMBER(sh_info);
 81  	e->e_u.e_elf.e_strndx = (strndx != SHN_XINDEX) ? strndx :
 82  	    GET_SHDR_MEMBER(sh_link);
 83  #undef	GET_SHDR_MEMBER
 84  
 85  	return (1);
 86  }
 87  
 88  #define	EHDR_INIT(E,SZ)	 do {						\
 89  		Elf##SZ##_Ehdr *eh = (E);				\
 90  		eh->e_ident[EI_MAG0] = ELFMAG0;				\
 91  		eh->e_ident[EI_MAG1] = ELFMAG1;				\
 92  		eh->e_ident[EI_MAG2] = ELFMAG2;				\
 93  		eh->e_ident[EI_MAG3] = ELFMAG3;				\
 94  		eh->e_ident[EI_CLASS] = ELFCLASS##SZ;			\
 95  		eh->e_ident[EI_DATA]  = ELFDATANONE;			\
 96  		eh->e_ident[EI_VERSION] = LIBELF_PRIVATE(version) & 0xFFU; \
 97  		eh->e_machine = EM_NONE;				\
 98  		eh->e_type    = ELF_K_NONE;				\
 99  		eh->e_version = LIBELF_PRIVATE(version);		\
100  	} while (0)
101  
102  void *
103  _libelf_ehdr(Elf *e, int ec, int allocate)
104  {
105  	void *ehdr;
106  	size_t fsz, msz;
107  	uint16_t phnum, shnum, strndx;
108  	uint64_t shoff;
109  	int (*xlator)(unsigned char *_d, size_t _dsz, unsigned char *_s,
110  	    size_t _c, int _swap);
111  
112  	assert(ec == ELFCLASS32 || ec == ELFCLASS64);
113  
114  	if (e == NULL || e->e_kind != ELF_K_ELF) {
115  		LIBELF_SET_ERROR(ARGUMENT, 0);
116  		return (NULL);
117  	}
118  
119  	if (e->e_class != ELFCLASSNONE && e->e_class != ec) {
120  		LIBELF_SET_ERROR(CLASS, 0);
121  		return (NULL);
122  	}
123  
124  	if (e->e_version != EV_CURRENT) {
125  		LIBELF_SET_ERROR(VERSION, 0);
126  		return (NULL);
127  	}
128  
129  	if (e->e_class == ELFCLASSNONE)
130  		e->e_class = ec;
131  
132  	if (ec == ELFCLASS32)
133  		ehdr = (void *) e->e_u.e_elf.e_ehdr.e_ehdr32;
134  	else
135  		ehdr = (void *) e->e_u.e_elf.e_ehdr.e_ehdr64;
136  
137  	if (ehdr != NULL)	/* already have a translated ehdr */
138  		return (ehdr);
139  
140  	fsz = _libelf_fsize(ELF_T_EHDR, ec, e->e_version, (size_t) 1);
141  	assert(fsz > 0);
142  
143  	if (e->e_cmd != ELF_C_WRITE && e->e_rawsize < fsz) {
144  		LIBELF_SET_ERROR(HEADER, 0);
145  		return (NULL);
146  	}
147  
148  	msz = _libelf_msize(ELF_T_EHDR, ec, EV_CURRENT);
149  
150  	assert(msz > 0);
151  
152  	if ((ehdr = calloc((size_t) 1, msz)) == NULL) {
153  		LIBELF_SET_ERROR(RESOURCE, 0);
154  		return (NULL);
155  	}
156  
157  	if (ec == ELFCLASS32) {
158  		e->e_u.e_elf.e_ehdr.e_ehdr32 = ehdr;
159  		EHDR_INIT(ehdr,32);
160  	} else {
161  		e->e_u.e_elf.e_ehdr.e_ehdr64 = ehdr;
162  		EHDR_INIT(ehdr,64);
163  	}
164  
165  	if (allocate)
166  		e->e_flags |= ELF_F_DIRTY;
167  
168  	if (e->e_cmd == ELF_C_WRITE)
169  		return (ehdr);
170  
171  	xlator = _libelf_get_translator(ELF_T_EHDR, ELF_TOMEMORY, ec);
172  	(*xlator)((unsigned char*) ehdr, msz, e->e_rawfile, (size_t) 1,
173  	    e->e_byteorder != LIBELF_PRIVATE(byteorder));
174  
175  	/*
176  	 * If extended numbering is being used, read the correct
177  	 * number of sections and program header entries.
178  	 */
179  	if (ec == ELFCLASS32) {
180  		phnum = ((Elf32_Ehdr *) ehdr)->e_phnum;
181  		shnum = ((Elf32_Ehdr *) ehdr)->e_shnum;
182  		shoff = ((Elf32_Ehdr *) ehdr)->e_shoff;
183  		strndx = ((Elf32_Ehdr *) ehdr)->e_shstrndx;
184  	} else {
185  		phnum = ((Elf64_Ehdr *) ehdr)->e_phnum;
186  		shnum = ((Elf64_Ehdr *) ehdr)->e_shnum;
187  		shoff = ((Elf64_Ehdr *) ehdr)->e_shoff;
188  		strndx = ((Elf64_Ehdr *) ehdr)->e_shstrndx;
189  	}
190  
191  	if (shnum >= SHN_LORESERVE ||
192  	    (shoff == 0LL && (shnum != 0 || phnum == PN_XNUM ||
193  		strndx == SHN_XINDEX))) {
194  		LIBELF_SET_ERROR(HEADER, 0);
195  		return (NULL);
196  	}
197  
198  	if (shnum != 0 || shoff == 0LL) { /* not using extended numbering */
199  		e->e_u.e_elf.e_nphdr = phnum;
200  		e->e_u.e_elf.e_nscn = shnum;
201  		e->e_u.e_elf.e_strndx = strndx;
202  	} else if (_libelf_load_extended(e, ec, shoff, phnum, strndx) == 0)
203  		return (NULL);
204  
205  	return (ehdr);
206  }