diff options
author | Elliott Hughes <enh@google.com> | 2015-02-17 20:56:04 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2015-02-17 20:56:04 +0000 |
commit | b48bfdc826ebdd316d5d8a4ff8f213391f1c710b (patch) | |
tree | 961dfef28571e02a7d969fbba9bb51991086476b /src/libelf/elf_getdata.c | |
parent | 5cb12756f3876faa52b0d4491745fc4c7adbab00 (diff) | |
parent | 41f677100e15f315f638774fbd055e497fc90069 (diff) | |
download | android_external_elfutils-b48bfdc826ebdd316d5d8a4ff8f213391f1c710b.tar.gz android_external_elfutils-b48bfdc826ebdd316d5d8a4ff8f213391f1c710b.tar.bz2 android_external_elfutils-b48bfdc826ebdd316d5d8a4ff8f213391f1c710b.zip |
Merge "Export elfutils headers and lose the version number from the path."
Diffstat (limited to 'src/libelf/elf_getdata.c')
-rw-r--r-- | src/libelf/elf_getdata.c | 506 |
1 files changed, 506 insertions, 0 deletions
diff --git a/src/libelf/elf_getdata.c b/src/libelf/elf_getdata.c new file mode 100644 index 00000000..5a718f82 --- /dev/null +++ b/src/libelf/elf_getdata.c @@ -0,0 +1,506 @@ +/* Return the next data element from the section after possibly converting it. + Copyright (C) 1998-2005, 2006, 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + Written by Ulrich Drepper <drepper@redhat.com>, 1998. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <errno.h> +#include <stddef.h> +#include <string.h> +#include <unistd.h> + +#include "libelfP.h" +#include <system.h> +#include "common.h" +#include "elf-knowledge.h" + + +#define TYPEIDX(Sh_Type) \ + (Sh_Type >= SHT_NULL && Sh_Type < SHT_NUM \ + ? Sh_Type \ + : (Sh_Type >= SHT_GNU_HASH && Sh_Type <= SHT_HISUNW \ + ? SHT_NUM + Sh_Type - SHT_GNU_HASH \ + : 0)) + +/* Associate section types with libelf types. */ +static const Elf_Type shtype_map[EV_NUM - 1][TYPEIDX (SHT_HISUNW) + 1] = + { + [EV_CURRENT - 1] = + { + [SHT_SYMTAB] = ELF_T_SYM, + [SHT_RELA] = ELF_T_RELA, + [SHT_HASH] = ELF_T_WORD, + [SHT_DYNAMIC] = ELF_T_DYN, + [SHT_REL] = ELF_T_REL, + [SHT_DYNSYM] = ELF_T_SYM, + [SHT_INIT_ARRAY] = ELF_T_ADDR, + [SHT_FINI_ARRAY] = ELF_T_ADDR, + [SHT_PREINIT_ARRAY] = ELF_T_ADDR, + [SHT_GROUP] = ELF_T_WORD, + [SHT_SYMTAB_SHNDX] = ELF_T_WORD, + [SHT_NOTE] = ELF_T_NHDR, + [TYPEIDX (SHT_GNU_verdef)] = ELF_T_VDEF, + [TYPEIDX (SHT_GNU_verneed)] = ELF_T_VNEED, + [TYPEIDX (SHT_GNU_versym)] = ELF_T_HALF, + [TYPEIDX (SHT_SUNW_syminfo)] = ELF_T_SYMINFO, + [TYPEIDX (SHT_SUNW_move)] = ELF_T_MOVE, + [TYPEIDX (SHT_GNU_LIBLIST)] = ELF_T_LIB, + [TYPEIDX (SHT_GNU_HASH)] = ELF_T_GNUHASH, + } + }; + +#if !ALLOW_UNALIGNED +/* Associate libelf types with their internal alignment requirements. */ +const uint_fast8_t __libelf_type_aligns[EV_NUM - 1][ELFCLASSNUM - 1][ELF_T_NUM] = + { +# define TYPE_ALIGNS(Bits) \ + { \ + [ELF_T_ADDR] = __alignof__ (ElfW2(Bits,Addr)), \ + [ELF_T_HALF] = __alignof__ (ElfW2(Bits,Half)), \ + [ELF_T_WORD] = __alignof__ (ElfW2(Bits,Word)), \ + [ELF_T_SYM] = __alignof__ (ElfW2(Bits,Sym)), \ + [ELF_T_SYMINFO] = __alignof__ (ElfW2(Bits,Syminfo)), \ + [ELF_T_REL] = __alignof__ (ElfW2(Bits,Rel)), \ + [ELF_T_RELA] = __alignof__ (ElfW2(Bits,Rela)), \ + [ELF_T_DYN] = __alignof__ (ElfW2(Bits,Dyn)), \ + [ELF_T_VDEF] = __alignof__ (ElfW2(Bits,Verdef)), \ + [ELF_T_VDAUX] = __alignof__ (ElfW2(Bits,Verdaux)), \ + [ELF_T_VNEED] = __alignof__ (ElfW2(Bits,Verneed)), \ + [ELF_T_VNAUX] = __alignof__ (ElfW2(Bits,Vernaux)), \ + [ELF_T_MOVE] = __alignof__ (ElfW2(Bits,Move)), \ + [ELF_T_LIB] = __alignof__ (ElfW2(Bits,Lib)), \ + [ELF_T_NHDR] = __alignof__ (ElfW2(Bits,Nhdr)), \ + } + [EV_CURRENT - 1] = + { + [ELFCLASS32 - 1] = TYPE_ALIGNS (32), + [ELFCLASS64 - 1] = TYPE_ALIGNS (64), + } +# undef TYPE_ALIGNS + }; +#endif + + +/* Convert the data in the current section. */ +static void +convert_data (Elf_Scn *scn, int version __attribute__ ((unused)), int eclass, + int data, size_t size, Elf_Type type) +{ + const size_t align = __libelf_type_align (eclass, type); + + if (data == MY_ELFDATA) + { + if (((((size_t) (char *) scn->rawdata_base)) & (align - 1)) == 0) + /* No need to copy, we can use the raw data. */ + scn->data_base = scn->rawdata_base; + else + { + scn->data_base = (char *) malloc (size); + if (scn->data_base == NULL) + { + __libelf_seterrno (ELF_E_NOMEM); + return; + } + + /* The copy will be appropriately aligned for direct access. */ + memcpy (scn->data_base, scn->rawdata_base, size); + } + } + else + { + xfct_t fp; + + scn->data_base = (char *) malloc (size); + if (scn->data_base == NULL) + { + __libelf_seterrno (ELF_E_NOMEM); + return; + } + + /* Get the conversion function. */ +#if EV_NUM != 2 + fp = __elf_xfctstom[version - 1][__libelf_version - 1][eclass - 1][type]; +#else + fp = __elf_xfctstom[0][0][eclass - 1][type]; +#endif + + fp (scn->data_base, scn->rawdata_base, size, 0); + } + + scn->data_list.data.d.d_buf = scn->data_base; + scn->data_list.data.d.d_size = size; + scn->data_list.data.d.d_type = type; + scn->data_list.data.d.d_off = scn->rawdata.d.d_off; + scn->data_list.data.d.d_align = scn->rawdata.d.d_align; + scn->data_list.data.d.d_version = scn->rawdata.d.d_version; + + scn->data_list.data.s = scn; +} + + +/* Store the information for the raw data in the `rawdata' element. */ +int +internal_function +__libelf_set_rawdata_wrlock (Elf_Scn *scn) +{ + size_t offset; + size_t size; + size_t align; + int type; + Elf *elf = scn->elf; + + if (elf->class == ELFCLASS32) + { + Elf32_Shdr *shdr + = scn->shdr.e32 ?: __elf32_getshdr_wrlock (scn); + + if (shdr == NULL) + /* Something went terribly wrong. */ + return 1; + + offset = shdr->sh_offset; + size = shdr->sh_size; + type = shdr->sh_type; + align = shdr->sh_addralign; + } + else + { + Elf64_Shdr *shdr + = scn->shdr.e64 ?: __elf64_getshdr_wrlock (scn); + + if (shdr == NULL) + /* Something went terribly wrong. */ + return 1; + + offset = shdr->sh_offset; + size = shdr->sh_size; + type = shdr->sh_type; + align = shdr->sh_addralign; + } + + /* If the section has no data (for whatever reason), leave the `d_buf' + pointer NULL. */ + if (size != 0 && type != SHT_NOBITS) + { + /* First a test whether the section is valid at all. */ + size_t entsize; + + if (type == SHT_HASH) + { + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr = __gelf_getehdr_rdlock (elf, &ehdr_mem); + entsize = SH_ENTSIZE_HASH (ehdr); + } + else + { + Elf_Type t = shtype_map[LIBELF_EV_IDX][TYPEIDX (type)]; + if (t == ELF_T_VDEF || t == ELF_T_NHDR + || (t == ELF_T_GNUHASH && elf->class == ELFCLASS64)) + entsize = 1; + else + entsize = __libelf_type_sizes[LIBELF_EV_IDX][elf->class - 1][t]; + } + + /* We assume it is an array of bytes if it is none of the structured + sections we know of. */ + if (entsize == 0) + entsize = 1; + + if (unlikely (size % entsize != 0)) + { + __libelf_seterrno (ELF_E_INVALID_DATA); + return 1; + } + + /* We can use the mapped or loaded data if available. */ + if (elf->map_address != NULL) + { + /* First see whether the information in the section header is + valid and it does not ask for too much. */ + if (unlikely (offset + size > elf->maximum_size)) + { + /* Something is wrong. */ + __libelf_seterrno (ELF_E_INVALID_SECTION_HEADER); + return 1; + } + + scn->rawdata_base = scn->rawdata.d.d_buf + = (char *) elf->map_address + elf->start_offset + offset; + } + else if (likely (elf->fildes != -1)) + { + /* We have to read the data from the file. Allocate the needed + memory. */ + scn->rawdata_base = scn->rawdata.d.d_buf + = (char *) malloc (size); + if (scn->rawdata.d.d_buf == NULL) + { + __libelf_seterrno (ELF_E_NOMEM); + return 1; + } + + ssize_t n = pread_retry (elf->fildes, scn->rawdata.d.d_buf, size, + elf->start_offset + offset); + if (unlikely ((size_t) n != size)) + { + /* Cannot read the data. */ + free (scn->rawdata.d.d_buf); + scn->rawdata_base = scn->rawdata.d.d_buf = NULL; + __libelf_seterrno (ELF_E_READ_ERROR); + return 1; + } + } + else + { + /* The file descriptor is already closed, we cannot get the data + anymore. */ + __libelf_seterrno (ELF_E_FD_DISABLED); + return 1; + } + } + + scn->rawdata.d.d_size = size; + /* Some broken ELF ABI for 64-bit machines use the wrong hash table + entry size. See elf-knowledge.h for more information. */ + if (type == SHT_HASH && elf->class == ELFCLASS64) + { + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr = __gelf_getehdr_rdlock (elf, &ehdr_mem); + scn->rawdata.d.d_type + = (SH_ENTSIZE_HASH (ehdr) == 4 ? ELF_T_WORD : ELF_T_XWORD); + } + else + scn->rawdata.d.d_type = shtype_map[LIBELF_EV_IDX][TYPEIDX (type)]; + scn->rawdata.d.d_off = 0; + scn->rawdata.d.d_align = align; + if (elf->class == ELFCLASS32 + || (offsetof (struct Elf, state.elf32.ehdr) + == offsetof (struct Elf, state.elf64.ehdr))) + scn->rawdata.d.d_version = + elf->state.elf32.ehdr->e_ident[EI_VERSION]; + else + scn->rawdata.d.d_version = + elf->state.elf64.ehdr->e_ident[EI_VERSION]; + + scn->rawdata.s = scn; + + scn->data_read = 1; + + /* We actually read data from the file. At least we tried. */ + scn->flags |= ELF_F_FILEDATA; + + return 0; +} + +int +internal_function +__libelf_set_rawdata (Elf_Scn *scn) +{ + int result; + + if (scn == NULL) + return 1; + + rwlock_wrlock (scn->elf->lock); + result = __libelf_set_rawdata_wrlock (scn); + rwlock_unlock (scn->elf->lock); + + return result; +} + +Elf_Data * +internal_function +__elf_getdata_rdlock (scn, data) + Elf_Scn *scn; + Elf_Data *data; +{ + Elf_Data *result = NULL; + Elf *elf; + int locked = 0; + + if (scn == NULL) + return NULL; + + if (unlikely (scn->elf->kind != ELF_K_ELF)) + { + __libelf_seterrno (ELF_E_INVALID_HANDLE); + return NULL; + } + + /* We will need this multiple times later on. */ + elf = scn->elf; + + /* If `data' is not NULL this means we are not addressing the initial + data in the file. But this also means this data is already read + (since otherwise it is not possible to have a valid `data' pointer) + and all the data structures are initialized as well. In this case + we can simply walk the list of data records. */ + if (data != NULL) + { + Elf_Data_List *runp; + + /* It is not possible that if DATA is not NULL the first entry is + returned. But this also means that there must be a first data + entry. */ + if (scn->data_list_rear == NULL + /* The section the reference data is for must match the section + parameter. */ + || unlikely (((Elf_Data_Scn *) data)->s != scn)) + { + __libelf_seterrno (ELF_E_DATA_MISMATCH); + goto out; + } + + /* We start searching with the first entry. */ + runp = &scn->data_list; + + while (1) + { + /* If `data' does not match any known record punt. */ + if (runp == NULL) + { + __libelf_seterrno (ELF_E_DATA_MISMATCH); + goto out; + } + + if (&runp->data.d == data) + /* Found the entry. */ + break; + + runp = runp->next; + } + + /* Return the data for the next data record. */ + result = runp->next ? &runp->next->data.d : NULL; + goto out; + } + + /* If the data for this section was not yet initialized do it now. */ + if (scn->data_read == 0) + { + /* We cannot acquire a write lock while we are holding a read + lock. Therefore give up the read lock and then get the write + lock. But this means that the data could meanwhile be + modified, therefore start the tests again. */ + rwlock_unlock (elf->lock); + rwlock_wrlock (elf->lock); + locked = 1; + + /* Read the data from the file. There is always a file (or + memory region) associated with this descriptor since + otherwise the `data_read' flag would be set. */ + if (scn->data_read == 0 && __libelf_set_rawdata_wrlock (scn) != 0) + /* Something went wrong. The error value is already set. */ + goto out; + } + + /* At this point we know the raw data is available. But it might be + empty in case the section has size zero (for whatever reason). + Now create the converted data in case this is necessary. */ + if (scn->data_list_rear == NULL) + { + if (scn->rawdata.d.d_buf != NULL && scn->rawdata.d.d_size > 0) + { + if (!locked) + { + rwlock_unlock (elf->lock); + rwlock_wrlock (elf->lock); + if (scn->data_list_rear != NULL) + goto pass; + } + + /* Convert according to the version and the type. */ + convert_data (scn, __libelf_version, elf->class, + (elf->class == ELFCLASS32 + || (offsetof (struct Elf, state.elf32.ehdr) + == offsetof (struct Elf, state.elf64.ehdr)) + ? elf->state.elf32.ehdr->e_ident[EI_DATA] + : elf->state.elf64.ehdr->e_ident[EI_DATA]), + scn->rawdata.d.d_size, scn->rawdata.d.d_type); + } + else + { + /* This is an empty or NOBITS section. There is no buffer but + the size information etc is important. */ + scn->data_list.data.d = scn->rawdata.d; + scn->data_list.data.s = scn; + } + + scn->data_list_rear = &scn->data_list; + } + + /* If no data is present we cannot return any. */ + if (scn->data_list_rear != NULL) + pass: + /* Return the first data element in the list. */ + result = &scn->data_list.data.d; + + out: + return result; +} + +Elf_Data * +elf_getdata (scn, data) + Elf_Scn *scn; + Elf_Data *data; +{ + Elf_Data *result; + + if (scn == NULL) + return NULL; + + rwlock_rdlock (scn->elf->lock); + result = __elf_getdata_rdlock (scn, data); + rwlock_unlock (scn->elf->lock); + + return result; +} +INTDEF(elf_getdata) |