1/* Copyright (C) 1999-2022 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) \
29do \
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. */
41int
42process_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 (status: 0, errnum: 0, _("%s is a 32 bit ELF file.\n"), file_name);
66 else if (elf_header->e_ident [EI_CLASS] == ELFCLASS64)
67 error (status: 0, errnum: 0, _("%s is a 64 bit ELF file.\n"), file_name);
68 else
69 error (status: 0, errnum: 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 (status: 0, errnum: 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 (status: 0, errnum: 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 (s1: program_interpreter, s2: 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 (s1: abi_note + 3, s2: "GNU", n: 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 (s1: note + 1, s2: "GNU", n: 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 (isal_level: isa_level, type,
221 datasz, data: 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 }
239done:
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 (s1: name, s2: 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

source code of glibc/elf/readelflib.c