mirror of
https://git.savannah.gnu.org/git/emacs.git
synced 2024-12-03 08:30:09 +00:00
290 lines
8.2 KiB
C
290 lines
8.2 KiB
C
/* Program execution for Emacs.
|
||
|
||
Copyright (C) 2023-2024 Free Software Foundation, Inc.
|
||
|
||
This file is part of GNU Emacs.
|
||
|
||
GNU Emacs is free software: you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation, either version 3 of the License, or (at
|
||
your option) any later version.
|
||
|
||
GNU Emacs is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||
|
||
#include <config.h>
|
||
#include <errno.h>
|
||
|
||
#include "mipsfpu.h"
|
||
|
||
|
||
|
||
/* OABI MIPS systems support several different modes of execution.
|
||
Each mode differs in the size and utilization of the hardware
|
||
floating-point registers.
|
||
|
||
Linux normally sets the floating point mode to one appropriate for
|
||
execution, taking into account the floating point modes of the
|
||
interpreter and executable binaries. However, this logic is
|
||
forsaken when the `execve' system call is overwritten.
|
||
|
||
Thus, the correct floating point mode must be determined and set
|
||
within the loader binary. */
|
||
|
||
|
||
|
||
/* Various constants used throughout this code. */
|
||
|
||
#define MIPS_ABI_FP_ANY 0 /* FP ABI doesn't matter */
|
||
#define MIPS_ABI_FP_DOUBLE 1 /* -mdouble-float */
|
||
#define MIPS_ABI_FP_SINGLE 2 /* -msingle-float */
|
||
#define MIPS_ABI_FP_SOFT 3 /* -msoft-float */
|
||
#define MIPS_ABI_FP_OLD_64 4 /* -mips32r2 -mfp64 */
|
||
#define MIPS_ABI_FP_XX 5 /* -mfpxx */
|
||
#define MIPS_ABI_FP_64 6 /* -mips32r2 -mfp64 */
|
||
#define MIPS_ABI_FP_64A 7 /* -mips32r2 -mfp64 -mno-odd-spreg */
|
||
|
||
#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used. */
|
||
#define EF_MIPS_PIC 2 /* Contains PIC code. */
|
||
#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence. */
|
||
#define EF_MIPS_XGOT 8
|
||
#define EF_MIPS_64BIT_WHIRL 16
|
||
#define EF_MIPS_ABI2 32
|
||
#define EF_MIPS_ABI_ON32 64
|
||
#define EF_MIPS_FP64 512 /* Uses FP64 (12 callee-saved). */
|
||
#define EF_MIPS_NAN2008 1024 /* Uses IEEE 754-2008 NaN encoding. */
|
||
#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level. */
|
||
|
||
|
||
|
||
/* Structure describing the requirements of a single floating-point
|
||
ABI. */
|
||
|
||
struct mode_description
|
||
{
|
||
/* Whether or not the ABI only executes single precision
|
||
instructions, and can operate in both 32-bit or 64-bit floating
|
||
point mode. */
|
||
bool single;
|
||
|
||
/* Whether or not the ABI performs floating point operations in
|
||
software, using integer registers. */
|
||
bool soft;
|
||
|
||
/* Whether or not the ABI requires the use of 64-bit floating point
|
||
registers. */
|
||
bool fr1;
|
||
|
||
/* Whether or not the ABI requires the use of 64-bit floating point
|
||
registers on NABI systems, and 32-bit ones on OABI systems. */
|
||
bool frdefault;
|
||
|
||
/* Whether or not this ABI requires single precision floating point
|
||
emulation. */
|
||
bool fre;
|
||
};
|
||
|
||
static struct mode_description fpu_reqs[] =
|
||
{
|
||
[MIPS_ABI_FP_ANY] = { true, true, true, true, true, },
|
||
[MIPS_ABI_FP_DOUBLE] = { false, false, false, true, true, },
|
||
[MIPS_ABI_FP_SINGLE] = { true, false, false, false, false, },
|
||
[MIPS_ABI_FP_SOFT] = { false, true, false, false, false, },
|
||
[MIPS_ABI_FP_OLD_64] = { false, false, false, false, false, },
|
||
[MIPS_ABI_FP_XX] = { false, false, true, true, true, },
|
||
[MIPS_ABI_FP_64] = { false, false, true, false, false, },
|
||
[MIPS_ABI_FP_64A] = { false, false, true, false, true, },
|
||
};
|
||
|
||
|
||
|
||
/* Return whether or not the given floating-point ABI is valid. */
|
||
|
||
static bool
|
||
valid_abi_p (int abi)
|
||
{
|
||
switch (abi)
|
||
{
|
||
case MIPS_ABI_FP_ANY:
|
||
case MIPS_ABI_FP_DOUBLE:
|
||
case MIPS_ABI_FP_SINGLE:
|
||
case MIPS_ABI_FP_SOFT:
|
||
case MIPS_ABI_FP_OLD_64:
|
||
case MIPS_ABI_FP_XX:
|
||
case MIPS_ABI_FP_64:
|
||
case MIPS_ABI_FP_64A:
|
||
return true;
|
||
|
||
default:
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/* Return the floating point mode appropriate for the specified
|
||
floating point ABI. */
|
||
|
||
static int
|
||
fp_mode_for_abi (int abi)
|
||
{
|
||
struct mode_description *desc;
|
||
|
||
desc = &fpu_reqs[abi];
|
||
|
||
if (desc->fre)
|
||
return FP_FRE;
|
||
else if (desc->fr1)
|
||
return FP_FR1;
|
||
|
||
return FP_FR0;
|
||
}
|
||
|
||
/* Determine whether or not the CPU is capable of operating in FR0
|
||
floating point mode. */
|
||
|
||
bool
|
||
cpu_supports_fr0_p (void)
|
||
{
|
||
#if defined __mips_isa_rev && __mips_isa_rev >= 6
|
||
return true;
|
||
#else /* !defined __mips_isa_rev | mips_isa_rev < 6 */
|
||
return false;
|
||
#endif /* defined __mips_isa_rev && mips_isa_rev >= 6 */
|
||
}
|
||
|
||
/* Determine the FPU mode for the executable whose ELF header is
|
||
HEADER. If INTERPRETER is non-NULL, also take an interpreter whose
|
||
header is INTERPRETER into account.
|
||
|
||
ABIFLAGS should be HEADER's corresponding PT_MIPS_ABIFLAGS program
|
||
header, and ABIFLAGS1 should be that of INTERPRETER, if set. Both
|
||
fields may be NULL if no PT_MIPS_ABIFLAGS header is present; in
|
||
that case, use HEADER->e_flags to determine the ABI instead.
|
||
|
||
Return the FPU mode in *MODE. Value is 0 upon success, 1
|
||
otherwise, with errno set. */
|
||
|
||
int
|
||
determine_fpu_mode (elf_header *header, elf_header *interpreter,
|
||
int *mode, struct mips_elf_abi_flags *abiflags,
|
||
struct mips_elf_abi_flags *abiflags1)
|
||
{
|
||
int exec_abi, interpreter_abi;
|
||
struct mode_description *exec_desc, *interpreter_desc, common;
|
||
|
||
/* Figure out the executable's floating point ABI. First, consult
|
||
header->e_flags, and use the old 64-bit floating point ABI if it
|
||
is specified. */
|
||
|
||
exec_abi = MIPS_ABI_FP_ANY;
|
||
|
||
/* First, check HEADER->e_flags. */
|
||
|
||
if (header->e_flags & EF_MIPS_FP64)
|
||
exec_abi = MIPS_ABI_FP_OLD_64;
|
||
|
||
/* Next, use ABIFLAGS if it exists. */
|
||
|
||
if (abiflags && valid_abi_p (abiflags->fp_abi))
|
||
exec_abi = abiflags->fp_abi;
|
||
else if (abiflags)
|
||
{
|
||
errno = ENOEXEC;
|
||
return 1;
|
||
}
|
||
|
||
/* Now determine that of the interpreter. */
|
||
|
||
interpreter_abi = MIPS_ABI_FP_ANY;
|
||
|
||
if (interpreter)
|
||
{
|
||
if (interpreter->e_flags & EF_MIPS_FP64)
|
||
interpreter_abi = MIPS_ABI_FP_OLD_64;
|
||
|
||
if (abiflags1 && valid_abi_p (abiflags->fp_abi))
|
||
interpreter_abi = abiflags->fp_abi;
|
||
else if (abiflags1)
|
||
{
|
||
errno = ELIBBAD;
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
/* If no interpreter flag is set, just return that of the
|
||
executable. */
|
||
|
||
if (!interpreter)
|
||
{
|
||
*mode = fp_mode_for_abi (exec_abi);
|
||
return 0;
|
||
}
|
||
|
||
/* Otherwise, compare both ABIs and try to find one which will run
|
||
both kinds of code.
|
||
|
||
First, see if there's an easy way out: both ABIs are identical,
|
||
or one ABI is MIPS_ABI_FP_ANY. */
|
||
|
||
if (exec_abi == interpreter_abi)
|
||
{
|
||
*mode = fp_mode_for_abi (exec_abi);
|
||
return 0;
|
||
}
|
||
else if (exec_abi == MIPS_ABI_FP_ANY)
|
||
{
|
||
*mode = fp_mode_for_abi (interpreter_abi);
|
||
return 0;
|
||
}
|
||
else if (interpreter_abi == MIPS_ABI_FP_ANY)
|
||
{
|
||
*mode = fp_mode_for_abi (exec_abi);
|
||
return 0;
|
||
}
|
||
|
||
/* If that doesn't work, compare various characteristics of both
|
||
ABIs and select an appropriate floating point mode. */
|
||
|
||
exec_desc = &fpu_reqs[exec_abi];
|
||
interpreter_desc = &fpu_reqs[interpreter_abi];
|
||
|
||
/* Merge both sets of requirements. */
|
||
common.single = exec_desc->single && interpreter_desc->single;
|
||
common.soft = exec_desc->soft && interpreter_desc->soft;
|
||
common.fr1 = exec_desc->fr1 && interpreter_desc->fr1;
|
||
common.frdefault = exec_desc->frdefault && interpreter_desc->frdefault;
|
||
common.fre = exec_desc->fre && interpreter_desc->fre;
|
||
|
||
/* Default to a mode capable of running code expecting 32-bit
|
||
registers. */
|
||
|
||
if (!(header->e_flags & EF_MIPS_ABI2))
|
||
*mode = FP_FR0;
|
||
else
|
||
/* But in this case, use FR1. */
|
||
*mode = FP_FR1;
|
||
|
||
if (common.fre && !common.frdefault && !common.fr1)
|
||
/* Floating point emulation mode is required. */
|
||
*mode = FP_FRE;
|
||
else if ((common.fr1 && common.frdefault)
|
||
|| (common.single && !common.frdefault)
|
||
|| common.fr1)
|
||
/* 64-bit mode is required. */
|
||
*mode = FP_FR1;
|
||
else if (!common.fre && !common.frdefault
|
||
&& !common.fr1 && !common.single
|
||
&& !common.soft)
|
||
{
|
||
/* The floating point modes specified are incompatible. */
|
||
errno = ELIBBAD;
|
||
return -1;
|
||
}
|
||
|
||
return 0;
|
||
}
|