Blame libelf/elf32_updatenull.c

Packit 032894
/* Update data structures for changes.
Packit 032894
   Copyright (C) 2000-2010, 2015, 2016 Red Hat, Inc.
Packit 032894
   This file is part of elfutils.
Packit 032894
   Written by Ulrich Drepper <drepper@redhat.com>, 2000.
Packit 032894
Packit 032894
   This file is free software; you can redistribute it and/or modify
Packit 032894
   it under the terms of either
Packit 032894
Packit 032894
     * the GNU Lesser General Public License as published by the Free
Packit 032894
       Software Foundation; either version 3 of the License, or (at
Packit 032894
       your option) any later version
Packit 032894
Packit 032894
   or
Packit 032894
Packit 032894
     * the GNU General Public License as published by the Free
Packit 032894
       Software Foundation; either version 2 of the License, or (at
Packit 032894
       your option) any later version
Packit 032894
Packit 032894
   or both in parallel, as here.
Packit 032894
Packit 032894
   elfutils is distributed in the hope that it will be useful, but
Packit 032894
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 032894
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 032894
   General Public License for more details.
Packit 032894
Packit 032894
   You should have received copies of the GNU General Public License and
Packit 032894
   the GNU Lesser General Public License along with this program.  If
Packit 032894
   not, see <http://www.gnu.org/licenses/>.  */
Packit 032894
Packit 032894
#ifdef HAVE_CONFIG_H
Packit 032894
# include <config.h>
Packit 032894
#endif
Packit 032894
Packit 032894
#include <assert.h>
Packit 032894
#include <endian.h>
Packit 032894
#include <libelf.h>
Packit 032894
#include <stdbool.h>
Packit 032894
#include <string.h>
Packit 032894
Packit 032894
#include <system.h>
Packit 032894
#include "libelfP.h"
Packit 032894
#include "elf-knowledge.h"
Packit 032894
Packit 032894
#ifndef LIBELFBITS
Packit 032894
# define LIBELFBITS 32
Packit 032894
#endif
Packit 032894
Packit 032894
/* Some fields contain 32/64 sizes.  We cannot use Elf32/64_Word for those,
Packit 032894
   since those are both 32bits.  Elf32/64_Xword is always 64bits.  */
Packit 032894
#define Elf32_SizeWord Elf32_Word
Packit 032894
#define Elf64_SizeWord Elf64_Xword
Packit 032894
Packit 032894
Packit 032894
static int
Packit 032894
ELFW(default_ehdr,LIBELFBITS) (Elf *elf, ElfW2(LIBELFBITS,Ehdr) *ehdr,
Packit 032894
			       size_t shnum, int *change_bop)
Packit 032894
{
Packit 032894
  /* Always write the magic bytes.  */
Packit 032894
  if (memcmp (&ehdr->e_ident[EI_MAG0], ELFMAG, SELFMAG) != 0)
Packit 032894
    {
Packit 032894
      memcpy (&ehdr->e_ident[EI_MAG0], ELFMAG, SELFMAG);
Packit 032894
      elf->state.ELFW(elf,LIBELFBITS).ehdr_flags |= ELF_F_DIRTY;
Packit 032894
    }
Packit 032894
Packit 032894
  /* Always set the file class.  */
Packit 032894
  update_if_changed (ehdr->e_ident[EI_CLASS], ELFW(ELFCLASS,LIBELFBITS),
Packit 032894
		     elf->state.ELFW(elf,LIBELFBITS).ehdr_flags);
Packit 032894
Packit 032894
  /* Set the data encoding if necessary.  */
Packit 032894
  if (unlikely (ehdr->e_ident[EI_DATA] == ELFDATANONE))
Packit 032894
    {
Packit 032894
      ehdr->e_ident[EI_DATA] =
Packit 032894
	BYTE_ORDER == BIG_ENDIAN ? ELFDATA2MSB : ELFDATA2LSB;
Packit 032894
      elf->state.ELFW(elf,LIBELFBITS).ehdr_flags |= ELF_F_DIRTY;
Packit 032894
    }
Packit 032894
  else if (unlikely (ehdr->e_ident[EI_DATA] >= ELFDATANUM))
Packit 032894
    {
Packit 032894
      __libelf_seterrno (ELF_E_DATA_ENCODING);
Packit 032894
      return 1;
Packit 032894
    }
Packit 032894
  else
Packit 032894
    *change_bop = ((BYTE_ORDER == LITTLE_ENDIAN
Packit 032894
		    && ehdr->e_ident[EI_DATA] != ELFDATA2LSB)
Packit 032894
		   || (BYTE_ORDER == BIG_ENDIAN
Packit 032894
		       && ehdr->e_ident[EI_DATA] != ELFDATA2MSB));
Packit 032894
Packit 032894
  /* Unconditionally overwrite the ELF version.  */
Packit 032894
  update_if_changed (ehdr->e_ident[EI_VERSION], EV_CURRENT,
Packit 032894
		     elf->state.ELFW(elf,LIBELFBITS).ehdr_flags);
Packit 032894
Packit 032894
  if (unlikely (ehdr->e_version == EV_NONE))
Packit 032894
    {
Packit 032894
      ehdr->e_version = EV_CURRENT;
Packit 032894
      elf->state.ELFW(elf,LIBELFBITS).ehdr_flags |= ELF_F_DIRTY;
Packit 032894
    }
Packit 032894
  else if (unlikely (ehdr->e_version != EV_CURRENT))
Packit 032894
    {
Packit 032894
      __libelf_seterrno (ELF_E_UNKNOWN_VERSION);
Packit 032894
      return 1;
Packit 032894
    }
Packit 032894
Packit 032894
  if (unlikely (shnum >= SHN_LORESERVE))
Packit 032894
    {
Packit 032894
      update_if_changed (ehdr->e_shnum, 0,
Packit 032894
			 elf->state.ELFW(elf,LIBELFBITS).ehdr_flags);
Packit 032894
    }
Packit 032894
  else
Packit 032894
    update_if_changed (ehdr->e_shnum, shnum,
Packit 032894
		       elf->state.ELFW(elf,LIBELFBITS).ehdr_flags);
Packit 032894
Packit 032894
  if (unlikely (ehdr->e_ehsize != elf_typesize (LIBELFBITS, ELF_T_EHDR, 1)))
Packit 032894
    {
Packit 032894
      ehdr->e_ehsize = elf_typesize (LIBELFBITS, ELF_T_EHDR, 1);
Packit 032894
      elf->state.ELFW(elf,LIBELFBITS).ehdr_flags |= ELF_F_DIRTY;
Packit 032894
    }
Packit 032894
Packit 032894
  /* If phnum is zero make sure e_phoff is also zero and not some random
Packit 032894
     value.  That would cause trouble in update_file.  */
Packit 032894
  if (ehdr->e_phnum == 0 && ehdr->e_phoff != 0)
Packit 032894
    {
Packit 032894
      ehdr->e_phoff = 0;
Packit 032894
      elf->state.ELFW(elf,LIBELFBITS).ehdr_flags |= ELF_F_DIRTY;
Packit 032894
    }
Packit 032894
Packit 032894
  return 0;
Packit 032894
}
Packit 032894
Packit 032894
Packit 032894
int64_t
Packit 032894
internal_function
Packit 032894
__elfw2(LIBELFBITS,updatenull_wrlock) (Elf *elf, int *change_bop, size_t shnum)
Packit 032894
{
Packit 032894
  ElfW2(LIBELFBITS,Ehdr) *ehdr;
Packit 032894
  int changed = 0;
Packit 032894
  int ehdr_flags = 0;
Packit 032894
Packit 032894
  ehdr = __elfw2(LIBELFBITS,getehdr_wrlock) (elf);
Packit 032894
Packit 032894
  /* Set the default values.  */
Packit 032894
  if (ELFW(default_ehdr,LIBELFBITS) (elf, ehdr, shnum, change_bop) != 0)
Packit 032894
    return -1;
Packit 032894
Packit 032894
  /* At least the ELF header is there.  */
Packit 032894
  ElfW2(LIBELFBITS,SizeWord) size = elf_typesize (LIBELFBITS, ELF_T_EHDR, 1);
Packit 032894
Packit 032894
  /* Set the program header position.  */
Packit 032894
  if (elf->state.ELFW(elf,LIBELFBITS).phdr == NULL)
Packit 032894
    (void) __elfw2(LIBELFBITS,getphdr_wrlock) (elf);
Packit 032894
  if (elf->state.ELFW(elf,LIBELFBITS).phdr != NULL)
Packit 032894
    {
Packit 032894
      size_t phnum;
Packit 032894
      if (unlikely (__elf_getphdrnum_rdlock (elf, &phnum) != 0))
Packit 032894
	return -1;
Packit 032894
Packit 032894
      if (elf->flags & ELF_F_LAYOUT)
Packit 032894
	{
Packit 032894
	  /* The user is supposed to fill out e_phoff.  Use it and
Packit 032894
	     e_phnum to determine the maximum extend.  */
Packit 032894
	  size = MAX (size,
Packit 032894
		      ehdr->e_phoff
Packit 032894
		      + elf_typesize (LIBELFBITS, ELF_T_PHDR, phnum));
Packit 032894
	}
Packit 032894
      else
Packit 032894
	{
Packit 032894
	  update_if_changed (ehdr->e_phoff,
Packit 032894
			     elf_typesize (LIBELFBITS, ELF_T_EHDR, 1),
Packit 032894
			     ehdr_flags);
Packit 032894
Packit 032894
	  /* We need no alignment here.  */
Packit 032894
	  size += elf_typesize (LIBELFBITS, ELF_T_PHDR, phnum);
Packit 032894
	}
Packit 032894
    }
Packit 032894
Packit 032894
  if (shnum > 0)
Packit 032894
    {
Packit 032894
      struct Elf_Scn *scn1 = NULL;
Packit 032894
      Elf_ScnList *list;
Packit 032894
      bool first = true;
Packit 032894
Packit 032894
      assert (elf->state.ELFW(elf,LIBELFBITS).scns.cnt > 0);
Packit 032894
Packit 032894
      if (shnum >= SHN_LORESERVE)
Packit 032894
	{
Packit 032894
	  /* We have to  fill in the number of sections in the header
Packit 032894
	     of the zeroth section.  */
Packit 032894
	  Elf_Scn *scn0 = &elf->state.ELFW(elf,LIBELFBITS).scns.data[0];
Packit 032894
Packit 032894
	  update_if_changed (scn0->shdr.ELFW(e,LIBELFBITS)->sh_size,
Packit 032894
			     shnum, scn0->shdr_flags);
Packit 032894
	}
Packit 032894
Packit 032894
      /* Go over all sections and find out how large they are.  */
Packit 032894
      list = &elf->state.ELFW(elf,LIBELFBITS).scns;
Packit 032894
Packit 032894
      /* Find the first section. */
Packit 032894
      if (list->cnt > 1)
Packit 032894
	scn1 = &list->data[1];
Packit 032894
      else if (list->next != NULL)
Packit 032894
	scn1 = &list->next->data[0];
Packit 032894
Packit 032894
      /* Load the section headers if necessary.  This loads the
Packit 032894
	 headers for all sections.  */
Packit 032894
      if (scn1 != NULL && scn1->shdr.ELFW(e,LIBELFBITS) == NULL)
Packit 032894
	(void) __elfw2(LIBELFBITS,getshdr_wrlock) (scn1);
Packit 032894
Packit 032894
      do
Packit 032894
	{
Packit 032894
	  for (size_t cnt = first == true; cnt < list->cnt; ++cnt)
Packit 032894
	    {
Packit 032894
	      Elf_Scn *scn = &list->data[cnt];
Packit 032894
	      ElfW2(LIBELFBITS,Shdr) *shdr = scn->shdr.ELFW(e,LIBELFBITS);
Packit 032894
	      int64_t offset = 0;
Packit 032894
Packit 032894
	      assert (shdr != NULL);
Packit 032894
	      ElfW2(LIBELFBITS,SizeWord) sh_entsize = shdr->sh_entsize;
Packit 032894
	      ElfW2(LIBELFBITS,SizeWord) sh_align = shdr->sh_addralign ?: 1;
Packit 032894
	      if (unlikely (! powerof2 (sh_align)))
Packit 032894
		{
Packit 032894
		  __libelf_seterrno (ELF_E_INVALID_ALIGN);
Packit 032894
		  return -1;
Packit 032894
		}
Packit 032894
Packit 032894
	      /* Set the sh_entsize value if we can reliably detect it.  */
Packit 032894
	      switch (shdr->sh_type)
Packit 032894
		{
Packit 032894
		case SHT_SYMTAB:
Packit 032894
		  sh_entsize = elf_typesize (LIBELFBITS, ELF_T_SYM, 1);
Packit 032894
		  break;
Packit 032894
		case SHT_RELA:
Packit 032894
		  sh_entsize = elf_typesize (LIBELFBITS, ELF_T_RELA, 1);
Packit 032894
		  break;
Packit 032894
		case SHT_GROUP:
Packit 032894
		  /* Only relocatable files can contain section groups.  */
Packit 032894
		  if (ehdr->e_type != ET_REL)
Packit 032894
		    {
Packit 032894
		      __libelf_seterrno (ELF_E_GROUP_NOT_REL);
Packit 032894
		      return -1;
Packit 032894
		    }
Packit 032894
		  FALLTHROUGH;
Packit 032894
		case SHT_SYMTAB_SHNDX:
Packit 032894
		  sh_entsize = elf_typesize (32, ELF_T_WORD, 1);
Packit 032894
		  break;
Packit 032894
		case SHT_HASH:
Packit 032894
		  sh_entsize = SH_ENTSIZE_HASH (ehdr);
Packit 032894
		  break;
Packit 032894
		case SHT_DYNAMIC:
Packit 032894
		  sh_entsize = elf_typesize (LIBELFBITS, ELF_T_DYN, 1);
Packit 032894
		  break;
Packit 032894
		case SHT_REL:
Packit 032894
		  sh_entsize = elf_typesize (LIBELFBITS, ELF_T_REL, 1);
Packit 032894
		  break;
Packit 032894
		case SHT_DYNSYM:
Packit 032894
		  sh_entsize = elf_typesize (LIBELFBITS, ELF_T_SYM, 1);
Packit 032894
		  break;
Packit 032894
		case SHT_SUNW_move:
Packit 032894
		  sh_entsize = elf_typesize (LIBELFBITS, ELF_T_MOVE, 1);
Packit 032894
		  break;
Packit 032894
		case SHT_SUNW_syminfo:
Packit 032894
		  sh_entsize = elf_typesize (LIBELFBITS, ELF_T_SYMINFO, 1);
Packit 032894
		  break;
Packit 032894
		default:
Packit 032894
		  break;
Packit 032894
		}
Packit 032894
Packit 032894
	      /* If the section header contained the wrong entry size
Packit 032894
		 correct it and mark the header as modified.  */
Packit 032894
	      update_if_changed (shdr->sh_entsize, sh_entsize,
Packit 032894
				 scn->shdr_flags);
Packit 032894
Packit 032894
	      if (scn->data_read == 0
Packit 032894
		  && __libelf_set_rawdata_wrlock (scn) != 0)
Packit 032894
		/* Something went wrong.  The error value is already set.  */
Packit 032894
		return -1;
Packit 032894
Packit 032894
	      /* Iterate over all data blocks.  */
Packit 032894
	      if (list->data[cnt].data_list_rear != NULL)
Packit 032894
		{
Packit 032894
		  Elf_Data_List *dl = &scn->data_list;
Packit 032894
Packit 032894
		  while (dl != NULL)
Packit 032894
		    {
Packit 032894
		      Elf_Data *data = &dl->data.d;
Packit 032894
		      if (dl == &scn->data_list && data->d_buf == NULL
Packit 032894
			  && scn->rawdata.d.d_buf != NULL)
Packit 032894
			data = &scn->rawdata.d;
Packit 032894
Packit 032894
		      if (unlikely (data->d_version != EV_CURRENT))
Packit 032894
			{
Packit 032894
			  __libelf_seterrno (ELF_E_UNKNOWN_VERSION);
Packit 032894
			  return -1;
Packit 032894
			}
Packit 032894
Packit 032894
		      if (unlikely (! powerof2 (data->d_align)))
Packit 032894
			{
Packit 032894
			  __libelf_seterrno (ELF_E_INVALID_ALIGN);
Packit 032894
			  return -1;
Packit 032894
			}
Packit 032894
Packit 032894
		      sh_align = MAX (sh_align, data->d_align);
Packit 032894
Packit 032894
		      if (elf->flags & ELF_F_LAYOUT)
Packit 032894
			{
Packit 032894
			  /* The user specified the offset and the size.
Packit 032894
			     All we have to do is check whether this block
Packit 032894
			     fits in the size specified for the section.  */
Packit 032894
			  if (unlikely ((ElfW2(LIBELFBITS,SizeWord))
Packit 032894
					(data->d_off + data->d_size)
Packit 032894
					> shdr->sh_size))
Packit 032894
			    {
Packit 032894
			      __libelf_seterrno (ELF_E_SECTION_TOO_SMALL);
Packit 032894
			      return -1;
Packit 032894
			    }
Packit 032894
			}
Packit 032894
		      else
Packit 032894
			{
Packit 032894
			  /* Determine the padding.  */
Packit 032894
			  offset = ((offset + data->d_align - 1)
Packit 032894
				    & ~(data->d_align - 1));
Packit 032894
Packit 032894
			  update_if_changed (data->d_off, offset, changed);
Packit 032894
Packit 032894
			  offset += data->d_size;
Packit 032894
			}
Packit 032894
Packit 032894
		      /* Next data block.  */
Packit 032894
		      dl = dl->next;
Packit 032894
		    }
Packit 032894
		}
Packit 032894
	      else
Packit 032894
		/* Get the size of the section from the raw data.  If
Packit 032894
		   none is available the value is zero.  */
Packit 032894
		offset += scn->rawdata.d.d_size;
Packit 032894
Packit 032894
	      if (elf->flags & ELF_F_LAYOUT)
Packit 032894
		{
Packit 032894
		  size = MAX (size,
Packit 032894
			      (shdr->sh_type != SHT_NOBITS
Packit 032894
			       ? shdr->sh_offset + shdr->sh_size : 0));
Packit 032894
Packit 032894
		  /* The alignment must be a power of two.  This is a
Packit 032894
		     requirement from the ELF specification.  Additionally
Packit 032894
		     we test for the alignment of the section being large
Packit 032894
		     enough for the largest alignment required by a data
Packit 032894
		     block.  */
Packit 032894
		  if (unlikely (! powerof2 (shdr->sh_addralign))
Packit 032894
		      || unlikely ((shdr->sh_addralign ?: 1) < sh_align))
Packit 032894
		    {
Packit 032894
		      __libelf_seterrno (ELF_E_INVALID_ALIGN);
Packit 032894
		      return -1;
Packit 032894
		    }
Packit 032894
		}
Packit 032894
	      else
Packit 032894
		{
Packit 032894
		  /* How much alignment do we need for this section.  */
Packit 032894
		  update_if_changed (shdr->sh_addralign, sh_align,
Packit 032894
				     scn->shdr_flags);
Packit 032894
Packit 032894
		  size = (size + sh_align - 1) & ~(sh_align - 1);
Packit 032894
		  int offset_changed = 0;
Packit 032894
		  update_if_changed (shdr->sh_offset, size, offset_changed);
Packit 032894
		  changed |= offset_changed;
Packit 032894
Packit 032894
		  if (offset_changed && scn->data_list_rear == NULL)
Packit 032894
		    {
Packit 032894
		      /* The position of the section in the file
Packit 032894
			 changed.  Create the section data list.  */
Packit 032894
		      if (__elf_getdata_rdlock (scn, NULL) == NULL)
Packit 032894
			return -1;
Packit 032894
		    }
Packit 032894
Packit 032894
		  /* See whether the section size is correct.  */
Packit 032894
		  int size_changed = 0;
Packit 032894
		  update_if_changed (shdr->sh_size,
Packit 032894
				     (ElfW2(LIBELFBITS,SizeWord)) offset,
Packit 032894
				     size_changed);
Packit 032894
		  changed |= size_changed;
Packit 032894
Packit 032894
		  if (shdr->sh_type != SHT_NOBITS)
Packit 032894
		    size += offset;
Packit 032894
Packit 032894
		  scn->shdr_flags |= (offset_changed | size_changed);
Packit 032894
		  scn->flags |= changed;
Packit 032894
		}
Packit 032894
Packit 032894
	      /* Check that the section size is actually a multiple of
Packit 032894
		 the entry size.  */
Packit 032894
	      if (shdr->sh_entsize != 0 && shdr->sh_entsize != 1
Packit 032894
		  && (elf->flags & ELF_F_PERMISSIVE) == 0)
Packit 032894
		{
Packit 032894
		  /* For compressed sections check the uncompressed size.  */
Packit 032894
		  ElfW2(LIBELFBITS,SizeWord) sh_size;
Packit 032894
		  if ((shdr->sh_flags & SHF_COMPRESSED) == 0)
Packit 032894
		    sh_size = shdr->sh_size;
Packit 032894
		  else
Packit 032894
		    {
Packit 032894
		      ElfW2(LIBELFBITS,Chdr) *chdr;
Packit 032894
		      chdr = elfw2(LIBELFBITS,getchdr) (scn);
Packit 032894
		      if (unlikely (chdr == NULL))
Packit 032894
			return -1;
Packit 032894
		      sh_size = chdr->ch_size;
Packit 032894
		    }
Packit 032894
Packit 032894
		  if (unlikely (sh_size % shdr->sh_entsize != 0))
Packit 032894
		    {
Packit 032894
		      __libelf_seterrno (ELF_E_INVALID_SHENTSIZE);
Packit 032894
		      return -1;
Packit 032894
		    }
Packit 032894
		}
Packit 032894
	    }
Packit 032894
Packit 032894
	  assert (list->next == NULL || list->cnt == list->max);
Packit 032894
Packit 032894
	  first = false;
Packit 032894
	}
Packit 032894
      while ((list = list->next) != NULL);
Packit 032894
Packit 032894
      /* Store section information.  */
Packit 032894
      update_if_changed (ehdr->e_shentsize,
Packit 032894
			 elf_typesize (LIBELFBITS, ELF_T_SHDR, 1), ehdr_flags);
Packit 032894
      if (elf->flags & ELF_F_LAYOUT)
Packit 032894
	{
Packit 032894
	  /* The user is supposed to fill out e_shoff.  Use it and
Packit 032894
	     e_shnum (or sh_size of the dummy, first section header)
Packit 032894
	     to determine the maximum extend.  */
Packit 032894
	  size = MAX (size,
Packit 032894
		      (ehdr->e_shoff
Packit 032894
		       + (elf_typesize (LIBELFBITS, ELF_T_SHDR, shnum))));
Packit 032894
	}
Packit 032894
      else
Packit 032894
	{
Packit 032894
	  /* Align for section header table.
Packit 032894
Packit 032894
	     Yes, we use `sizeof' and not `__alignof__' since we do not
Packit 032894
	     want to be surprised by architectures with less strict
Packit 032894
	     alignment rules.  */
Packit 032894
#define SHDR_ALIGN sizeof (ElfW2(LIBELFBITS,Off))
Packit 032894
	  size = (size + SHDR_ALIGN - 1) & ~(SHDR_ALIGN - 1);
Packit 032894
Packit 032894
	  update_if_changed (ehdr->e_shoff, size, elf->flags);
Packit 032894
Packit 032894
	  /* Account for the section header size.  */
Packit 032894
	  size += elf_typesize (LIBELFBITS, ELF_T_SHDR, shnum);
Packit 032894
	}
Packit 032894
    }
Packit 032894
Packit 032894
  elf->state.ELFW(elf,LIBELFBITS).ehdr_flags |= ehdr_flags;
Packit 032894
Packit 032894
  return size;
Packit 032894
}