From 0bd956720c457ff054325b48f26ac7c91cb060e8 Mon Sep 17 00:00:00 2001 From: Matthew Fortune Date: Sun, 1 Jun 2014 00:16:25 +0100 Subject: Add support for MIPS O32 FPXX and .MIPS.abiflags * elf/elf.h (PT_MIPS_ABIFLAGS): Define. (Elf_MIPS_ABIFlags_v0): New structure. (EF_MIPS_FP64): Define. (MIPS_AFL_REG_NONE, MIPS_AFL_REG_32, MIPS_AFL_REG_64): Likewise. (MIPS_AFL_REG_128, MIPS_AFL_ASE_DSP, MIPS_AFL_ASE_DSP64): Likewise. (MIPS_AFL_ASE_DSPR2, MIPS_AFL_ASE_EVA, MIPS_AFL_ASE_MCU): Likewise. (MIPS_AFL_ASE_MDMX, MIPS_AFL_ASE_MIPS3D, MIPS_AFL_ASE_MT): Likewise. (MIPS_AFL_ASE_SMARTMIPS, MIPS_AFL_ASE_VIRT): Likewise. (MIPS_AFL_ASE_VIRT64, MIPS_AFL_ASE_MSA, MIPS_AFL_ASE_MSA64): Likewise. (MIPS_AFL_ASE_MIPS16, MIPS_AFL_ASE_MICROMIPS): Likewise. (MIPS_AFL_ASE_XPA, MIPS_AFL_EXT_XLR, MIPS_AFL_EXT_OCTEON2): Likewise. (MIPS_AFL_EXT_OCTEONP, MIPS_AFL_EXT_LOONGSON_3A): Likewise. (MIPS_AFL_EXT_OCTEON, MIPS_AFL_EXT_5900, MIPS_AFL_EXT_4010): Likewise. (MIPS_AFL_EXT_4100, MIPS_AFL_EXT_3900, MIPS_AFL_EXT_10000): Likewise. (MIPS_AFL_EXT_SB1, MIPS_AFL_EXT_4111, MIPS_AFL_EXT_4120): Likewise. (MIPS_AFL_EXT_5400, MIPS_AFL_EXT_5500): Likewise. (MIPS_AFL_EXT_LOONGSON_2E, MIPS_AFL_EXT_LOONGSON_2F): Likewise. (Val_GNU_MIPS_ABI_FP_ANY, Val_GNU_MIPS_ABI_FP_DOUBLE): New enum values. (Val_GNU_MIPS_ABI_FP_SINGLE, Val_GNU_MIPS_ABI_FP_SOFT): Likewise. (Val_GNU_MIPS_ABI_FP_OLD_64, Val_GNU_MIPS_ABI_FP_XX): Likewise. (Val_GNU_MIPS_ABI_FP_64, Val_GNU_MIPS_ABI_FP_64A): Likewise. (Val_GNU_MIPS_ABI_FP_MAX): Likewise. * sysdeps/mips/Makefile [subdir=elf]: Add tst-abi-interlink, tst-mode-switch-1, tst-mode-switch-2, tst-mode-switch-3 tests. * sysdeps/mips/bits/linkmap.h (struct link_map_machine): Add fpmode field. * sysdeps/mips/dl-machine.h (elf_machine_matches_host): Reject EF_MIPS_FP64. * sysdeps/mips/dl-machine-reject-phdr.h: New file. * sysdeps/mips/tst-abi-fp32mod.c: Likewise. * sysdeps/mips/tst-abi-fpxxmod.c: Likewise. * sysdeps/mips/tst-abi-fpxxomod.c: Likewise. * sysdeps/mips/tst-abi-fp64mod.c: Likewise. * sysdeps/mips/tst-abi-fp64amod.c: Likewise. * sysdeps/mips/tst-abi-interlink.c: Likewise. * sysdeps/mips/tst-mode-switch-1.c: Likewise. * sysdeps/mips/tst-mode-switch-2.c: Likewise. * sysdeps/mips/tst-mode-switch-3.c: Likewise. * sysdeps/unix/sysv/linux/mips/configure.ac (o32-fpabi): Define to record the current FP ABI extension. (mips-mode-switch): Define to show if kernel headers support mode switching. * sysdeps/unix/sysv/linux/mips/configure: Regenerate. * sysdeps/unix/sysv/linux/mips/ldsodefs.h: Increase maximum supported SYSV ABI version to 3. * sysdeps/unix/sysv/linux/mips/libc-abis: Add new MIPS_O32_FP64 feature. --- sysdeps/mips/tst-abi-interlink.c | 844 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 844 insertions(+) create mode 100644 sysdeps/mips/tst-abi-interlink.c (limited to 'sysdeps/mips/tst-abi-interlink.c') diff --git a/sysdeps/mips/tst-abi-interlink.c b/sysdeps/mips/tst-abi-interlink.c new file mode 100644 index 0000000000..c9a29cce92 --- /dev/null +++ b/sysdeps/mips/tst-abi-interlink.c @@ -0,0 +1,844 @@ +/* Copyright (C) 2014 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include +#include +#include + +#if defined PR_GET_FP_MODE && defined PR_SET_FP_MODE +# define HAVE_PRCTL_FP_MODE 1 +# define FR1_MODE (PR_FP_MODE_FR) +# define FRE_MODE (PR_FP_MODE_FR | PR_FP_MODE_FRE) +#else +# define HAVE_PRCTL_FP_MODE 0 +# define FR1_MODE 0x1 +# define FRE_MODE 0x2 +#endif + +#define STR_VAL(VAL) #VAL +#define N_STR(VAL) STR_VAL(VAL) + +#define START_STATE(NAME) \ +case s_ ## NAME: \ + { \ + switch (obj) \ + { + +#define END_STATE \ + default: \ + return false; \ + } \ + break; \ + } + +#define NEXT(OBJ, NEXT_STATE) \ +case o_ ## OBJ: \ + current_fp_state = s_ ## NEXT_STATE; \ + break; + +#define NEXT_REQ_FR1(OBJ, NEXT_STATE) \ +case o_ ## OBJ: \ + { \ + if (has_fr1) \ + current_fp_state = s_ ## NEXT_STATE; \ + else \ + return false; \ + } \ + break; + +#define NEXT_REQ_FR0(OBJ, NEXT_STATE) \ +case o_ ## OBJ: \ + { \ + if (!is_r6 \ + || (is_r6 && has_fr1 && has_fre)) \ + current_fp_state = s_ ## NEXT_STATE; \ + else \ + return false; \ + } \ + break; + +#define NEXT_REQ_FRE(OBJ, NEXT_STATE) \ +case o_ ## OBJ: \ + { \ + if (has_fr1 && has_fre) \ + current_fp_state = s_ ## NEXT_STATE; \ + else \ + return false; \ + } \ + break; + +#define NEXT_NO_MODE_CHANGE(OBJ, NEXT_STATE) \ +case o_ ## OBJ: \ + { \ + if (current_mode_valid_p (s_ ## NEXT_STATE)) \ + { \ + current_fp_state = s_ ## NEXT_STATE; \ + cant_change_mode = true; \ + } \ + else \ + return false; \ + } \ + break; + +static const char * const shared_lib_names[] = + { + "tst-abi-fpanymod.so", "tst-abi-fpsoftmod.so", "tst-abi-fpsinglemod.so", + "tst-abi-fp32mod.so", "tst-abi-fp64mod.so", "tst-abi-fp64amod.so", + "tst-abi-fpxxmod.so", "tst-abi-fpxxomod.so" + }; + +struct fp_mode_req +{ + int mode1; + int mode2; + int mode3; +}; + +enum fp_obj +{ + o_any, + o_soft, + o_single, + o_fp32, + o_fp64, + o_fp64a, + o_fpxx, + o_fpxxo, + o_max +}; + +enum fp_state +{ + s_any, + s_soft, + s_single, + s_fp32, + s_fpxx, + s_fpxxo, + s_fp64a, + s_fp64, + s_fpxxo_fpxx, + s_fp32_fpxx, + s_fp32_fpxxo, + s_fp32_fpxxo_fpxx, + s_fp32_fp64a_fpxx, + s_fp32_fp64a_fpxxo, + s_fp32_fp64a_fpxxo_fpxx, + s_fp64a_fp32, + s_fp64a_fpxx, + s_fp64a_fpxxo, + s_fp64a_fp64, + s_fp64a_fp64_fpxx, + s_fp64a_fp64_fpxxo, + s_fp64a_fpxx_fpxxo, + s_fp64a_fp64_fpxxo_fpxx, + s_fp64_fpxx, + s_fp64_fpxxo, + s_fp64_fpxx_fpxxo +}; + + +static int current_fp_mode; +static bool cant_change_mode = false; +static bool has_fr1 = false; +static bool has_fre = false; +static bool is_r6 = false; +static unsigned int fp_obj_count[o_max]; +void * shared_lib_ptrs[o_max]; +static enum fp_state current_fp_state = s_any; +static enum fp_obj test_objects[FPABI_COUNT] = { FPABI_LIST }; + +/* This function will return the valid FP modes for the specified state. */ + +static struct fp_mode_req +compute_fp_modes (enum fp_state state) +{ + struct fp_mode_req requirements; + + requirements.mode1 = -1; + requirements.mode2 = -1; + requirements.mode3 = -1; + + switch (state) + { + case s_single: + { + if (is_r6) + requirements.mode1 = FR1_MODE; + else + { + requirements.mode1 = 0; + requirements.mode2 = FR1_MODE; + } + break; + } + case s_fp32: + case s_fp32_fpxx: + case s_fp32_fpxxo: + case s_fp32_fpxxo_fpxx: + { + if (is_r6) + requirements.mode1 = FRE_MODE; + else + { + requirements.mode1 = 0; + requirements.mode2 = FRE_MODE; + } + break; + } + case s_fpxx: + case s_fpxxo: + case s_fpxxo_fpxx: + case s_any: + case s_soft: + { + if (is_r6) + { + requirements.mode1 = FR1_MODE; + requirements.mode2 = FRE_MODE; + } + else + { + requirements.mode1 = 0; + requirements.mode2 = FR1_MODE; + requirements.mode3 = FRE_MODE; + } + break; + } + case s_fp64a: + case s_fp64a_fpxx: + case s_fp64a_fpxxo: + case s_fp64a_fpxx_fpxxo: + { + requirements.mode1 = FR1_MODE; + requirements.mode2 = FRE_MODE; + break; + } + case s_fp64: + case s_fp64_fpxx: + case s_fp64_fpxxo: + case s_fp64_fpxx_fpxxo: + case s_fp64a_fp64: + case s_fp64a_fp64_fpxx: + case s_fp64a_fp64_fpxxo: + case s_fp64a_fp64_fpxxo_fpxx: + { + requirements.mode1 = FR1_MODE; + break; + } + case s_fp64a_fp32: + case s_fp32_fp64a_fpxx: + case s_fp32_fp64a_fpxxo: + case s_fp32_fp64a_fpxxo_fpxx: + { + requirements.mode1 = FRE_MODE; + break; + } + } + return requirements; +} + +/* Check the current mode is suitable for the specified state. */ + +static bool +current_mode_valid_p (enum fp_state s) +{ + struct fp_mode_req req = compute_fp_modes (s); + return (req.mode1 == current_fp_mode + || req.mode2 == current_fp_mode + || req.mode3 == current_fp_mode); +} + +/* Run the state machine by adding a new object. */ + +static bool +set_next_fp_state (enum fp_obj obj) +{ + cant_change_mode = false; + switch (current_fp_state) + { + + START_STATE(soft) + NEXT(soft,soft) + NEXT(any,soft) + END_STATE + + START_STATE(single) + NEXT(single,single) + NEXT(any,single) + END_STATE + + START_STATE(any) + NEXT_REQ_FR0(fp32, fp32) + NEXT(fpxx, fpxx) + NEXT(fpxxo, fpxxo) + NEXT_REQ_FR1(fp64a, fp64a) + NEXT_REQ_FR1(fp64, fp64) + NEXT(any,any) + NEXT(soft,soft) + NEXT(single,single) + END_STATE + + START_STATE(fp32) + NEXT_REQ_FR0(fp32,fp32) + NEXT(fpxx, fp32_fpxx) + NEXT(fpxxo, fp32_fpxxo) + NEXT_REQ_FRE(fp64a, fp64a_fp32) + NEXT(any,fp32) + END_STATE + + START_STATE(fpxx) + NEXT_REQ_FR0(fp32, fp32_fpxx) + NEXT_REQ_FR1(fp64, fp64_fpxx) + NEXT_REQ_FR1(fp64a, fp64a_fpxx) + NEXT(fpxxo, fpxxo_fpxx) + NEXT(fpxx,fpxx) + NEXT(any,fpxx) + END_STATE + + START_STATE(fpxxo) + NEXT_NO_MODE_CHANGE(fp32, fp32_fpxxo) + NEXT_NO_MODE_CHANGE(fp64, fp64_fpxxo) + NEXT_NO_MODE_CHANGE(fp64a, fp64a_fpxxo) + NEXT_NO_MODE_CHANGE(fpxx, fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fpxxo,fpxxo) + NEXT_NO_MODE_CHANGE(any,fpxxo) + END_STATE + + START_STATE(fp64a) + NEXT_REQ_FRE(fp32, fp64a_fp32) + NEXT_REQ_FR1(fp64, fp64a_fp64) + NEXT(fpxxo, fp64a_fpxxo) + NEXT(fpxx, fp64a_fpxx) + NEXT_REQ_FR1(fp64a, fp64a) + NEXT(any, fp64a) + END_STATE + + START_STATE(fp64) + NEXT_REQ_FR1(fp64a, fp64a_fp64) + NEXT(fpxxo, fp64_fpxxo) + NEXT(fpxx, fp64_fpxx) + NEXT_REQ_FR1(fp64, fp64) + NEXT(any, fp64) + END_STATE + + START_STATE(fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fp32, fp32_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fp64, fp64_fpxx_fpxxo) + NEXT_NO_MODE_CHANGE(fp64a, fp64a_fpxx_fpxxo) + NEXT_NO_MODE_CHANGE(fpxx, fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fpxxo, fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(any, fpxxo_fpxx) + END_STATE + + START_STATE(fp32_fpxx) + NEXT_REQ_FR0(fp32, fp32_fpxx) + NEXT(fpxx, fp32_fpxx) + NEXT(fpxxo, fp32_fpxxo_fpxx) + NEXT_REQ_FRE(fp64a, fp32_fp64a_fpxx) + NEXT(any, fp32_fpxx) + END_STATE + + START_STATE(fp32_fpxxo) + NEXT_NO_MODE_CHANGE(fp32, fp32_fpxxo) + NEXT_NO_MODE_CHANGE(fpxxo, fp32_fpxxo) + NEXT_NO_MODE_CHANGE(fpxx, fp32_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fp64a, fp32_fp64a_fpxxo) + NEXT_NO_MODE_CHANGE(any, fp32_fpxxo) + END_STATE + + START_STATE(fp32_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fp32, fp32_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fpxxo, fp32_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fpxx, fp32_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fp64a, fp32_fp64a_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(any, fp32_fpxxo_fpxx) + END_STATE + + START_STATE(fp64a_fp32) + NEXT_REQ_FRE(fp32, fp64a_fp32) + NEXT_REQ_FRE(fp64a, fp64a_fp32) + NEXT(fpxxo, fp32_fp64a_fpxxo) + NEXT(fpxx, fp32_fp64a_fpxx) + NEXT(any, fp64a_fp32) + END_STATE + + START_STATE(fp64a_fpxx) + NEXT_REQ_FRE(fp32, fp32_fp64a_fpxx) + NEXT_REQ_FR1(fp64a, fp64a_fpxx) + NEXT(fpxx, fp64a_fpxx) + NEXT(fpxxo, fp64a_fpxx_fpxxo) + NEXT_REQ_FR1(fp64, fp64a_fp64_fpxx) + NEXT(any, fp64a_fpxx) + END_STATE + + START_STATE(fp64a_fpxxo) + NEXT_NO_MODE_CHANGE(fp32, fp32_fp64a_fpxxo) + NEXT_NO_MODE_CHANGE(fp64a, fp64a_fpxxo) + NEXT_NO_MODE_CHANGE(fpxx, fp64a_fpxx_fpxxo) + NEXT_NO_MODE_CHANGE(fpxxo, fp64a_fpxxo) + NEXT_NO_MODE_CHANGE(fp64, fp64a_fp64_fpxxo) + NEXT_NO_MODE_CHANGE(any, fp64a_fpxxo) + END_STATE + + START_STATE(fp64a_fpxx_fpxxo) + NEXT_NO_MODE_CHANGE(fp32, fp32_fp64a_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fp64a, fp64a_fpxx_fpxxo) + NEXT_NO_MODE_CHANGE(fpxx, fp64a_fpxx_fpxxo) + NEXT_NO_MODE_CHANGE(fpxxo, fp64a_fpxx_fpxxo) + NEXT_NO_MODE_CHANGE(fp64, fp64a_fp64_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(any, fp64a_fpxx_fpxxo) + END_STATE + + START_STATE(fp64_fpxx) + NEXT_REQ_FR1(fp64a, fp64a_fp64_fpxx) + NEXT(fpxxo, fp64_fpxx_fpxxo) + NEXT(fpxx, fp64_fpxx) + NEXT_REQ_FR1(fp64, fp64_fpxx) + NEXT(any, fp64_fpxx) + END_STATE + + START_STATE(fp64_fpxxo) + NEXT_NO_MODE_CHANGE(fp64a, fp64a_fp64_fpxxo) + NEXT_NO_MODE_CHANGE(fpxxo, fp64_fpxxo) + NEXT_NO_MODE_CHANGE(fpxx, fp64_fpxx_fpxxo) + NEXT_NO_MODE_CHANGE(fp64, fp64_fpxxo) + NEXT_NO_MODE_CHANGE(any, fp64_fpxxo) + END_STATE + + START_STATE(fp64_fpxx_fpxxo) + NEXT_NO_MODE_CHANGE(fp64a, fp64a_fp64_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fpxxo, fp64_fpxx_fpxxo) + NEXT_NO_MODE_CHANGE(fpxx, fp64_fpxx_fpxxo) + NEXT_NO_MODE_CHANGE(fp64, fp64_fpxx_fpxxo) + NEXT_NO_MODE_CHANGE(any, fp64_fpxx_fpxxo) + END_STATE + + START_STATE(fp64a_fp64) + NEXT_REQ_FR1(fp64a, fp64a_fp64) + NEXT(fpxxo, fp64a_fp64_fpxxo) + NEXT(fpxx, fp64a_fp64_fpxx) + NEXT_REQ_FR1(fp64, fp64a_fp64) + NEXT(any, fp64a_fp64) + END_STATE + + START_STATE(fp64a_fp64_fpxx) + NEXT_REQ_FR1(fp64a, fp64a_fp64_fpxx) + NEXT(fpxxo, fp64a_fp64_fpxxo_fpxx) + NEXT(fpxx, fp64a_fp64_fpxx) + NEXT_REQ_FR1(fp64, fp64a_fp64_fpxx) + NEXT(any, fp64a_fp64_fpxx) + END_STATE + + START_STATE(fp64a_fp64_fpxxo) + NEXT_NO_MODE_CHANGE(fp64a, fp64a_fp64_fpxxo) + NEXT_NO_MODE_CHANGE(fpxx, fp64a_fp64_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fpxxo, fp64a_fp64_fpxxo) + NEXT_NO_MODE_CHANGE(fp64, fp64a_fp64_fpxxo) + NEXT_NO_MODE_CHANGE(any, fp64a_fp64_fpxxo) + END_STATE + + START_STATE(fp64a_fp64_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fp64a, fp64a_fp64_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fpxx, fp64a_fp64_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fpxxo, fp64a_fp64_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fp64, fp64a_fp64_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(any, fp64a_fp64_fpxxo_fpxx) + END_STATE + + START_STATE(fp32_fp64a_fpxx) + NEXT_REQ_FRE(fp32, fp32_fp64a_fpxx) + NEXT_REQ_FRE(fp64a, fp32_fp64a_fpxx) + NEXT(fpxxo, fp32_fp64a_fpxxo_fpxx) + NEXT(fpxx, fp32_fp64a_fpxx) + NEXT(any, fp32_fp64a_fpxx) + END_STATE + + START_STATE(fp32_fp64a_fpxxo) + NEXT_NO_MODE_CHANGE(fp32, fp32_fp64a_fpxxo) + NEXT_NO_MODE_CHANGE(fp64a, fp32_fp64a_fpxxo) + NEXT_NO_MODE_CHANGE(fpxx, fp32_fp64a_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fpxxo, fp32_fp64a_fpxxo) + NEXT_NO_MODE_CHANGE(any, fp32_fp64a_fpxxo) + END_STATE + + START_STATE(fp32_fp64a_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fp32, fp32_fp64a_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fp64a, fp32_fp64a_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fpxx, fp32_fp64a_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(fpxxo, fp32_fp64a_fpxxo_fpxx) + NEXT_NO_MODE_CHANGE(any, fp32_fp64a_fpxxo_fpxx) + END_STATE + } + + if (obj != o_max) + fp_obj_count[obj]++; + + return true; +} + +/* Run the state machine by removing an object. */ + +static bool +remove_object (enum fp_obj obj) +{ + if (obj == o_max) + return false; + + fp_obj_count[obj]--; + + /* We can't change fp state until all the objects + of a particular type have been unloaded. */ + if (fp_obj_count[obj] != 0) + return false; + + switch (current_fp_state) + { + START_STATE(soft) + NEXT(soft,any) + END_STATE + + START_STATE(single) + NEXT(single,any) + END_STATE + + START_STATE(any) + NEXT(any,any) + END_STATE + + START_STATE(fp32) + NEXT (fp32,any) + END_STATE + + START_STATE(fpxx) + NEXT (fpxx,any) + END_STATE + + START_STATE(fpxxo) + NEXT (fpxxo,any) + END_STATE + + START_STATE(fp64a) + NEXT(fp64a, any) + END_STATE + + START_STATE(fp64) + NEXT(fp64, any) + END_STATE + + START_STATE(fpxxo_fpxx) + NEXT(fpxx, fpxxo) + NEXT(fpxxo, fpxx) + END_STATE + + START_STATE(fp32_fpxx) + NEXT(fp32, fpxx) + NEXT(fpxx, fp32) + END_STATE + + START_STATE(fp32_fpxxo) + NEXT(fp32, fpxxo) + NEXT(fpxxo, fp32) + END_STATE + + START_STATE(fp32_fpxxo_fpxx) + NEXT(fp32, fpxxo_fpxx) + NEXT(fpxxo, fp32_fpxx) + NEXT(fpxx, fp32_fpxxo) + END_STATE + + START_STATE(fp64a_fp32) + NEXT(fp32, fp64a) + NEXT(fp64a, fp32) + END_STATE + + START_STATE(fp64a_fpxx) + NEXT(fp64a, fpxx) + NEXT(fpxx, fp64a) + END_STATE + + START_STATE(fp64a_fpxxo) + NEXT(fp64a, fpxxo) + NEXT(fpxxo, fp64a) + END_STATE + + START_STATE(fp64a_fpxx_fpxxo) + NEXT(fp64a, fpxxo_fpxx) + NEXT(fpxx, fp64a_fpxxo) + NEXT(fpxxo, fp64a_fpxx) + END_STATE + + START_STATE(fp64_fpxx) + NEXT(fpxx, fp64) + NEXT(fp64, fpxx) + END_STATE + + START_STATE(fp64_fpxxo) + NEXT(fpxxo, fp64) + NEXT(fp64, fpxxo) + END_STATE + + START_STATE(fp64_fpxx_fpxxo) + NEXT(fp64, fpxxo_fpxx) + NEXT(fpxxo, fp64_fpxx) + NEXT(fpxx, fp64_fpxxo) + END_STATE + + START_STATE(fp64a_fp64) + NEXT(fp64a, fp64) + NEXT(fp64, fp64a) + END_STATE + + START_STATE(fp64a_fp64_fpxx) + NEXT(fp64a, fp64_fpxx) + NEXT(fpxx, fp64a_fp64) + NEXT(fp64, fp64a_fpxx) + END_STATE + + START_STATE(fp64a_fp64_fpxxo) + NEXT(fp64a, fp64_fpxxo) + NEXT(fpxxo, fp64a_fp64) + NEXT(fp64, fp64a_fpxxo) + END_STATE + + START_STATE(fp64a_fp64_fpxxo_fpxx) + NEXT(fp64a, fp64_fpxx_fpxxo) + NEXT(fpxx, fp64a_fp64_fpxxo) + NEXT(fpxxo, fp64a_fp64_fpxx) + NEXT(fp64, fp64a_fpxx_fpxxo) + END_STATE + + START_STATE(fp32_fp64a_fpxx) + NEXT(fp32, fp64a_fpxx) + NEXT(fp64a, fp32_fpxx) + NEXT(fpxx, fp64a_fp32) + END_STATE + + START_STATE(fp32_fp64a_fpxxo) + NEXT(fp32, fp64a_fpxxo) + NEXT(fp64a, fp32_fpxxo) + NEXT(fpxxo, fp64a_fp32) + END_STATE + + START_STATE(fp32_fp64a_fpxxo_fpxx) + NEXT(fp32, fp64a_fpxx_fpxxo) + NEXT(fp64a, fp32_fpxxo_fpxx) + NEXT(fpxx, fp32_fp64a_fpxxo) + NEXT(fpxxo, fp32_fp64a_fpxx) + END_STATE + } + + return true; +} + +static int +mode_transition_valid_p (void) +{ + int prev_fp_mode; + + /* Get the current fp mode. */ + prev_fp_mode = current_fp_mode; +#if HAVE_PRCTL_FP_MODE + current_fp_mode = prctl (PR_GET_FP_MODE); + + /* If the prctl call fails assume the core only has FR0 mode support. */ + if (current_fp_mode == -1) + current_fp_mode = 0; +#endif + + if (!current_mode_valid_p (current_fp_state)) + return 0; + + /* Check if mode changes are not allowed but a mode change happened. */ + if (cant_change_mode + && current_fp_mode != prev_fp_mode) + return 0; + + return 1; +} + +/* Load OBJ and check that it was/was not loaded correctly. */ +bool +load_object (enum fp_obj obj) +{ + bool should_load = set_next_fp_state (obj); + + shared_lib_ptrs[obj] = dlopen (shared_lib_names[obj], RTLD_LAZY); + + /* If we expected an error and the load was successful then fail. */ + if (!should_load && (shared_lib_ptrs[obj] != 0)) + return false; + + if (should_load && (shared_lib_ptrs[obj] == 0)) + return false; + + if (!mode_transition_valid_p ()) + return false; + + return true; +} + +/* Remove an object and check the state remains valid. */ +bool +unload_object (enum fp_obj obj) +{ + if (!shared_lib_ptrs[obj]) + return true; + + remove_object (obj); + + if (dlclose (shared_lib_ptrs[obj]) != 0) + return false; + + shared_lib_ptrs[obj] = 0; + + if (!mode_transition_valid_p ()) + return false; + + return true; +} + +/* Load every permuation of OBJECTS. */ +static bool +test_permutations (enum fp_obj objects[], int count) +{ + int i; + + for (i = 0 ; i < count ; i++) + { + if (!load_object (objects[i])) + return false; + + if (count > 1) + { + enum fp_obj new_objects[count - 1]; + int j; + int k = 0; + + for (j = 0 ; j < count ; j++) + { + if (j != i) + new_objects[k++] = objects[j]; + } + + if (!test_permutations (new_objects, count - 1)) + return false; + } + + if (!unload_object (objects[i])) + return false; + } + return true; +} + +int +do_test (void) +{ +#if HAVE_PRCTL_FP_MODE + /* Determine available hardware support and current mode. */ + current_fp_mode = prctl (PR_GET_FP_MODE); + + /* If the prctl call fails assume the core only has FR0 mode support. */ + if (current_fp_mode == -1) + current_fp_mode = 0; + else + { + if (prctl (PR_SET_FP_MODE, 0) != 0) + { + if (errno == ENOTSUP) + is_r6 = true; + else + { + printf ("unexpected error from PR_SET_FP_MODE, 0: %m\n"); + return 1; + } + } + + if (prctl (PR_SET_FP_MODE, PR_FP_MODE_FR) != 0) + { + if (errno != ENOTSUP) + { + printf ("unexpected error from PR_SET_FP_MODE, " + "PR_FP_MODE_FR: %m\n"); + return 1; + } + } + else + has_fr1 = true; + + if (prctl (PR_SET_FP_MODE, PR_FP_MODE_FR | PR_FP_MODE_FRE) != 0) + { + if (errno != ENOTSUP) + { + printf ("unexpected error from PR_SET_FP_MODE, " + "PR_FP_MODE_FR | PR_FP_MODE_FRE: %m\n"); + return 1; + } + } + else + has_fre = true; + + if (prctl (PR_SET_FP_MODE, current_fp_mode) != 0) + { + printf ("unable to restore initial FP mode: %m\n"); + return 1; + } + } + + if ((is_r6 && !(current_fp_mode & PR_FP_MODE_FR)) + || (!has_fr1 && (current_fp_mode & PR_FP_MODE_FR)) + || (!has_fre && (current_fp_mode & PR_FP_MODE_FRE))) + { + puts ("Inconsistency detected between initial FP mode " + "and supported FP modes\n"); + return 1; + } +#else + current_fp_mode = 0; +#endif + + /* Set up the initial state from executable and LDSO. Assumptions: + 1) All system libraries have the same ABI as ld.so. + 2) Due to the fact that ld.so is tested by invoking it directly + rather than via an interpreter, there is no point in varying + the ABI of the test program. Instead the ABI only varies for + the shared libraries which get loaded. */ + if (!set_next_fp_state (FPABI_NATIVE)) + { + puts ("Unable to enter initial ABI state\n"); + return 1; + } + + /* Compare the computed state with the hardware state. */ + if (!mode_transition_valid_p ()) + return 1; + + /* Run all possible test permutations. */ + if (!test_permutations (test_objects, FPABI_COUNT)) + { + puts ("Mode checks failed\n"); + return 1; + } + + return 0; +} + +#define TEST_FUNCTION do_test () +#include "../../test-skeleton.c" -- cgit v1.2.3