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

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