universally apply our cflags (no vsx, no altivec..)
[glibc.git] / elf / readelflib.c
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) \
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
42 process_elf_file (const char *file_name, const char *lib, int *flag,
43 unsigned int *isa_level, char **soname, void *file_contents,
44 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
59 if (elf_header->e_ident [EI_CLASS] != ElfW (CLASS))
60 {
61 if (opt_verbose)
62 {
63 if (elf_header->e_ident [EI_CLASS] == ELFCLASS32)
64 error (0, 0, _("%s is a 32 bit ELF file.\n"), file_name);
65 else if (elf_header->e_ident [EI_CLASS] == ELFCLASS64)
66 error (0, 0, _("%s is a 64 bit ELF file.\n"), file_name);
67 else
68 error (0, 0, _("Unknown ELFCLASS in file %s.\n"), file_name);
69 }
70 return 1;
71 }
72
73 if (elf_header->e_type != ET_DYN)
74 {
75 error (0, 0, _("%s is not a shared object file (Type: %d).\n"), file_name,
76 elf_header->e_type);
77 return 1;
78 }
79
80 /* Get information from elf program header. */
81 elf_pheader = (ElfW(Phdr) *) (elf_header->e_phoff + file_contents);
82 check_ptr (elf_pheader);
83
84 /* The library is an elf library, now search for soname and
85 libc5/libc6. */
86 *flag = FLAG_ELF;
87
88 /* The default ISA level is 0. */
89 *isa_level = 0;
90
91 dynamic_addr = 0;
92 dynamic_size = 0;
93 program_interpreter = NULL;
94 for (i = 0, segment = elf_pheader;
95 i < elf_header->e_phnum; i++, segment++)
96 {
97 check_ptr (segment);
98
99 switch (segment->p_type)
100 {
101 case PT_DYNAMIC:
102 if (dynamic_addr)
103 error (0, 0, _("more than one dynamic segment\n"));
104
105 dynamic_addr = segment->p_offset;
106 dynamic_size = segment->p_filesz;
107 break;
108
109 case PT_INTERP:
110 program_interpreter = (char *) (file_contents + segment->p_offset);
111 check_ptr (program_interpreter);
112
113 /* Check if this is enough to classify the binary. */
114 for (j = 0; j < sizeof (interpreters) / sizeof (interpreters [0]);
115 ++j)
116 if (strcmp (program_interpreter, interpreters[j].soname) == 0)
117 {
118 *flag = interpreters[j].flag;
119 break;
120 }
121 break;
122
123 case PT_GNU_PROPERTY:
124 /* The NT_GNU_PROPERTY_TYPE_0 note must be aligned to 4 bytes
125 in 32-bit objects and to 8 bytes in 64-bit objects. Skip
126 notes with incorrect alignment. */
127 if (segment->p_align == (__ELF_NATIVE_CLASS / 8))
128 {
129 const ElfW(Nhdr) *note = (const void *) (file_contents
130 + segment->p_offset);
131 const ElfW(Addr) size = segment->p_filesz;
132 const ElfW(Addr) align = segment->p_align;
133
134 const ElfW(Addr) start = (ElfW(Addr)) (uintptr_t) note;
135 unsigned int last_type = 0;
136
137 while ((ElfW(Addr)) (uintptr_t) (note + 1) - start < size)
138 {
139 /* Find the NT_GNU_PROPERTY_TYPE_0 note. */
140 if (note->n_namesz == 4
141 && note->n_type == NT_GNU_PROPERTY_TYPE_0
142 && memcmp (note + 1, "GNU", 4) == 0)
143 {
144 /* Check for invalid property. */
145 if (note->n_descsz < 8
146 || (note->n_descsz % sizeof (ElfW(Addr))) != 0)
147 goto done;
148
149 /* Start and end of property array. */
150 unsigned char *ptr = (unsigned char *) (note + 1) + 4;
151 unsigned char *ptr_end = ptr + note->n_descsz;
152
153 do
154 {
155 unsigned int type = *(unsigned int *) ptr;
156 unsigned int datasz = *(unsigned int *) (ptr + 4);
157
158 /* Property type must be in ascending order. */
159 if (type < last_type)
160 goto done;
161
162 ptr += 8;
163 if ((ptr + datasz) > ptr_end)
164 goto done;
165
166 last_type = type;
167
168 /* Target specific property processing.
169 Return value:
170 false: Continue processing the properties.
171 true : Stop processing the properties.
172 */
173 if (read_gnu_property (isa_level, type,
174 datasz, ptr))
175 goto done;
176
177 /* Check the next property item. */
178 ptr += ALIGN_UP (datasz, sizeof (ElfW(Addr)));
179 }
180 while ((ptr_end - ptr) >= 8);
181
182 /* Only handle one NT_GNU_PROPERTY_TYPE_0. */
183 goto done;
184 }
185
186 note = ((const void *) note
187 + ELF_NOTE_NEXT_OFFSET (note->n_namesz,
188 note->n_descsz,
189 align));
190 }
191 }
192 done:
193 break;
194
195 default:
196 break;
197 }
198
199 }
200
201 /* Now we can read the dynamic sections. */
202 if (dynamic_size == 0)
203 return 1;
204
205 dynamic_segment = (ElfW(Dyn) *) (file_contents + dynamic_addr);
206 check_ptr (dynamic_segment);
207
208 /* Find the string table. */
209 dynamic_strings = NULL;
210 for (dyn_entry = dynamic_segment; dyn_entry->d_tag != DT_NULL;
211 ++dyn_entry)
212 {
213 check_ptr (dyn_entry);
214 if (dyn_entry->d_tag == DT_STRTAB)
215 {
216 /* Find the file offset of the segment containing the dynamic
217 string table. */
218 ElfW(Off) loadoff = -1;
219 for (i = 0, segment = elf_pheader;
220 i < elf_header->e_phnum; i++, segment++)
221 {
222 if (segment->p_type == PT_LOAD
223 && dyn_entry->d_un.d_val >= segment->p_vaddr
224 && (dyn_entry->d_un.d_val - segment->p_vaddr
225 < segment->p_filesz))
226 {
227 loadoff = segment->p_vaddr - segment->p_offset;
228 break;
229 }
230 }
231 if (loadoff == (ElfW(Off)) -1)
232 {
233 /* Very strange. */
234 loadoff = 0;
235 }
236
237 dynamic_strings = (char *) (file_contents + dyn_entry->d_un.d_val
238 - loadoff);
239 check_ptr (dynamic_strings);
240 break;
241 }
242 }
243
244 if (dynamic_strings == NULL)
245 return 1;
246
247 /* Now read the DT_NEEDED and DT_SONAME entries. */
248 for (dyn_entry = dynamic_segment; dyn_entry->d_tag != DT_NULL;
249 ++dyn_entry)
250 {
251 if (dyn_entry->d_tag == DT_NEEDED || dyn_entry->d_tag == DT_SONAME)
252 {
253 char *name = dynamic_strings + dyn_entry->d_un.d_val;
254 check_ptr (name);
255
256 if (dyn_entry->d_tag == DT_NEEDED)
257 {
258
259 if (*flag == FLAG_ELF)
260 {
261 /* Check if this is enough to classify the binary. */
262 for (j = 0;
263 j < sizeof (known_libs) / sizeof (known_libs [0]);
264 ++j)
265 if (strcmp (name, known_libs [j].soname) == 0)
266 {
267 *flag = known_libs [j].flag;
268 break;
269 }
270 }
271 }
272
273 else if (dyn_entry->d_tag == DT_SONAME)
274 *soname = xstrdup (name);
275
276 /* Do we have everything we need? */
277 if (*soname && *flag != FLAG_ELF)
278 return 0;
279 }
280 }
281
282 return 0;
283 }