]> git.ipfire.org Git - thirdparty/glibc.git/blob - sysdeps/mips/dl-machine-reject-phdr.h
Update copyright dates with scripts/update-copyrights
[thirdparty/glibc.git] / sysdeps / mips / dl-machine-reject-phdr.h
1 /* Machine-dependent program header inspection for the ELF loader.
2 Copyright (C) 2014-2024 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19 #ifndef _DL_MACHINE_REJECT_PHDR_H
20 #define _DL_MACHINE_REJECT_PHDR_H 1
21
22 #include <unistd.h>
23 #include <sys/prctl.h>
24
25 #if defined PR_GET_FP_MODE && defined PR_SET_FP_MODE
26 # define HAVE_PRCTL_FP_MODE 1
27 #else
28 # define HAVE_PRCTL_FP_MODE 0
29 #endif
30
31 /* Reject an object with a debug message. */
32 #define REJECT(str, args...) \
33 { \
34 if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS)) \
35 _dl_debug_printf (str, ##args); \
36 return true; \
37 }
38
39 /* Search the program headers for the ABI Flags. */
40
41 static inline const ElfW(Phdr) *
42 find_mips_abiflags (const ElfW(Phdr) *phdr, ElfW(Half) phnum)
43 {
44 const ElfW(Phdr) *ph;
45
46 for (ph = phdr; ph < &phdr[phnum]; ++ph)
47 if (ph->p_type == PT_MIPS_ABIFLAGS)
48 return ph;
49 return NULL;
50 }
51
52 /* Cache the FP ABI value from the PT_MIPS_ABIFLAGS program header. */
53
54 static bool
55 cached_fpabi_reject_phdr_p (struct link_map *l)
56 {
57 if (l->l_mach.fpabi == 0)
58 {
59 const ElfW(Phdr) *ph = find_mips_abiflags (l->l_phdr, l->l_phnum);
60
61 if (ph)
62 {
63 Elf_MIPS_ABIFlags_v0 * mips_abiflags;
64 if (ph->p_filesz < sizeof (Elf_MIPS_ABIFlags_v0))
65 REJECT (" %s: malformed PT_MIPS_ABIFLAGS found\n", l->l_name);
66
67 mips_abiflags = (Elf_MIPS_ABIFlags_v0 *) (l->l_addr + ph->p_vaddr);
68
69 if (__glibc_unlikely (mips_abiflags->flags2 != 0))
70 REJECT (" %s: unknown MIPS.abiflags flags2: %u\n", l->l_name,
71 mips_abiflags->flags2);
72
73 l->l_mach.fpabi = mips_abiflags->fp_abi;
74 l->l_mach.odd_spreg = (mips_abiflags->flags1
75 & MIPS_AFL_FLAGS1_ODDSPREG) != 0;
76 }
77 else
78 {
79 l->l_mach.fpabi = -1;
80 l->l_mach.odd_spreg = true;
81 }
82 }
83 return false;
84 }
85
86 /* Return a description of the specified floating-point ABI. */
87
88 static const char *
89 fpabi_string (int fpabi)
90 {
91 switch (fpabi)
92 {
93 case Val_GNU_MIPS_ABI_FP_ANY:
94 return "Hard or soft float";
95 case Val_GNU_MIPS_ABI_FP_DOUBLE:
96 return "Hard float (double precision)";
97 case Val_GNU_MIPS_ABI_FP_SINGLE:
98 return "Hard float (single precision)";
99 case Val_GNU_MIPS_ABI_FP_SOFT:
100 return "Soft float";
101 case Val_GNU_MIPS_ABI_FP_OLD_64:
102 return "Unsupported FP64";
103 case Val_GNU_MIPS_ABI_FP_XX:
104 return "Hard float (32-bit CPU, Any FPU)";
105 case Val_GNU_MIPS_ABI_FP_64:
106 return "Hard float (32-bit CPU, 64-bit FPU)";
107 case Val_GNU_MIPS_ABI_FP_64A:
108 return "Hard float compat (32-bit CPU, 64-bit FPU)";
109 case -1:
110 return "Double precision, single precision or soft float";
111 default:
112 return "Unknown FP ABI";
113 }
114 }
115
116 /* A structure to describe the requirements of each FP ABI extension.
117 Each field says whether the ABI can be executed in that mode. The FR0 field
118 is actually overloaded and means 'default' FR mode for the ABI. I.e. For
119 O32 it is FR0 and for N32/N64 it is actually FR1. Since this logic is
120 focussed on the intricacies of mode management for O32 we call the field
121 FR0. */
122
123 struct abi_req
124 {
125 bool single;
126 bool soft;
127 bool fr0;
128 bool fr1;
129 bool fre;
130 };
131
132 /* FP ABI requirements for all Val_GNU_MIPS_ABI_FP_* values. */
133
134 static const struct abi_req reqs[Val_GNU_MIPS_ABI_FP_MAX + 1] =
135 {{true, true, true, true, true}, /* Any */
136 {false, false, true, false, true}, /* Double-float */
137 {true, false, false, false, false}, /* Single-float */
138 {false, true, false, false, false}, /* Soft-float */
139 {false, false, false, false, false}, /* old-FP64 */
140 {false, false, true, true, true}, /* FPXX */
141 {false, false, false, true, false}, /* FP64 */
142 {false, false, false, true, true}}; /* FP64A */
143
144 /* FP ABI requirements for objects without a PT_MIPS_ABIFLAGS segment. */
145
146 static const struct abi_req none_req = { true, true, true, false, true };
147
148 /* Return true iff ELF program headers are incompatible with the running
149 host. This verifies that floating-point ABIs are compatible and
150 re-configures the hardware mode if necessary. This code handles both the
151 DT_NEEDED libraries and the dlopen'ed libraries. It also accounts for the
152 impact of dlclose. */
153
154 static bool __attribute_used__
155 elf_machine_reject_phdr_p (const ElfW(Phdr) *phdr, unsigned int phnum,
156 const char *buf, size_t len, struct link_map *map,
157 int fd)
158 {
159 const ElfW(Phdr) *ph = find_mips_abiflags (phdr, phnum);
160 struct link_map *l;
161 Lmid_t nsid;
162 int in_abi = -1;
163 struct abi_req in_req;
164 Elf_MIPS_ABIFlags_v0 mips_abiflags;
165 bool perfect_match = false;
166 #if _MIPS_SIM == _ABIO32
167 unsigned int cur_mode = -1;
168 # if HAVE_PRCTL_FP_MODE
169 bool cannot_mode_switch = false;
170
171 /* Get the current hardware mode. */
172 cur_mode = __prctl (PR_GET_FP_MODE);
173 # endif
174 #endif
175
176 /* Read the attributes section. */
177 if (ph != NULL)
178 {
179 ElfW(Addr) size = sizeof (Elf_MIPS_ABIFlags_v0);
180
181 if (ph->p_filesz < size)
182 REJECT (" contains malformed PT_MIPS_ABIFLAGS\n");
183
184 __lseek (fd, ph->p_offset, SEEK_SET);
185 if (__libc_read (fd, (void *) &mips_abiflags, size) != size)
186 REJECT (" unable to read PT_MIPS_ABIFLAGS\n");
187
188 if (__glibc_unlikely (mips_abiflags.flags2 != 0))
189 REJECT (" unknown MIPS.abiflags flags2: %u\n", mips_abiflags.flags2);
190
191 in_abi = mips_abiflags.fp_abi;
192 }
193
194 /* ANY is compatible with anything. */
195 perfect_match |= (in_abi == Val_GNU_MIPS_ABI_FP_ANY);
196
197 /* Unknown ABIs are rejected. */
198 if (in_abi != -1 && in_abi > Val_GNU_MIPS_ABI_FP_MAX)
199 REJECT (" uses unknown FP ABI: %u\n", in_abi);
200
201 /* Obtain the initial requirements. */
202 in_req = (in_abi == -1) ? none_req : reqs[in_abi];
203
204 /* Check that the new requirement does not conflict with any currently
205 loaded object. */
206 for (nsid = 0; nsid < DL_NNS; ++nsid)
207 for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next)
208 {
209 struct abi_req existing_req;
210
211 if (cached_fpabi_reject_phdr_p (l))
212 return true;
213
214 #if _MIPS_SIM == _ABIO32
215 /* A special case arises for O32 FP64 and FP64A where the kernel
216 pre-dates PT_MIPS_ABIFLAGS. These ABIs will be blindly loaded even
217 if the hardware mode is unavailable or disabled. In this
218 circumstance the prctl call to obtain the current mode will fail.
219 Detect this situation here and reject everything. This will
220 effectively prevent dynamically linked applications from failing in
221 unusual ways but there is nothing we can do to help static
222 applications. */
223 if ((l->l_mach.fpabi == Val_GNU_MIPS_ABI_FP_64A
224 || l->l_mach.fpabi == Val_GNU_MIPS_ABI_FP_64)
225 && cur_mode == -1)
226 REJECT (" found %s running in the wrong mode\n",
227 fpabi_string (l->l_mach.fpabi));
228 #endif
229
230 /* Found a perfect match, success. */
231 perfect_match |= (in_abi == l->l_mach.fpabi);
232
233 /* Unknown ABIs are rejected. */
234 if (l->l_mach.fpabi != -1 && l->l_mach.fpabi > Val_GNU_MIPS_ABI_FP_MAX)
235 REJECT (" found unknown FP ABI: %u\n", l->l_mach.fpabi);
236
237 existing_req = (l->l_mach.fpabi == -1 ? none_req
238 : reqs[l->l_mach.fpabi]);
239
240 /* Merge requirements. */
241 in_req.soft &= existing_req.soft;
242 in_req.single &= existing_req.single;
243 in_req.fr0 &= existing_req.fr0;
244 in_req.fr1 &= existing_req.fr1;
245 in_req.fre &= existing_req.fre;
246
247 /* If there is at least one mode which is still usable then the new
248 object can be loaded. */
249 if (in_req.single || in_req.soft || in_req.fr1 || in_req.fr0
250 || in_req.fre)
251 {
252 #if _MIPS_SIM == _ABIO32 && HAVE_PRCTL_FP_MODE
253 /* Account for loaded ABIs which prohibit mode switching. */
254 if (l->l_mach.fpabi == Val_GNU_MIPS_ABI_FP_XX)
255 cannot_mode_switch |= l->l_mach.odd_spreg;
256 #endif
257 }
258 else
259 REJECT (" uses %s, already loaded %s\n",
260 fpabi_string (in_abi),
261 fpabi_string (l->l_mach.fpabi));
262 }
263
264 #if _MIPS_SIM == _ABIO32
265 /* At this point we know that the newly loaded object is compatible with all
266 existing objects but the hardware mode may not be correct. */
267 if ((in_req.fr1 || in_req.fre || in_req.fr0)
268 && !perfect_match)
269 {
270 if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
271 _dl_debug_printf (" needs %s%s mode\n", in_req.fr0 ? "FR0 or " : "",
272 (in_req.fre && !in_req.fr1) ? "FRE" : "FR1");
273
274 /* If the PR_GET_FP_MODE is not supported then only FR0 is available.
275 If the overall requirements cannot be met by FR0 then reject the
276 object. */
277 if (cur_mode == -1)
278 return !in_req.fr0;
279
280 # if HAVE_PRCTL_FP_MODE
281 {
282 unsigned int fr1_mode = PR_FP_MODE_FR;
283
284 /* It is not possible to change the mode of a thread which may be
285 executing FPXX code with odd-singles. If an FPXX object with
286 odd-singles is loaded then just check the current mode is OK. This
287 can be either the FR1 mode or FR0 if the requirements are met by
288 FR0. */
289 if (cannot_mode_switch)
290 return (!(in_req.fre && cur_mode == (PR_FP_MODE_FR | PR_FP_MODE_FRE))
291 && !(in_req.fr1 && cur_mode == PR_FP_MODE_FR)
292 && !(in_req.fr0 && cur_mode == 0));
293
294 /* If the overall requirements can be satisfied by FRE but not FR1 then
295 fr1_mode must become FRE. */
296 if (in_req.fre && !in_req.fr1)
297 fr1_mode |= PR_FP_MODE_FRE;
298
299 /* Set the new mode. Use fr1_mode if the requirements cannot be met by
300 FR0. */
301 if (!in_req.fr0)
302 return __prctl (PR_SET_FP_MODE, fr1_mode) != 0;
303 else if (__prctl (PR_SET_FP_MODE, /* fr0_mode */ 0) != 0)
304 {
305 /* Setting FR0 can validly fail on an R6 core so retry with the FR1
306 mode as a fall back. */
307 if (errno != ENOTSUP)
308 return true;
309
310 return __prctl (PR_SET_FP_MODE, fr1_mode) != 0;
311 }
312 }
313 # endif /* HAVE_PRCTL_FP_MODE */
314 }
315 #endif /* _MIPS_SIM == _ABIO32 */
316
317 return false;
318 }
319
320 #endif /* dl-machine-reject-phdr.h */