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