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

source code of glibc/elf/readelflib.c