| 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 */ |