Squashed 'third_party/elfutils/' content from commit 555e15e
Change-Id: I61cde98949e47e5c8c09c33260de17f30921be79
git-subtree-dir: third_party/elfutils
git-subtree-split: 555e15ebe8bf1eb33d00747173cfc80cc65648a4
diff --git a/libelf/elf_getdata.c b/libelf/elf_getdata.c
new file mode 100644
index 0000000..97c503b
--- /dev/null
+++ b/libelf/elf_getdata.c
@@ -0,0 +1,564 @@
+/* Return the next data element from the section after possibly converting it.
+ Copyright (C) 1998-2005, 2006, 2007, 2015, 2016 Red Hat, Inc.
+ This file is part of elfutils.
+ Written by Ulrich Drepper <drepper@redhat.com>, 1998.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ 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 copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#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_EHDR] = __alignof__ (ElfW2(Bits,Ehdr)), \
+ [ELF_T_HALF] = __alignof__ (ElfW2(Bits,Half)), \
+ [ELF_T_OFF] = __alignof__ (ElfW2(Bits,Off)), \
+ [ELF_T_PHDR] = __alignof__ (ElfW2(Bits,Phdr)), \
+ [ELF_T_SHDR] = __alignof__ (ElfW2(Bits,Shdr)), \
+ [ELF_T_SWORD] = __alignof__ (ElfW2(Bits,Sword)), \
+ [ELF_T_WORD] = __alignof__ (ElfW2(Bits,Word)), \
+ [ELF_T_XWORD] = __alignof__ (ElfW2(Bits,Xword)), \
+ [ELF_T_SXWORD] = __alignof__ (ElfW2(Bits,Sxword)), \
+ [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)), \
+ [ELF_T_GNUHASH] = __alignof__ (Elf32_Word), \
+ [ELF_T_AUXV] = __alignof__ (ElfW2(Bits,auxv_t)), \
+ [ELF_T_CHDR] = __alignof__ (ElfW2(Bits,Chdr)), \
+ }
+ [EV_CURRENT - 1] =
+ {
+ [ELFCLASS32 - 1] = TYPE_ALIGNS (32),
+ [ELFCLASS64 - 1] = TYPE_ALIGNS (64),
+ }
+# undef TYPE_ALIGNS
+ };
+#endif
+
+
+Elf_Type
+internal_function
+__libelf_data_type (Elf *elf, int sh_type)
+{
+ /* Some broken ELF ABI for 64-bit machines use the wrong hash table
+ entry size. See elf-knowledge.h for more information. */
+ if (sh_type == SHT_HASH && elf->class == ELFCLASS64)
+ {
+ GElf_Ehdr ehdr_mem;
+ GElf_Ehdr *ehdr = __gelf_getehdr_rdlock (elf, &ehdr_mem);
+ return (SH_ENTSIZE_HASH (ehdr) == 4 ? ELF_T_WORD : ELF_T_XWORD);
+ }
+ else
+ return shtype_map[LIBELF_EV_IDX][TYPEIDX (sh_type)];
+}
+
+/* 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;
+ }
+
+ /* Make sure the source is correctly aligned for the conversion
+ function to directly access the data elements. */
+ char *rawdata_source;
+ if (ALLOW_UNALIGNED ||
+ ((((size_t) (char *) scn->rawdata_base)) & (align - 1)) == 0)
+ rawdata_source = scn->rawdata_base;
+ else
+ {
+ rawdata_source = (char *) malloc (size);
+ if (rawdata_source == NULL)
+ {
+ __libelf_seterrno (ELF_E_NOMEM);
+ return;
+ }
+
+ /* The copy will be appropriately aligned for direct access. */
+ memcpy (rawdata_source, scn->rawdata_base, size);
+ }
+
+ /* 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, rawdata_source, size, 0);
+
+ if (rawdata_source != scn->rawdata_base)
+ free (rawdata_source);
+ }
+
+ 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)
+{
+ Elf64_Off offset;
+ Elf64_Xword size;
+ Elf64_Xword align;
+ Elf64_Xword flags;
+ 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;
+ flags = shdr->sh_flags;
+ }
+ 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;
+ flags = shdr->sh_flags;
+ }
+
+ /* 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;
+
+ /* Compressed data has a header, but then compressed data. */
+ if ((flags & SHF_COMPRESSED) != 0)
+ entsize = 1;
+ else 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. Check for unsigned
+ overflow. */
+ if (unlikely (offset > elf->maximum_size
+ || elf->maximum_size - offset < 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))
+ {
+ /* First see whether the information in the section header is
+ valid and it does not ask for too much. Check for unsigned
+ overflow. */
+ if (unlikely (offset > elf->maximum_size
+ || elf->maximum_size - offset < size))
+ {
+ /* Something is wrong. */
+ __libelf_seterrno (ELF_E_INVALID_SECTION_HEADER);
+ return 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;
+
+ /* Compressed data always has type ELF_T_CHDR regardless of the
+ section type. */
+ if ((flags & SHF_COMPRESSED) != 0)
+ scn->rawdata.d.d_type = ELF_T_CHDR;
+ else
+ scn->rawdata.d.d_type = __libelf_data_type (elf, type);
+ scn->rawdata.d.d_off = 0;
+
+ /* Make sure the alignment makes sense. d_align should be aligned both
+ in the section (trivially true since d_off is zero) and in the file.
+ Unfortunately we cannot be too strict because there are ELF files
+ out there that fail this requirement. We will try to fix those up
+ in elf_update when writing out the image. But for very large
+ alignment values this can bloat the image considerably. So here
+ just check and clamp the alignment value to not be bigger than the
+ actual offset of the data in the file. Given that there is always
+ at least an ehdr this will only trigger for alignment values > 64
+ which should be uncommon. */
+ align = align ?: 1;
+ if (type != SHT_NOBITS && align > offset)
+ align = offset;
+ 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;
+}
+
+void
+internal_function
+__libelf_set_data_list_rdlock (Elf_Scn *scn, int wrlocked)
+{
+ if (scn->rawdata.d.d_buf != NULL && scn->rawdata.d.d_size > 0)
+ {
+ Elf *elf = scn->elf;
+
+ /* Upgrade the lock to a write lock if necessary and check
+ nobody else already did the work. */
+ if (!wrlocked)
+ {
+ rwlock_unlock (elf->lock);
+ rwlock_wrlock (elf->lock);
+ if (scn->data_list_rear != NULL)
+ return;
+ }
+
+ /* 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;
+}
+
+Elf_Data *
+internal_function
+__elf_getdata_rdlock (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)
+ __libelf_set_data_list_rdlock (scn, locked);
+
+ /* Return the first data element in the list. */
+ result = &scn->data_list.data.d;
+
+ out:
+ return result;
+}
+
+Elf_Data *
+elf_getdata (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)