Blame sysdeps/mips/dl-machine-reject-phdr.h

Packit Service 82fcde
/* Machine-dependent program header inspection for the ELF loader.
Packit Service 82fcde
   Copyright (C) 2014-2018 Free Software Foundation, Inc.
Packit Service 82fcde
   This file is part of the GNU C Library.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is free software; you can redistribute it and/or
Packit Service 82fcde
   modify it under the terms of the GNU Lesser General Public
Packit Service 82fcde
   License as published by the Free Software Foundation; either
Packit Service 82fcde
   version 2.1 of the License, or (at your option) any later version.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is distributed in the hope that it will be useful,
Packit Service 82fcde
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 82fcde
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 82fcde
   Lesser General Public License for more details.
Packit Service 82fcde
Packit Service 82fcde
   You should have received a copy of the GNU Lesser General Public
Packit Service 82fcde
   License along with the GNU C Library; if not, see
Packit Service 82fcde
   <http://www.gnu.org/licenses/>.  */
Packit Service 82fcde
Packit Service 82fcde
#ifndef _DL_MACHINE_REJECT_PHDR_H
Packit Service 82fcde
#define _DL_MACHINE_REJECT_PHDR_H 1
Packit Service 82fcde
Packit Service 82fcde
#include <unistd.h>
Packit Service 82fcde
#include <sys/prctl.h>
Packit Service 82fcde
Packit Service 82fcde
#if defined PR_GET_FP_MODE && defined PR_SET_FP_MODE
Packit Service 82fcde
# define HAVE_PRCTL_FP_MODE 1
Packit Service 82fcde
#else
Packit Service 82fcde
# define HAVE_PRCTL_FP_MODE 0
Packit Service 82fcde
#endif
Packit Service 82fcde
Packit Service 82fcde
/* Reject an object with a debug message.  */
Packit Service 82fcde
#define REJECT(str, args...)						      \
Packit Service 82fcde
  {									      \
Packit Service 82fcde
    if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))		      \
Packit Service 82fcde
      _dl_debug_printf (str, ##args);					      \
Packit Service 82fcde
    return true;							      \
Packit Service 82fcde
  }
Packit Service 82fcde
Packit Service 82fcde
/* Search the program headers for the ABI Flags.  */
Packit Service 82fcde
Packit Service 82fcde
static inline const ElfW(Phdr) *
Packit Service 82fcde
find_mips_abiflags (const ElfW(Phdr) *phdr, ElfW(Half) phnum)
Packit Service 82fcde
{
Packit Service 82fcde
  const ElfW(Phdr) *ph;
Packit Service 82fcde
Packit Service 82fcde
  for (ph = phdr; ph < &phdr[phnum]; ++ph)
Packit Service 82fcde
    if (ph->p_type == PT_MIPS_ABIFLAGS)
Packit Service 82fcde
      return ph;
Packit Service 82fcde
  return NULL;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Cache the FP ABI value from the PT_MIPS_ABIFLAGS program header.  */
Packit Service 82fcde
Packit Service 82fcde
static bool
Packit Service 82fcde
cached_fpabi_reject_phdr_p (struct link_map *l)
Packit Service 82fcde
{
Packit Service 82fcde
  if (l->l_mach.fpabi == 0)
Packit Service 82fcde
    {
Packit Service 82fcde
      const ElfW(Phdr) *ph = find_mips_abiflags (l->l_phdr, l->l_phnum);
Packit Service 82fcde
Packit Service 82fcde
      if (ph)
Packit Service 82fcde
	{
Packit Service 82fcde
	  Elf_MIPS_ABIFlags_v0 * mips_abiflags;
Packit Service 82fcde
	  if (ph->p_filesz < sizeof (Elf_MIPS_ABIFlags_v0))
Packit Service 82fcde
	    REJECT ("   %s: malformed PT_MIPS_ABIFLAGS found\n", l->l_name);
Packit Service 82fcde
Packit Service 82fcde
	  mips_abiflags = (Elf_MIPS_ABIFlags_v0 *) (l->l_addr + ph->p_vaddr);
Packit Service 82fcde
Packit Service 82fcde
	  if (__glibc_unlikely (mips_abiflags->flags2 != 0))
Packit Service 82fcde
	    REJECT ("   %s: unknown MIPS.abiflags flags2: %u\n", l->l_name,
Packit Service 82fcde
		    mips_abiflags->flags2);
Packit Service 82fcde
Packit Service 82fcde
	  l->l_mach.fpabi = mips_abiflags->fp_abi;
Packit Service 82fcde
	  l->l_mach.odd_spreg = (mips_abiflags->flags1
Packit Service 82fcde
				 & MIPS_AFL_FLAGS1_ODDSPREG) != 0;
Packit Service 82fcde
	}
Packit Service 82fcde
      else
Packit Service 82fcde
	{
Packit Service 82fcde
	  l->l_mach.fpabi = -1;
Packit Service 82fcde
	  l->l_mach.odd_spreg = true;
Packit Service 82fcde
	}
Packit Service 82fcde
    }
Packit Service 82fcde
  return false;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Return a description of the specified floating-point ABI.  */
Packit Service 82fcde
Packit Service 82fcde
static const char *
Packit Service 82fcde
fpabi_string (int fpabi)
Packit Service 82fcde
{
Packit Service 82fcde
  switch (fpabi)
Packit Service 82fcde
    {
Packit Service 82fcde
    case Val_GNU_MIPS_ABI_FP_ANY:
Packit Service 82fcde
      return "Hard or soft float";
Packit Service 82fcde
    case Val_GNU_MIPS_ABI_FP_DOUBLE:
Packit Service 82fcde
      return "Hard float (double precision)";
Packit Service 82fcde
    case Val_GNU_MIPS_ABI_FP_SINGLE:
Packit Service 82fcde
      return "Hard float (single precision)";
Packit Service 82fcde
    case Val_GNU_MIPS_ABI_FP_SOFT:
Packit Service 82fcde
      return "Soft float";
Packit Service 82fcde
    case Val_GNU_MIPS_ABI_FP_OLD_64:
Packit Service 82fcde
      return "Unsupported FP64";
Packit Service 82fcde
    case Val_GNU_MIPS_ABI_FP_XX:
Packit Service 82fcde
      return "Hard float (32-bit CPU, Any FPU)";
Packit Service 82fcde
    case Val_GNU_MIPS_ABI_FP_64:
Packit Service 82fcde
      return "Hard float (32-bit CPU, 64-bit FPU)";
Packit Service 82fcde
    case Val_GNU_MIPS_ABI_FP_64A:
Packit Service 82fcde
      return "Hard float compat (32-bit CPU, 64-bit FPU)";
Packit Service 82fcde
    case -1:
Packit Service 82fcde
      return "Double precision, single precision or soft float";
Packit Service 82fcde
    default:
Packit Service 82fcde
      return "Unknown FP ABI";
Packit Service 82fcde
    }
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* A structure to describe the requirements of each FP ABI extension.
Packit Service 82fcde
   Each field says whether the ABI can be executed in that mode.  The FR0 field
Packit Service 82fcde
   is actually overloaded and means 'default' FR mode for the ABI.  I.e. For
Packit Service 82fcde
   O32 it is FR0 and for N32/N64 it is actually FR1.  Since this logic is
Packit Service 82fcde
   focussed on the intricacies of mode management for O32 we call the field
Packit Service 82fcde
   FR0.  */
Packit Service 82fcde
Packit Service 82fcde
struct abi_req
Packit Service 82fcde
{
Packit Service 82fcde
  bool single;
Packit Service 82fcde
  bool soft;
Packit Service 82fcde
  bool fr0;
Packit Service 82fcde
  bool fr1;
Packit Service 82fcde
  bool fre;
Packit Service 82fcde
};
Packit Service 82fcde
Packit Service 82fcde
/* FP ABI requirements for all Val_GNU_MIPS_ABI_FP_* values.  */
Packit Service 82fcde
Packit Service 82fcde
static const struct abi_req reqs[Val_GNU_MIPS_ABI_FP_MAX + 1] =
Packit Service 82fcde
    {{true,  true,  true,  true,  true},  /* Any */
Packit Service 82fcde
     {false, false, true,  false, true},  /* Double-float */
Packit Service 82fcde
     {true,  false, false, false, false}, /* Single-float */
Packit Service 82fcde
     {false, true,  false, false, false}, /* Soft-float */
Packit Service 82fcde
     {false, false, false, false, false}, /* old-FP64 */
Packit Service 82fcde
     {false, false, true,  true,  true},  /* FPXX */
Packit Service 82fcde
     {false, false, false, true,  false}, /* FP64 */
Packit Service 82fcde
     {false, false, false, true,  true}}; /* FP64A */
Packit Service 82fcde
Packit Service 82fcde
/* FP ABI requirements for objects without a PT_MIPS_ABIFLAGS segment.  */
Packit Service 82fcde
Packit Service 82fcde
static const struct abi_req none_req = { true, true, true, false, true };
Packit Service 82fcde
Packit Service 82fcde
/* Return true iff ELF program headers are incompatible with the running
Packit Service 82fcde
   host.  This verifies that floating-point ABIs are compatible and
Packit Service 82fcde
   re-configures the hardware mode if necessary.  This code handles both the
Packit Service 82fcde
   DT_NEEDED libraries and the dlopen'ed libraries.  It also accounts for the
Packit Service 82fcde
   impact of dlclose.  */
Packit Service 82fcde
Packit Service 82fcde
static bool __attribute_used__
Packit Service 82fcde
elf_machine_reject_phdr_p (const ElfW(Phdr) *phdr, uint_fast16_t phnum,
Packit Service 82fcde
			   const char *buf, size_t len, struct link_map *map,
Packit Service 82fcde
			   int fd)
Packit Service 82fcde
{
Packit Service 82fcde
  const ElfW(Phdr) *ph = find_mips_abiflags (phdr, phnum);
Packit Service 82fcde
  struct link_map *l;
Packit Service 82fcde
  Lmid_t nsid;
Packit Service 82fcde
  int in_abi = -1;
Packit Service 82fcde
  struct abi_req in_req;
Packit Service 82fcde
  Elf_MIPS_ABIFlags_v0 *mips_abiflags = NULL;
Packit Service 82fcde
  bool perfect_match = false;
Packit Service 82fcde
#if _MIPS_SIM == _ABIO32
Packit Service 82fcde
  unsigned int cur_mode = -1;
Packit Service 82fcde
# if HAVE_PRCTL_FP_MODE
Packit Service 82fcde
  bool cannot_mode_switch = false;
Packit Service 82fcde
Packit Service 82fcde
  /* Get the current hardware mode.  */
Packit Service 82fcde
  cur_mode = __prctl (PR_GET_FP_MODE);
Packit Service 82fcde
# endif
Packit Service 82fcde
#endif
Packit Service 82fcde
Packit Service 82fcde
  /* Read the attributes section.  */
Packit Service 82fcde
  if (ph != NULL)
Packit Service 82fcde
    {
Packit Service 82fcde
      ElfW(Addr) size = ph->p_filesz;
Packit Service 82fcde
Packit Service 82fcde
      if (ph->p_offset + size <= len)
Packit Service 82fcde
	mips_abiflags = (Elf_MIPS_ABIFlags_v0 *) (buf + ph->p_offset);
Packit Service 82fcde
      else
Packit Service 82fcde
	{
Packit Service 82fcde
	  mips_abiflags = alloca (size);
Packit Service 82fcde
	  __lseek (fd, ph->p_offset, SEEK_SET);
Packit Service 82fcde
	  if (__libc_read (fd, (void *) mips_abiflags, size) != size)
Packit Service 82fcde
	    REJECT ("   unable to read PT_MIPS_ABIFLAGS\n");
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      if (size < sizeof (Elf_MIPS_ABIFlags_v0))
Packit Service 82fcde
	REJECT ("   contains malformed PT_MIPS_ABIFLAGS\n");
Packit Service 82fcde
Packit Service 82fcde
      if (__glibc_unlikely (mips_abiflags->flags2 != 0))
Packit Service 82fcde
	REJECT ("   unknown MIPS.abiflags flags2: %u\n", mips_abiflags->flags2);
Packit Service 82fcde
Packit Service 82fcde
      in_abi = mips_abiflags->fp_abi;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  /* ANY is compatible with anything.  */
Packit Service 82fcde
  perfect_match |= (in_abi == Val_GNU_MIPS_ABI_FP_ANY);
Packit Service 82fcde
Packit Service 82fcde
  /* Unknown ABIs are rejected.  */
Packit Service 82fcde
  if (in_abi != -1 && in_abi > Val_GNU_MIPS_ABI_FP_MAX)
Packit Service 82fcde
    REJECT ("   uses unknown FP ABI: %u\n", in_abi);
Packit Service 82fcde
Packit Service 82fcde
  /* Obtain the initial requirements.  */
Packit Service 82fcde
  in_req = (in_abi == -1) ? none_req : reqs[in_abi];
Packit Service 82fcde
Packit Service 82fcde
  /* Check that the new requirement does not conflict with any currently
Packit Service 82fcde
     loaded object.  */
Packit Service 82fcde
  for (nsid = 0; nsid < DL_NNS; ++nsid)
Packit Service 82fcde
    for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next)
Packit Service 82fcde
      {
Packit Service 82fcde
	struct abi_req existing_req;
Packit Service 82fcde
Packit Service 82fcde
	if (cached_fpabi_reject_phdr_p (l))
Packit Service 82fcde
	  return true;
Packit Service 82fcde
Packit Service 82fcde
#if _MIPS_SIM == _ABIO32
Packit Service 82fcde
	/* A special case arises for O32 FP64 and FP64A where the kernel
Packit Service 82fcde
	   pre-dates PT_MIPS_ABIFLAGS.  These ABIs will be blindly loaded even
Packit Service 82fcde
	   if the hardware mode is unavailable or disabled.  In this
Packit Service 82fcde
	   circumstance the prctl call to obtain the current mode will fail.
Packit Service 82fcde
	   Detect this situation here and reject everything.  This will
Packit Service 82fcde
	   effectively prevent dynamically linked applications from failing in
Packit Service 82fcde
	   unusual ways but there is nothing we can do to help static
Packit Service 82fcde
	   applications.  */
Packit Service 82fcde
	if ((l->l_mach.fpabi == Val_GNU_MIPS_ABI_FP_64A
Packit Service 82fcde
	     || l->l_mach.fpabi == Val_GNU_MIPS_ABI_FP_64)
Packit Service 82fcde
	    && cur_mode == -1)
Packit Service 82fcde
	  REJECT ("   found %s running in the wrong mode\n",
Packit Service 82fcde
		  fpabi_string (l->l_mach.fpabi));
Packit Service 82fcde
#endif
Packit Service 82fcde
Packit Service 82fcde
	/* Found a perfect match, success.  */
Packit Service 82fcde
	perfect_match |= (in_abi == l->l_mach.fpabi);
Packit Service 82fcde
Packit Service 82fcde
	/* Unknown ABIs are rejected.  */
Packit Service 82fcde
	if (l->l_mach.fpabi != -1 && l->l_mach.fpabi > Val_GNU_MIPS_ABI_FP_MAX)
Packit Service 82fcde
	  REJECT ("   found unknown FP ABI: %u\n", l->l_mach.fpabi);
Packit Service 82fcde
Packit Service 82fcde
	existing_req = (l->l_mach.fpabi == -1 ? none_req
Packit Service 82fcde
			: reqs[l->l_mach.fpabi]);
Packit Service 82fcde
Packit Service 82fcde
	/* Merge requirements.  */
Packit Service 82fcde
	in_req.soft &= existing_req.soft;
Packit Service 82fcde
	in_req.single &= existing_req.single;
Packit Service 82fcde
	in_req.fr0 &= existing_req.fr0;
Packit Service 82fcde
	in_req.fr1 &= existing_req.fr1;
Packit Service 82fcde
	in_req.fre &= existing_req.fre;
Packit Service 82fcde
Packit Service 82fcde
	/* If there is at least one mode which is still usable then the new
Packit Service 82fcde
	   object can be loaded.  */
Packit Service 82fcde
	if (in_req.single || in_req.soft || in_req.fr1 || in_req.fr0
Packit Service 82fcde
	    || in_req.fre)
Packit Service 82fcde
	  {
Packit Service 82fcde
#if _MIPS_SIM == _ABIO32 && HAVE_PRCTL_FP_MODE
Packit Service 82fcde
	    /* Account for loaded ABIs which prohibit mode switching.  */
Packit Service 82fcde
	    if (l->l_mach.fpabi == Val_GNU_MIPS_ABI_FP_XX)
Packit Service 82fcde
	      cannot_mode_switch |= l->l_mach.odd_spreg;
Packit Service 82fcde
#endif
Packit Service 82fcde
	  }
Packit Service 82fcde
	else
Packit Service 82fcde
	  REJECT ("   uses %s, already loaded %s\n",
Packit Service 82fcde
		  fpabi_string (in_abi),
Packit Service 82fcde
		  fpabi_string (l->l_mach.fpabi));
Packit Service 82fcde
      }
Packit Service 82fcde
Packit Service 82fcde
#if _MIPS_SIM == _ABIO32
Packit Service 82fcde
  /* At this point we know that the newly loaded object is compatible with all
Packit Service 82fcde
     existing objects but the hardware mode may not be correct.  */
Packit Service 82fcde
  if ((in_req.fr1 || in_req.fre || in_req.fr0)
Packit Service 82fcde
      && !perfect_match)
Packit Service 82fcde
    {
Packit Service 82fcde
      if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
Packit Service 82fcde
	_dl_debug_printf ("   needs %s%s mode\n", in_req.fr0 ? "FR0 or " : "",
Packit Service 82fcde
			  (in_req.fre && !in_req.fr1) ? "FRE" : "FR1");
Packit Service 82fcde
Packit Service 82fcde
      /* If the PR_GET_FP_MODE is not supported then only FR0 is available.
Packit Service 82fcde
	 If the overall requirements cannot be met by FR0 then reject the
Packit Service 82fcde
	 object.  */
Packit Service 82fcde
      if (cur_mode == -1)
Packit Service 82fcde
	return !in_req.fr0;
Packit Service 82fcde
Packit Service 82fcde
# if HAVE_PRCTL_FP_MODE
Packit Service 82fcde
      {
Packit Service 82fcde
	unsigned int fr1_mode = PR_FP_MODE_FR;
Packit Service 82fcde
Packit Service 82fcde
	/* It is not possible to change the mode of a thread which may be
Packit Service 82fcde
	   executing FPXX code with odd-singles.  If an FPXX object with
Packit Service 82fcde
	   odd-singles is loaded then just check the current mode is OK. This
Packit Service 82fcde
	   can be either the FR1 mode or FR0 if the requirements are met by
Packit Service 82fcde
	   FR0.  */
Packit Service 82fcde
	if (cannot_mode_switch)
Packit Service 82fcde
	  return (!(in_req.fre && cur_mode == (PR_FP_MODE_FR | PR_FP_MODE_FRE))
Packit Service 82fcde
		  && !(in_req.fr1 && cur_mode == PR_FP_MODE_FR)
Packit Service 82fcde
		  && !(in_req.fr0 && cur_mode == 0));
Packit Service 82fcde
Packit Service 82fcde
	/* If the overall requirements can be satisfied by FRE but not FR1 then
Packit Service 82fcde
	   fr1_mode must become FRE.  */
Packit Service 82fcde
	if (in_req.fre && !in_req.fr1)
Packit Service 82fcde
	  fr1_mode |= PR_FP_MODE_FRE;
Packit Service 82fcde
Packit Service 82fcde
	/* Set the new mode.  Use fr1_mode if the requirements cannot be met by
Packit Service 82fcde
	   FR0.  */
Packit Service 82fcde
	if (!in_req.fr0)
Packit Service 82fcde
	  return __prctl (PR_SET_FP_MODE, fr1_mode) != 0;
Packit Service 82fcde
	else if (__prctl (PR_SET_FP_MODE, /* fr0_mode */ 0) != 0)
Packit Service 82fcde
	  {
Packit Service 82fcde
	    /* Setting FR0 can validly fail on an R6 core so retry with the FR1
Packit Service 82fcde
	       mode as a fall back.  */
Packit Service 82fcde
	    if (errno != ENOTSUP)
Packit Service 82fcde
	      return true;
Packit Service 82fcde
Packit Service 82fcde
	    return __prctl (PR_SET_FP_MODE, fr1_mode) != 0;
Packit Service 82fcde
	  }
Packit Service 82fcde
      }
Packit Service 82fcde
# endif /* HAVE_PRCTL_FP_MODE */
Packit Service 82fcde
    }
Packit Service 82fcde
#endif /* _MIPS_SIM == _ABIO32 */
Packit Service 82fcde
Packit Service 82fcde
  return false;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
#endif /* dl-machine-reject-phdr.h */