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