rtld: properly handle root directory in load path (bug 30435)
[platform/upstream/glibc.git] / elf / readelflib.c
1 /* Copyright (C) 1999-2023 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 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 (0, 0, _("%s is a 32 bit ELF file.\n"), file_name);
64           else if (elf_header->e_ident [EI_CLASS] == ELFCLASS64)
65             error (0, 0, _("%s is a 64 bit ELF file.\n"), file_name);
66           else
67             error (0, 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 (0, 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 (0, 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
111         case PT_GNU_PROPERTY:
112           /* The NT_GNU_PROPERTY_TYPE_0 note must be aligned to 4 bytes
113              in 32-bit objects and to 8 bytes in 64-bit objects.  Skip
114              notes with incorrect alignment.  */
115           if (segment->p_align == (__ELF_NATIVE_CLASS / 8))
116             {
117               const ElfW(Nhdr) *note = (const void *) (file_contents
118                                                        + segment->p_offset);
119               const ElfW(Addr) size = segment->p_filesz;
120               const ElfW(Addr) align = segment->p_align;
121
122               const ElfW(Addr) start = (ElfW(Addr)) (uintptr_t) note;
123               unsigned int last_type = 0;
124
125               while ((ElfW(Addr)) (uintptr_t) (note + 1) - start < size)
126                 {
127                   /* Find the NT_GNU_PROPERTY_TYPE_0 note.  */
128                   if (note->n_namesz == 4
129                       && note->n_type == NT_GNU_PROPERTY_TYPE_0
130                       && memcmp (note + 1, "GNU", 4) == 0)
131                     {
132                       /* Check for invalid property.  */
133                       if (note->n_descsz < 8
134                           || (note->n_descsz % sizeof (ElfW(Addr))) != 0)
135                         goto done;
136
137                       /* Start and end of property array.  */
138                       unsigned char *ptr = (unsigned char *) (note + 1) + 4;
139                       unsigned char *ptr_end = ptr + note->n_descsz;
140
141                       do
142                         {
143                           unsigned int type = *(unsigned int *) ptr;
144                           unsigned int datasz = *(unsigned int *) (ptr + 4);
145
146                           /* Property type must be in ascending order.  */
147                           if (type < last_type)
148                             goto done;
149
150                           ptr += 8;
151                           if ((ptr + datasz) > ptr_end)
152                             goto done;
153
154                           last_type = type;
155
156                           /* Target specific property processing.
157                              Return value:
158                                false: Continue processing the properties.
159                                true : Stop processing the properties.
160                            */
161                           if (read_gnu_property (isa_level, type,
162                                                  datasz, ptr))
163                             goto done;
164
165                           /* Check the next property item.  */
166                           ptr += ALIGN_UP (datasz, sizeof (ElfW(Addr)));
167                         }
168                       while ((ptr_end - ptr) >= 8);
169
170                       /* Only handle one NT_GNU_PROPERTY_TYPE_0.  */
171                       goto done;
172                     }
173
174                   note = ((const void *) note
175                           + ELF_NOTE_NEXT_OFFSET (note->n_namesz,
176                                                   note->n_descsz,
177                                                   align));
178                 }
179             }
180 done:
181           break;
182
183         default:
184           break;
185         }
186
187     }
188
189   /* Now we can read the dynamic sections.  */
190   if (dynamic_size == 0)
191     return 1;
192
193   dynamic_segment = (ElfW(Dyn) *) (file_contents + dynamic_addr);
194   check_ptr (dynamic_segment);
195
196   /* Find the string table.  */
197   dynamic_strings = NULL;
198   for (dyn_entry = dynamic_segment; dyn_entry->d_tag != DT_NULL;
199        ++dyn_entry)
200     {
201       check_ptr (dyn_entry);
202       if (dyn_entry->d_tag == DT_STRTAB)
203         {
204           /* Find the file offset of the segment containing the dynamic
205              string table.  */
206           ElfW(Off) loadoff = -1;
207           for (i = 0, segment = elf_pheader;
208                i < elf_header->e_phnum; i++, segment++)
209             {
210               if (segment->p_type == PT_LOAD
211                   && dyn_entry->d_un.d_val >= segment->p_vaddr
212                   && (dyn_entry->d_un.d_val - segment->p_vaddr
213                       < segment->p_filesz))
214                 {
215                   loadoff = segment->p_vaddr - segment->p_offset;
216                   break;
217                 }
218             }
219           if (loadoff == (ElfW(Off)) -1)
220             {
221               /* Very strange. */
222               loadoff = 0;
223             }
224
225           dynamic_strings = (char *) (file_contents + dyn_entry->d_un.d_val
226                                       - loadoff);
227           check_ptr (dynamic_strings);
228           break;
229         }
230     }
231
232   if (dynamic_strings == NULL)
233     return 1;
234
235   /* Now read the DT_SONAME entries.  */
236   for (dyn_entry = dynamic_segment; dyn_entry->d_tag != DT_NULL;
237        ++dyn_entry)
238     {
239       if (dyn_entry->d_tag == DT_SONAME)
240         {
241           char *name = dynamic_strings + dyn_entry->d_un.d_val;
242           check_ptr (name);
243           *soname = xstrdup (name);
244           return 0;
245         }
246     }
247
248   return 0;
249 }