1 /* Copyright (C) 1999-2021 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3 
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8 
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, see
16    <https://www.gnu.org/licenses/>.  */
17 
18 #include <elf-read-prop.h>
19 
20 /* This code is a heavily simplified version of the readelf program
21    that's part of the current binutils development version.  For architectures
22    which need to handle both 32bit and 64bit ELF libraries,  this file is
23    included twice for each arch size.  */
24 
25 /* check_ptr checks that a pointer is in the mmaped file and doesn't
26    point outside it.  */
27 #undef check_ptr
28 #define check_ptr(ptr)						\
29 do								\
30   {								\
31     if ((void *)(ptr) < file_contents				\
32 	|| (void *)(ptr) > (file_contents+file_length))		\
33       {								\
34 	error (0, 0, _("file %s is truncated\n"), file_name);	\
35 	return 1;						\
36       }								\
37   }								\
38  while (0);
39 
40 /* Returns 0 if everything is ok, != 0 in case of error.  */
41 int
process_elf_file(const char * file_name,const char * lib,int * flag,unsigned int * osversion,unsigned int * isa_level,char ** soname,void * file_contents,size_t file_length)42 process_elf_file (const char *file_name, const char *lib, int *flag,
43 		  unsigned int *osversion, unsigned int *isa_level,
44 		  char **soname, void *file_contents, size_t file_length)
45 {
46   int i;
47   unsigned int j;
48   unsigned int dynamic_addr;
49   size_t dynamic_size;
50   char *program_interpreter;
51 
52   ElfW(Ehdr) *elf_header;
53   ElfW(Phdr) *elf_pheader, *segment;
54   ElfW(Dyn) *dynamic_segment, *dyn_entry;
55   char *dynamic_strings;
56 
57   elf_header = (ElfW(Ehdr) *) file_contents;
58   *osversion = 0;
59 
60   if (elf_header->e_ident [EI_CLASS] != ElfW (CLASS))
61     {
62       if (opt_verbose)
63 	{
64 	  if (elf_header->e_ident [EI_CLASS] == ELFCLASS32)
65 	    error (0, 0, _("%s is a 32 bit ELF file.\n"), file_name);
66 	  else if (elf_header->e_ident [EI_CLASS] == ELFCLASS64)
67 	    error (0, 0, _("%s is a 64 bit ELF file.\n"), file_name);
68 	  else
69 	    error (0, 0, _("Unknown ELFCLASS in file %s.\n"), file_name);
70 	}
71       return 1;
72     }
73 
74   if (elf_header->e_type != ET_DYN)
75     {
76       error (0, 0, _("%s is not a shared object file (Type: %d).\n"), file_name,
77 	     elf_header->e_type);
78       return 1;
79     }
80 
81   /* Get information from elf program header.  */
82   elf_pheader = (ElfW(Phdr) *) (elf_header->e_phoff + file_contents);
83   check_ptr (elf_pheader);
84 
85   /* The library is an elf library, now search for soname and
86      libc5/libc6.  */
87   *flag = FLAG_ELF;
88 
89   /* The default ISA level is 0.  */
90   *isa_level = 0;
91 
92   dynamic_addr = 0;
93   dynamic_size = 0;
94   program_interpreter = NULL;
95   for (i = 0, segment = elf_pheader;
96        i < elf_header->e_phnum; i++, segment++)
97     {
98       check_ptr (segment);
99 
100       switch (segment->p_type)
101 	{
102 	case PT_DYNAMIC:
103 	  if (dynamic_addr)
104 	    error (0, 0, _("more than one dynamic segment\n"));
105 
106 	  dynamic_addr = segment->p_offset;
107 	  dynamic_size = segment->p_filesz;
108 	  break;
109 
110 	case PT_INTERP:
111 	  program_interpreter = (char *) (file_contents + segment->p_offset);
112 	  check_ptr (program_interpreter);
113 
114 	  /* Check if this is enough to classify the binary.  */
115 	  for (j = 0; j < sizeof (interpreters) / sizeof (interpreters [0]);
116 	       ++j)
117 	    if (strcmp (program_interpreter, interpreters[j].soname) == 0)
118 	      {
119 		*flag = interpreters[j].flag;
120 		break;
121 	      }
122 	  break;
123 
124 	case PT_NOTE:
125 	  if (!*osversion && segment->p_filesz >= 32 && segment->p_align >= 4)
126 	    {
127 	      ElfW(Word) *abi_note = (ElfW(Word) *) (file_contents
128 						     + segment->p_offset);
129 	      ElfW(Addr) size = segment->p_filesz;
130 	      /* NB: Some PT_NOTE segment may have alignment value of 0
131 		 or 1.  gABI specifies that PT_NOTE segments should be
132 		 aligned to 4 bytes in 32-bit objects and to 8 bytes in
133 		 64-bit objects.  As a Linux extension, we also support
134 		 4 byte alignment in 64-bit objects.  If p_align is less
135 		 than 4, we treate alignment as 4 bytes since some note
136 		 segments have 0 or 1 byte alignment.   */
137 	      ElfW(Addr) align = segment->p_align;
138 	      if (align < 4)
139 		align = 4;
140 	      else if (align != 4 && align != 8)
141 		continue;
142 
143 	      while (abi_note [0] != 4 || abi_note [1] != 16
144 		     || abi_note [2] != 1
145 		     || memcmp (abi_note + 3, "GNU", 4) != 0)
146 		{
147 		  ElfW(Addr) note_size
148 		    = ELF_NOTE_NEXT_OFFSET (abi_note[0], abi_note[1],
149 					    align);
150 
151 		  if (size - 32 < note_size || note_size == 0)
152 		    {
153 		      size = 0;
154 		      break;
155 		    }
156 		  size -= note_size;
157 		  abi_note = (void *) abi_note + note_size;
158 		}
159 
160 	      if (size == 0)
161 		break;
162 
163 	      *osversion = ((abi_note [4] << 24)
164 			    | ((abi_note [5] & 0xff) << 16)
165 			    | ((abi_note [6] & 0xff) << 8)
166 			    | (abi_note [7] & 0xff));
167 	    }
168 	  break;
169 
170 	case PT_GNU_PROPERTY:
171 	  /* The NT_GNU_PROPERTY_TYPE_0 note must be aligned to 4 bytes
172 	     in 32-bit objects and to 8 bytes in 64-bit objects.  Skip
173 	     notes with incorrect alignment.  */
174 	  if (segment->p_align == (__ELF_NATIVE_CLASS / 8))
175 	    {
176 	      const ElfW(Nhdr) *note = (const void *) (file_contents
177 						       + segment->p_offset);
178 	      const ElfW(Addr) size = segment->p_filesz;
179 	      const ElfW(Addr) align = segment->p_align;
180 
181 	      const ElfW(Addr) start = (ElfW(Addr)) (uintptr_t) note;
182 	      unsigned int last_type = 0;
183 
184 	      while ((ElfW(Addr)) (uintptr_t) (note + 1) - start < size)
185 		{
186 		  /* Find the NT_GNU_PROPERTY_TYPE_0 note.  */
187 		  if (note->n_namesz == 4
188 		      && note->n_type == NT_GNU_PROPERTY_TYPE_0
189 		      && memcmp (note + 1, "GNU", 4) == 0)
190 		    {
191 		      /* Check for invalid property.  */
192 		      if (note->n_descsz < 8
193 			  || (note->n_descsz % sizeof (ElfW(Addr))) != 0)
194 			goto done;
195 
196 		      /* Start and end of property array.  */
197 		      unsigned char *ptr = (unsigned char *) (note + 1) + 4;
198 		      unsigned char *ptr_end = ptr + note->n_descsz;
199 
200 		      do
201 			{
202 			  unsigned int type = *(unsigned int *) ptr;
203 			  unsigned int datasz = *(unsigned int *) (ptr + 4);
204 
205 			  /* Property type must be in ascending order.  */
206 			  if (type < last_type)
207 			    goto done;
208 
209 			  ptr += 8;
210 			  if ((ptr + datasz) > ptr_end)
211 			    goto done;
212 
213 			  last_type = type;
214 
215 			  /* Target specific property processing.
216 			     Return value:
217 			       false: Continue processing the properties.
218 			       true : Stop processing the properties.
219 			   */
220 			  if (read_gnu_property (isa_level, type,
221 						 datasz, ptr))
222 			    goto done;
223 
224 			  /* Check the next property item.  */
225 			  ptr += ALIGN_UP (datasz, sizeof (ElfW(Addr)));
226 			}
227 		      while ((ptr_end - ptr) >= 8);
228 
229 		      /* Only handle one NT_GNU_PROPERTY_TYPE_0.  */
230 		      goto done;
231 		    }
232 
233 		  note = ((const void *) note
234 			  + ELF_NOTE_NEXT_OFFSET (note->n_namesz,
235 						  note->n_descsz,
236 						  align));
237 		}
238 	    }
239 done:
240 	  break;
241 
242 	default:
243 	  break;
244 	}
245 
246     }
247 
248   /* Now we can read the dynamic sections.  */
249   if (dynamic_size == 0)
250     return 1;
251 
252   dynamic_segment = (ElfW(Dyn) *) (file_contents + dynamic_addr);
253   check_ptr (dynamic_segment);
254 
255   /* Find the string table.  */
256   dynamic_strings = NULL;
257   for (dyn_entry = dynamic_segment; dyn_entry->d_tag != DT_NULL;
258        ++dyn_entry)
259     {
260       check_ptr (dyn_entry);
261       if (dyn_entry->d_tag == DT_STRTAB)
262 	{
263 	  /* Find the file offset of the segment containing the dynamic
264 	     string table.  */
265 	  ElfW(Off) loadoff = -1;
266 	  for (i = 0, segment = elf_pheader;
267 	       i < elf_header->e_phnum; i++, segment++)
268 	    {
269 	      if (segment->p_type == PT_LOAD
270 		  && dyn_entry->d_un.d_val >= segment->p_vaddr
271 		  && (dyn_entry->d_un.d_val - segment->p_vaddr
272 		      < segment->p_filesz))
273 		{
274 		  loadoff = segment->p_vaddr - segment->p_offset;
275 		  break;
276 		}
277 	    }
278 	  if (loadoff == (ElfW(Off)) -1)
279 	    {
280 	      /* Very strange. */
281 	      loadoff = 0;
282 	    }
283 
284 	  dynamic_strings = (char *) (file_contents + dyn_entry->d_un.d_val
285 				      - loadoff);
286 	  check_ptr (dynamic_strings);
287 	  break;
288 	}
289     }
290 
291   if (dynamic_strings == NULL)
292     return 1;
293 
294   /* Now read the DT_NEEDED and DT_SONAME entries.  */
295   for (dyn_entry = dynamic_segment; dyn_entry->d_tag != DT_NULL;
296        ++dyn_entry)
297     {
298       if (dyn_entry->d_tag == DT_NEEDED || dyn_entry->d_tag == DT_SONAME)
299 	{
300 	  char *name = dynamic_strings + dyn_entry->d_un.d_val;
301 	  check_ptr (name);
302 
303 	  if (dyn_entry->d_tag == DT_NEEDED)
304 	    {
305 
306 	      if (*flag == FLAG_ELF)
307 		{
308 		  /* Check if this is enough to classify the binary.  */
309 		  for (j = 0;
310 		       j < sizeof (known_libs) / sizeof (known_libs [0]);
311 		       ++j)
312 		    if (strcmp (name, known_libs [j].soname) == 0)
313 		      {
314 			*flag = known_libs [j].flag;
315 			break;
316 		      }
317 		}
318 	    }
319 
320 	  else if (dyn_entry->d_tag == DT_SONAME)
321 	    *soname = xstrdup (name);
322 
323 	  /* Do we have everything we need?  */
324 	  if (*soname && *flag != FLAG_ELF)
325 	    return 0;
326 	}
327     }
328 
329   return 0;
330 }
331