Fix failing nss/tst-nss-files-hosts-long with local resolver
[glibc.git] / elf / readelflib.c
blob10b10b6080b94152ac61707058fbae150375fad1
1 /* Copyright (C) 1999-2021 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
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.
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.
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/>. */
18 #include <elf-read-prop.h>
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. */
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);
40 /* Returns 0 if everything is ok, != 0 in case of error. */
41 int
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)
46 int i;
47 unsigned int j;
48 unsigned int dynamic_addr;
49 size_t dynamic_size;
50 char *program_interpreter;
52 ElfW(Ehdr) *elf_header;
53 ElfW(Phdr) *elf_pheader, *segment;
54 ElfW(Dyn) *dynamic_segment, *dyn_entry;
55 char *dynamic_strings;
57 elf_header = (ElfW(Ehdr) *) file_contents;
58 *osversion = 0;
60 if (elf_header->e_ident [EI_CLASS] != ElfW (CLASS))
62 if (opt_verbose)
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);
71 return 1;
74 if (elf_header->e_type != ET_DYN)
76 error (0, 0, _("%s is not a shared object file (Type: %d).\n"), file_name,
77 elf_header->e_type);
78 return 1;
81 /* Get information from elf program header. */
82 elf_pheader = (ElfW(Phdr) *) (elf_header->e_phoff + file_contents);
83 check_ptr (elf_pheader);
85 /* The library is an elf library, now search for soname and
86 libc5/libc6. */
87 *flag = FLAG_ELF;
89 /* The default ISA level is 0. */
90 *isa_level = 0;
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++)
98 check_ptr (segment);
100 switch (segment->p_type)
102 case PT_DYNAMIC:
103 if (dynamic_addr)
104 error (0, 0, _("more than one dynamic segment\n"));
106 dynamic_addr = segment->p_offset;
107 dynamic_size = segment->p_filesz;
108 break;
110 case PT_INTERP:
111 program_interpreter = (char *) (file_contents + segment->p_offset);
112 check_ptr (program_interpreter);
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)
119 *flag = interpreters[j].flag;
120 break;
122 break;
124 case PT_NOTE:
125 if (!*osversion && segment->p_filesz >= 32 && segment->p_align >= 4)
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;
143 while (abi_note [0] != 4 || abi_note [1] != 16
144 || abi_note [2] != 1
145 || memcmp (abi_note + 3, "GNU", 4) != 0)
147 ElfW(Addr) note_size
148 = ELF_NOTE_NEXT_OFFSET (abi_note[0], abi_note[1],
149 align);
151 if (size - 32 < note_size || note_size == 0)
153 size = 0;
154 break;
156 size -= note_size;
157 abi_note = (void *) abi_note + note_size;
160 if (size == 0)
161 break;
163 *osversion = ((abi_note [4] << 24)
164 | ((abi_note [5] & 0xff) << 16)
165 | ((abi_note [6] & 0xff) << 8)
166 | (abi_note [7] & 0xff));
168 break;
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))
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;
181 const ElfW(Addr) start = (ElfW(Addr)) (uintptr_t) note;
182 unsigned int last_type = 0;
184 while ((ElfW(Addr)) (uintptr_t) (note + 1) - start < size)
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)
191 /* Check for invalid property. */
192 if (note->n_descsz < 8
193 || (note->n_descsz % sizeof (ElfW(Addr))) != 0)
194 goto done;
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;
202 unsigned int type = *(unsigned int *) ptr;
203 unsigned int datasz = *(unsigned int *) (ptr + 4);
205 /* Property type must be in ascending order. */
206 if (type < last_type)
207 goto done;
209 ptr += 8;
210 if ((ptr + datasz) > ptr_end)
211 goto done;
213 last_type = type;
215 /* Target specific property processing.
216 Return value:
217 false: Continue processing the properties.
218 true : Stop processing the properties.
220 if (read_gnu_property (isa_level, type,
221 datasz, ptr))
222 goto done;
224 /* Check the next property item. */
225 ptr += ALIGN_UP (datasz, sizeof (ElfW(Addr)));
227 while ((ptr_end - ptr) >= 8);
229 /* Only handle one NT_GNU_PROPERTY_TYPE_0. */
230 goto done;
233 note = ((const void *) note
234 + ELF_NOTE_NEXT_OFFSET (note->n_namesz,
235 note->n_descsz,
236 align));
239 done:
240 break;
242 default:
243 break;
248 /* Now we can read the dynamic sections. */
249 if (dynamic_size == 0)
250 return 1;
252 dynamic_segment = (ElfW(Dyn) *) (file_contents + dynamic_addr);
253 check_ptr (dynamic_segment);
255 /* Find the string table. */
256 dynamic_strings = NULL;
257 for (dyn_entry = dynamic_segment; dyn_entry->d_tag != DT_NULL;
258 ++dyn_entry)
260 check_ptr (dyn_entry);
261 if (dyn_entry->d_tag == DT_STRTAB)
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++)
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))
274 loadoff = segment->p_vaddr - segment->p_offset;
275 break;
278 if (loadoff == (ElfW(Off)) -1)
280 /* Very strange. */
281 loadoff = 0;
284 dynamic_strings = (char *) (file_contents + dyn_entry->d_un.d_val
285 - loadoff);
286 check_ptr (dynamic_strings);
287 break;
291 if (dynamic_strings == NULL)
292 return 1;
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)
298 if (dyn_entry->d_tag == DT_NEEDED || dyn_entry->d_tag == DT_SONAME)
300 char *name = dynamic_strings + dyn_entry->d_un.d_val;
301 check_ptr (name);
303 if (dyn_entry->d_tag == DT_NEEDED)
306 if (*flag == FLAG_ELF)
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)
314 *flag = known_libs [j].flag;
315 break;
320 else if (dyn_entry->d_tag == DT_SONAME)
321 *soname = xstrdup (name);
323 /* Do we have everything we need? */
324 if (*soname && *flag != FLAG_ELF)
325 return 0;
329 return 0;