(root)/
glibc-2.38/
sysdeps/
mips/
dl-machine-reject-phdr.h
       1  /* Machine-dependent program header inspection for the ELF loader.
       2     Copyright (C) 2014-2023 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 = 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 */