Warning: This file is not a C or C++ file. It does not have highlighting.
1 | /* Machine-dependent program header inspection for the ELF loader. |
---|---|
2 | Copyright (C) 2014-2022 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, uint_fast16_t 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 = NULL; |
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 = ph->p_filesz; |
180 | |
181 | if (ph->p_offset + size <= len) |
182 | mips_abiflags = (Elf_MIPS_ABIFlags_v0 *) (buf + ph->p_offset); |
183 | else |
184 | { |
185 | mips_abiflags = alloca (size); |
186 | __lseek (fd, ph->p_offset, SEEK_SET); |
187 | if (__libc_read (fd, (void *) mips_abiflags, size) != size) |
188 | REJECT (" unable to read PT_MIPS_ABIFLAGS\n"); |
189 | } |
190 | |
191 | if (size < sizeof (Elf_MIPS_ABIFlags_v0)) |
192 | REJECT (" contains malformed PT_MIPS_ABIFLAGS\n"); |
193 | |
194 | if (__glibc_unlikely (mips_abiflags->flags2 != 0)) |
195 | REJECT (" unknown MIPS.abiflags flags2: %u\n", mips_abiflags->flags2); |
196 | |
197 | in_abi = mips_abiflags->fp_abi; |
198 | } |
199 | |
200 | /* ANY is compatible with anything. */ |
201 | perfect_match |= (in_abi == Val_GNU_MIPS_ABI_FP_ANY); |
202 | |
203 | /* Unknown ABIs are rejected. */ |
204 | if (in_abi != -1 && in_abi > Val_GNU_MIPS_ABI_FP_MAX) |
205 | REJECT (" uses unknown FP ABI: %u\n", in_abi); |
206 | |
207 | /* Obtain the initial requirements. */ |
208 | in_req = (in_abi == -1) ? none_req : reqs[in_abi]; |
209 | |
210 | /* Check that the new requirement does not conflict with any currently |
211 | loaded object. */ |
212 | for (nsid = 0; nsid < DL_NNS; ++nsid) |
213 | for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next) |
214 | { |
215 | struct abi_req existing_req; |
216 | |
217 | if (cached_fpabi_reject_phdr_p (l)) |
218 | return true; |
219 | |
220 | #if _MIPS_SIM == _ABIO32 |
221 | /* A special case arises for O32 FP64 and FP64A where the kernel |
222 | pre-dates PT_MIPS_ABIFLAGS. These ABIs will be blindly loaded even |
223 | if the hardware mode is unavailable or disabled. In this |
224 | circumstance the prctl call to obtain the current mode will fail. |
225 | Detect this situation here and reject everything. This will |
226 | effectively prevent dynamically linked applications from failing in |
227 | unusual ways but there is nothing we can do to help static |
228 | applications. */ |
229 | if ((l->l_mach.fpabi == Val_GNU_MIPS_ABI_FP_64A |
230 | || l->l_mach.fpabi == Val_GNU_MIPS_ABI_FP_64) |
231 | && cur_mode == -1) |
232 | REJECT (" found %s running in the wrong mode\n", |
233 | fpabi_string (l->l_mach.fpabi)); |
234 | #endif |
235 | |
236 | /* Found a perfect match, success. */ |
237 | perfect_match |= (in_abi == l->l_mach.fpabi); |
238 | |
239 | /* Unknown ABIs are rejected. */ |
240 | if (l->l_mach.fpabi != -1 && l->l_mach.fpabi > Val_GNU_MIPS_ABI_FP_MAX) |
241 | REJECT (" found unknown FP ABI: %u\n", l->l_mach.fpabi); |
242 | |
243 | existing_req = (l->l_mach.fpabi == -1 ? none_req |
244 | : reqs[l->l_mach.fpabi]); |
245 | |
246 | /* Merge requirements. */ |
247 | in_req.soft &= existing_req.soft; |
248 | in_req.single &= existing_req.single; |
249 | in_req.fr0 &= existing_req.fr0; |
250 | in_req.fr1 &= existing_req.fr1; |
251 | in_req.fre &= existing_req.fre; |
252 | |
253 | /* If there is at least one mode which is still usable then the new |
254 | object can be loaded. */ |
255 | if (in_req.single || in_req.soft || in_req.fr1 || in_req.fr0 |
256 | || in_req.fre) |
257 | { |
258 | #if _MIPS_SIM == _ABIO32 && HAVE_PRCTL_FP_MODE |
259 | /* Account for loaded ABIs which prohibit mode switching. */ |
260 | if (l->l_mach.fpabi == Val_GNU_MIPS_ABI_FP_XX) |
261 | cannot_mode_switch |= l->l_mach.odd_spreg; |
262 | #endif |
263 | } |
264 | else |
265 | REJECT (" uses %s, already loaded %s\n", |
266 | fpabi_string (in_abi), |
267 | fpabi_string (l->l_mach.fpabi)); |
268 | } |
269 | |
270 | #if _MIPS_SIM == _ABIO32 |
271 | /* At this point we know that the newly loaded object is compatible with all |
272 | existing objects but the hardware mode may not be correct. */ |
273 | if ((in_req.fr1 || in_req.fre || in_req.fr0) |
274 | && !perfect_match) |
275 | { |
276 | if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS)) |
277 | _dl_debug_printf (" needs %s%s mode\n", in_req.fr0 ? "FR0 or " : "", |
278 | (in_req.fre && !in_req.fr1) ? "FRE" : "FR1"); |
279 | |
280 | /* If the PR_GET_FP_MODE is not supported then only FR0 is available. |
281 | If the overall requirements cannot be met by FR0 then reject the |
282 | object. */ |
283 | if (cur_mode == -1) |
284 | return !in_req.fr0; |
285 | |
286 | # if HAVE_PRCTL_FP_MODE |
287 | { |
288 | unsigned int fr1_mode = PR_FP_MODE_FR; |
289 | |
290 | /* It is not possible to change the mode of a thread which may be |
291 | executing FPXX code with odd-singles. If an FPXX object with |
292 | odd-singles is loaded then just check the current mode is OK. This |
293 | can be either the FR1 mode or FR0 if the requirements are met by |
294 | FR0. */ |
295 | if (cannot_mode_switch) |
296 | return (!(in_req.fre && cur_mode == (PR_FP_MODE_FR | PR_FP_MODE_FRE)) |
297 | && !(in_req.fr1 && cur_mode == PR_FP_MODE_FR) |
298 | && !(in_req.fr0 && cur_mode == 0)); |
299 | |
300 | /* If the overall requirements can be satisfied by FRE but not FR1 then |
301 | fr1_mode must become FRE. */ |
302 | if (in_req.fre && !in_req.fr1) |
303 | fr1_mode |= PR_FP_MODE_FRE; |
304 | |
305 | /* Set the new mode. Use fr1_mode if the requirements cannot be met by |
306 | FR0. */ |
307 | if (!in_req.fr0) |
308 | return __prctl (PR_SET_FP_MODE, fr1_mode) != 0; |
309 | else if (__prctl (PR_SET_FP_MODE, /* fr0_mode */ 0) != 0) |
310 | { |
311 | /* Setting FR0 can validly fail on an R6 core so retry with the FR1 |
312 | mode as a fall back. */ |
313 | if (errno != ENOTSUP) |
314 | return true; |
315 | |
316 | return __prctl (PR_SET_FP_MODE, fr1_mode) != 0; |
317 | } |
318 | } |
319 | # endif /* HAVE_PRCTL_FP_MODE */ |
320 | } |
321 | #endif /* _MIPS_SIM == _ABIO32 */ |
322 | |
323 | return false; |
324 | } |
325 | |
326 | #endif /* dl-machine-reject-phdr.h */ |
327 |
Warning: This file is not a C or C++ file. It does not have highlighting.