Blame elf/readelflib.c

Packit 6c4009
/* Copyright (C) 1999-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Andreas Jaeger <aj@suse.de>, 1999 and
Packit 6c4009
		  Jakub Jelinek <jakub@redhat.com>, 1999.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
/* This code is a heavily simplified version of the readelf program
Packit 6c4009
   that's part of the current binutils development version.  For architectures
Packit 6c4009
   which need to handle both 32bit and 64bit ELF libraries,  this file is
Packit 6c4009
   included twice for each arch size.  */
Packit 6c4009
Packit 6c4009
/* check_ptr checks that a pointer is in the mmaped file and doesn't
Packit 6c4009
   point outside it.  */
Packit 6c4009
#undef check_ptr
Packit 6c4009
#define check_ptr(ptr)						\
Packit 6c4009
do								\
Packit 6c4009
  {								\
Packit 6c4009
    if ((void *)(ptr) < file_contents				\
Packit 6c4009
	|| (void *)(ptr) > (file_contents+file_length))		\
Packit 6c4009
      {								\
Packit 6c4009
	error (0, 0, _("file %s is truncated\n"), file_name);	\
Packit 6c4009
	return 1;						\
Packit 6c4009
      }								\
Packit 6c4009
  }								\
Packit 6c4009
 while (0);
Packit 6c4009
Packit 6c4009
/* Returns 0 if everything is ok, != 0 in case of error.  */
Packit 6c4009
int
Packit 6c4009
process_elf_file (const char *file_name, const char *lib, int *flag,
Packit 6c4009
		  unsigned int *osversion, char **soname, void *file_contents,
Packit 6c4009
		  size_t file_length)
Packit 6c4009
{
Packit 6c4009
  int i;
Packit 6c4009
  unsigned int j;
Packit 6c4009
  unsigned int dynamic_addr;
Packit 6c4009
  size_t dynamic_size;
Packit 6c4009
  char *program_interpreter;
Packit 6c4009
Packit 6c4009
  ElfW(Ehdr) *elf_header;
Packit 6c4009
  ElfW(Phdr) *elf_pheader, *segment;
Packit 6c4009
  ElfW(Dyn) *dynamic_segment, *dyn_entry;
Packit 6c4009
  char *dynamic_strings;
Packit 6c4009
Packit 6c4009
  elf_header = (ElfW(Ehdr) *) file_contents;
Packit 6c4009
  *osversion = 0;
Packit 6c4009
Packit 6c4009
  if (elf_header->e_ident [EI_CLASS] != ElfW (CLASS))
Packit 6c4009
    {
Packit 6c4009
      if (opt_verbose)
Packit 6c4009
	{
Packit 6c4009
	  if (elf_header->e_ident [EI_CLASS] == ELFCLASS32)
Packit 6c4009
	    error (0, 0, _("%s is a 32 bit ELF file.\n"), file_name);
Packit 6c4009
	  else if (elf_header->e_ident [EI_CLASS] == ELFCLASS64)
Packit 6c4009
	    error (0, 0, _("%s is a 64 bit ELF file.\n"), file_name);
Packit 6c4009
	  else
Packit 6c4009
	    error (0, 0, _("Unknown ELFCLASS in file %s.\n"), file_name);
Packit 6c4009
	}
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (elf_header->e_type != ET_DYN)
Packit 6c4009
    {
Packit 6c4009
      error (0, 0, _("%s is not a shared object file (Type: %d).\n"), file_name,
Packit 6c4009
	     elf_header->e_type);
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Get information from elf program header.  */
Packit 6c4009
  elf_pheader = (ElfW(Phdr) *) (elf_header->e_phoff + file_contents);
Packit 6c4009
  check_ptr (elf_pheader);
Packit 6c4009
Packit 6c4009
  /* The library is an elf library, now search for soname and
Packit 6c4009
     libc5/libc6.  */
Packit 6c4009
  *flag = FLAG_ELF;
Packit 6c4009
Packit 6c4009
  dynamic_addr = 0;
Packit 6c4009
  dynamic_size = 0;
Packit 6c4009
  program_interpreter = NULL;
Packit 6c4009
  for (i = 0, segment = elf_pheader;
Packit 6c4009
       i < elf_header->e_phnum; i++, segment++)
Packit 6c4009
    {
Packit 6c4009
      check_ptr (segment);
Packit 6c4009
Packit 6c4009
      switch (segment->p_type)
Packit 6c4009
	{
Packit 6c4009
	case PT_DYNAMIC:
Packit 6c4009
	  if (dynamic_addr)
Packit 6c4009
	    error (0, 0, _("more than one dynamic segment\n"));
Packit 6c4009
Packit 6c4009
	  dynamic_addr = segment->p_offset;
Packit 6c4009
	  dynamic_size = segment->p_filesz;
Packit 6c4009
	  break;
Packit 6c4009
Packit 6c4009
	case PT_INTERP:
Packit 6c4009
	  program_interpreter = (char *) (file_contents + segment->p_offset);
Packit 6c4009
	  check_ptr (program_interpreter);
Packit 6c4009
Packit 6c4009
	  /* Check if this is enough to classify the binary.  */
Packit 6c4009
	  for (j = 0; j < sizeof (interpreters) / sizeof (interpreters [0]);
Packit 6c4009
	       ++j)
Packit 6c4009
	    if (strcmp (program_interpreter, interpreters[j].soname) == 0)
Packit 6c4009
	      {
Packit 6c4009
		*flag = interpreters[j].flag;
Packit 6c4009
		break;
Packit 6c4009
	      }
Packit 6c4009
	  break;
Packit 6c4009
Packit 6c4009
	case PT_NOTE:
Packit 6c4009
	  if (!*osversion && segment->p_filesz >= 32 && segment->p_align >= 4)
Packit 6c4009
	    {
Packit 6c4009
	      ElfW(Word) *abi_note = (ElfW(Word) *) (file_contents
Packit 6c4009
						     + segment->p_offset);
Packit 6c4009
	      ElfW(Addr) size = segment->p_filesz;
Packit 6c4009
	      /* NB: Some PT_NOTE segment may have alignment value of 0
Packit 6c4009
		 or 1.  gABI specifies that PT_NOTE segments should be
Packit 6c4009
		 aligned to 4 bytes in 32-bit objects and to 8 bytes in
Packit 6c4009
		 64-bit objects.  As a Linux extension, we also support
Packit 6c4009
		 4 byte alignment in 64-bit objects.  If p_align is less
Packit 6c4009
		 than 4, we treate alignment as 4 bytes since some note
Packit 6c4009
		 segments have 0 or 1 byte alignment.   */
Packit 6c4009
	      ElfW(Addr) align = segment->p_align;
Packit 6c4009
	      if (align < 4)
Packit 6c4009
		align = 4;
Packit 6c4009
	      else if (align != 4 && align != 8)
Packit 6c4009
		continue;
Packit 6c4009
Packit 6c4009
	      while (abi_note [0] != 4 || abi_note [1] != 16
Packit 6c4009
		     || abi_note [2] != 1
Packit 6c4009
		     || memcmp (abi_note + 3, "GNU", 4) != 0)
Packit 6c4009
		{
Packit 6c4009
		  ElfW(Addr) note_size
Packit 6c4009
		    = ELF_NOTE_NEXT_OFFSET (abi_note[0], abi_note[1],
Packit 6c4009
					    align);
Packit 6c4009
Packit 6c4009
		  if (size - 32 < note_size || note_size == 0)
Packit 6c4009
		    {
Packit 6c4009
		      size = 0;
Packit 6c4009
		      break;
Packit 6c4009
		    }
Packit 6c4009
		  size -= note_size;
Packit 6c4009
		  abi_note = (void *) abi_note + note_size;
Packit 6c4009
		}
Packit 6c4009
Packit 6c4009
	      if (size == 0)
Packit 6c4009
		break;
Packit 6c4009
Packit 6c4009
	      *osversion = (abi_note [4] << 24) |
Packit 6c4009
			   ((abi_note [5] & 0xff) << 16) |
Packit 6c4009
			   ((abi_note [6] & 0xff) << 8) |
Packit 6c4009
			   (abi_note [7] & 0xff);
Packit 6c4009
	    }
Packit 6c4009
	  break;
Packit 6c4009
Packit 6c4009
	default:
Packit 6c4009
	  break;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Now we can read the dynamic sections.  */
Packit 6c4009
  if (dynamic_size == 0)
Packit 6c4009
    return 1;
Packit 6c4009
Packit 6c4009
  dynamic_segment = (ElfW(Dyn) *) (file_contents + dynamic_addr);
Packit 6c4009
  check_ptr (dynamic_segment);
Packit 6c4009
Packit 6c4009
  /* Find the string table.  */
Packit 6c4009
  dynamic_strings = NULL;
Packit 6c4009
  for (dyn_entry = dynamic_segment; dyn_entry->d_tag != DT_NULL;
Packit 6c4009
       ++dyn_entry)
Packit 6c4009
    {
Packit 6c4009
      check_ptr (dyn_entry);
Packit 6c4009
      if (dyn_entry->d_tag == DT_STRTAB)
Packit 6c4009
	{
Packit Service d1d1a1
	  /* Find the file offset of the segment containing the dynamic
Packit Service d1d1a1
	     string table.  */
Packit Service d1d1a1
	  ElfW(Off) loadoff = -1;
Packit Service d1d1a1
	  for (i = 0, segment = elf_pheader;
Packit Service d1d1a1
	       i < elf_header->e_phnum; i++, segment++)
Packit Service d1d1a1
	    {
Packit Service d1d1a1
	      if (segment->p_type == PT_LOAD
Packit Service d1d1a1
		  && dyn_entry->d_un.d_val >= segment->p_vaddr
Packit Service d1d1a1
		  && (dyn_entry->d_un.d_val - segment->p_vaddr
Packit Service d1d1a1
		      < segment->p_filesz))
Packit Service d1d1a1
		{
Packit Service d1d1a1
		  loadoff = segment->p_vaddr - segment->p_offset;
Packit Service d1d1a1
		  break;
Packit Service d1d1a1
		}
Packit Service d1d1a1
	    }
Packit Service d1d1a1
	  if (loadoff == (ElfW(Off)) -1)
Packit Service d1d1a1
	    {
Packit Service d1d1a1
	      /* Very strange. */
Packit Service d1d1a1
	      loadoff = 0;
Packit Service d1d1a1
	    }
Packit Service d1d1a1
Packit Service d1d1a1
	  dynamic_strings = (char *) (file_contents + dyn_entry->d_un.d_val
Packit Service d1d1a1
				      - loadoff);
Packit 6c4009
	  check_ptr (dynamic_strings);
Packit 6c4009
	  break;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (dynamic_strings == NULL)
Packit 6c4009
    return 1;
Packit 6c4009
Packit 6c4009
  /* Now read the DT_NEEDED and DT_SONAME entries.  */
Packit 6c4009
  for (dyn_entry = dynamic_segment; dyn_entry->d_tag != DT_NULL;
Packit 6c4009
       ++dyn_entry)
Packit 6c4009
    {
Packit 6c4009
      if (dyn_entry->d_tag == DT_NEEDED || dyn_entry->d_tag == DT_SONAME)
Packit 6c4009
	{
Packit 6c4009
	  char *name = dynamic_strings + dyn_entry->d_un.d_val;
Packit 6c4009
	  check_ptr (name);
Packit 6c4009
Packit 6c4009
	  if (dyn_entry->d_tag == DT_NEEDED)
Packit 6c4009
	    {
Packit 6c4009
Packit 6c4009
	      if (*flag == FLAG_ELF)
Packit 6c4009
		{
Packit 6c4009
		  /* Check if this is enough to classify the binary.  */
Packit 6c4009
		  for (j = 0;
Packit 6c4009
		       j < sizeof (known_libs) / sizeof (known_libs [0]);
Packit 6c4009
		       ++j)
Packit 6c4009
		    if (strcmp (name, known_libs [j].soname) == 0)
Packit 6c4009
		      {
Packit 6c4009
			*flag = known_libs [j].flag;
Packit 6c4009
			break;
Packit 6c4009
		      }
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  else if (dyn_entry->d_tag == DT_SONAME)
Packit 6c4009
	    *soname = xstrdup (name);
Packit 6c4009
Packit 6c4009
	  /* Do we have everything we need?  */
Packit 6c4009
	  if (*soname && *flag != FLAG_ELF)
Packit 6c4009
	    return 0;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}