xf.li | bdd93d5 | 2023-05-12 07:10:14 -0700 | [diff] [blame] | 1 | /* Machine-dependent program header inspection for the ELF loader. |
| 2 | Copyright (C) 2014-2016 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 | <http://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 */ |