mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-21 04:42:53 +08:00
045f385d9a
This commit adds Zfhmin and Zhinxmin extensions (subsets of Zfh and Zhinx extensions, respectively). In the process supporting Zfhmin and Zhinxmin extension, this commit also changes how instructions are categorized considering Zfhmin, Zhinx and Zhinxmin extensions. Detailed changes, * From INSN_CLASS_ZFH to INSN_CLASS_ZFHMIN: flh, fsh, fmv.x.h and fmv.h.x. * From INSN_CLASS_ZFH to INSN_CLASS_ZFH_OR_ZHINX: fmv.h. * From INSN_CLASS_ZFH_OR_ZHINX to INSN_CLASS_ZFH_OR_ZHINX: fneg.h, fabs.h, fsgnj.h, fsgnjn.h, fsgnjx.h, fadd.h, fsub.h, fmul.h, fdiv.h, fsqrt.h, fmin.h, fmax.h, fmadd.h, fnmadd.h, fmsub.h, fnmsub.h, fcvt.w.h, fcvt.wu.h, fcvt.h.w, fcvt.h.wu, fcvt.l.h, fcvt.lu.h, fcvt.h.l, fcvt.h.lu, feq.h, flt.h, fle.h, fgt.h, fge.h, fclass.h. * From INSN_CLASS_ZFH_OR_ZHINX to INSN_CLASS_ZFHMIN_OR_ZHINXMIN: fcvt.s.h and fcvt.h.s. * From INSN_CLASS_D_AND_ZFH_INX to INSN_CLASS_ZFHMIN_AND_D: fcvt.d.h and fcvt.h.d. * From INSN_CLASS_Q_AND_ZFH_INX to INSN_CLASS_ZFHMIN_AND_Q: fcvt.q.h and fcvt.h.q. bfd/ChangeLog: * elfxx-riscv.c (riscv_implicit_subsets): Change implicit subsets. Zfh->Zicsr is not needed and Zfh->F is replaced with Zfh->Zfhmin and Zfhmin->F. Zhinx->Zicsr is not needed and Zhinx->Zfinx is replaced with Zhinx->Zhinxmin and Zhinxmin->Zfinx. (riscv_supported_std_z_ext): Added zfhmin and zhinxmin. (riscv_multi_subset_supports): Rewrite handling for new instruction classes. (riscv_multi_subset_supports_ext): Updated. (riscv_parse_check_conflicts): Change error message to include zfh and zfhmin extensions. gas/ChangeLog: * testsuite/gas/riscv/zfhmin-d-insn-class-fail.s: New complex error handling test. * testsuite/gas/riscv/zfhmin-d-insn-class-fail-1.d: Likewise. * testsuite/gas/riscv/zfhmin-d-insn-class-fail-1.l: Likewise. * testsuite/gas/riscv/zfhmin-d-insn-class-fail-2.d: Likewise. * testsuite/gas/riscv/zfhmin-d-insn-class-fail-2.l: Likewise. * testsuite/gas/riscv/zfhmin-d-insn-class-fail-3.d: Likewise. * testsuite/gas/riscv/zfhmin-d-insn-class-fail-3.l: Likewise. * testsuite/gas/riscv/zfhmin-d-insn-class-fail-4.d: Likewise. * testsuite/gas/riscv/zfhmin-d-insn-class-fail-4.l: Likewise. * testsuite/gas/riscv/zfhmin-d-insn-class-fail-5.d: Likewise. * testsuite/gas/riscv/zfhmin-d-insn-class-fail-5.l: Likewise. * testsuite/gas/riscv/zhinx.d: Renamed from fp-zhinx-insns.d and refactored. * testsuite/gas/riscv/zhinx.s: Likewise. include/ChangeLog: * opcode/riscv.h (enum riscv_insn_class): Removed INSN_CLASS_ZFH, INSN_CLASS_D_AND_ZFH_INX and INSN_CLASS_Q_AND_ZFH_INX. Added INSN_CLASS_ZFHMIN, INSN_CLASS_ZFHMIN_OR_ZHINXMIN, INSN_CLASS_ZFHMIN_AND_D and INSN_CLASS_ZFHMIN_AND_Q. opcodes/ChangeLog: * riscv-opc.c (riscv_opcodes): Change instruction classes for Zfh and Zfhmin instructions. Fix `fcvt.h.lu' instruction (two operand variant) mask.
2536 lines
73 KiB
C
2536 lines
73 KiB
C
/* RISC-V-specific support for ELF.
|
|
Copyright (C) 2011-2022 Free Software Foundation, Inc.
|
|
|
|
Contributed by Andrew Waterman (andrew@sifive.com).
|
|
Based on TILE-Gx and MIPS targets.
|
|
|
|
This file is part of BFD, the Binary File Descriptor library.
|
|
|
|
This program 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.
|
|
|
|
This program 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 this program; see the file COPYING3. If not,
|
|
see <http://www.gnu.org/licenses/>. */
|
|
|
|
#include "sysdep.h"
|
|
#include "bfd.h"
|
|
#include "libbfd.h"
|
|
#include "elf-bfd.h"
|
|
#include "elf/riscv.h"
|
|
#include "opcode/riscv.h"
|
|
#include "libiberty.h"
|
|
#include "elfxx-riscv.h"
|
|
#include "safe-ctype.h"
|
|
|
|
#define MINUS_ONE ((bfd_vma)0 - 1)
|
|
|
|
/* Special handler for ADD/SUB relocations that allows them to be filled out
|
|
both in the pre-linked and post-linked file. This is necessary to make
|
|
pre-linked debug info work, as due to linker relaxations we need to emit
|
|
relocations for the debug info. */
|
|
static bfd_reloc_status_type riscv_elf_add_sub_reloc
|
|
(bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
|
|
|
|
/* The relocation table used for SHT_RELA sections. */
|
|
|
|
static reloc_howto_type howto_table[] =
|
|
{
|
|
/* No relocation. */
|
|
HOWTO (R_RISCV_NONE, /* type */
|
|
0, /* rightshift */
|
|
0, /* size */
|
|
0, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_NONE", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 32 bit relocation. */
|
|
HOWTO (R_RISCV_32, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_32", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xffffffff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 64 bit relocation. */
|
|
HOWTO (R_RISCV_64, /* type */
|
|
0, /* rightshift */
|
|
8, /* size */
|
|
64, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_64", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
MINUS_ONE, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* Relocation against a local symbol in a shared object. */
|
|
HOWTO (R_RISCV_RELATIVE, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_RELATIVE", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xffffffff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
HOWTO (R_RISCV_COPY, /* type */
|
|
0, /* rightshift */
|
|
0, /* this one is variable size */
|
|
0, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_bitfield, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_COPY", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
HOWTO (R_RISCV_JUMP_SLOT, /* type */
|
|
0, /* rightshift */
|
|
8, /* size */
|
|
64, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_bitfield, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_JUMP_SLOT", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* Dynamic TLS relocations. */
|
|
HOWTO (R_RISCV_TLS_DTPMOD32, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TLS_DTPMOD32", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xffffffff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
HOWTO (R_RISCV_TLS_DTPMOD64, /* type */
|
|
0, /* rightshift */
|
|
8, /* size */
|
|
64, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TLS_DTPMOD64", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
MINUS_ONE, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
HOWTO (R_RISCV_TLS_DTPREL32, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TLS_DTPREL32", /* name */
|
|
true, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xffffffff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
HOWTO (R_RISCV_TLS_DTPREL64, /* type */
|
|
0, /* rightshift */
|
|
8, /* size */
|
|
64, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TLS_DTPREL64", /* name */
|
|
true, /* partial_inplace */
|
|
0, /* src_mask */
|
|
MINUS_ONE, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
HOWTO (R_RISCV_TLS_TPREL32, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TLS_TPREL32", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xffffffff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
HOWTO (R_RISCV_TLS_TPREL64, /* type */
|
|
0, /* rightshift */
|
|
8, /* size */
|
|
64, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TLS_TPREL64", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
MINUS_ONE, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* Reserved for future relocs that the dynamic linker must understand. */
|
|
EMPTY_HOWTO (12),
|
|
EMPTY_HOWTO (13),
|
|
EMPTY_HOWTO (14),
|
|
EMPTY_HOWTO (15),
|
|
|
|
/* 12-bit PC-relative branch offset. */
|
|
HOWTO (R_RISCV_BRANCH, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
true, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_signed, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_BRANCH", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_BTYPE_IMM (-1U), /* dst_mask */
|
|
true), /* pcrel_offset */
|
|
|
|
/* 20-bit PC-relative jump offset. */
|
|
HOWTO (R_RISCV_JAL, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
true, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_JAL", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_JTYPE_IMM (-1U), /* dst_mask */
|
|
true), /* pcrel_offset */
|
|
|
|
/* 32-bit PC-relative function call (AUIPC/JALR). */
|
|
HOWTO (R_RISCV_CALL, /* type */
|
|
0, /* rightshift */
|
|
8, /* size */
|
|
64, /* bitsize */
|
|
true, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_CALL", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_UTYPE_IMM (-1U) | ((bfd_vma) ENCODE_ITYPE_IMM (-1U) << 32),
|
|
/* dst_mask */
|
|
true), /* pcrel_offset */
|
|
|
|
/* Like R_RISCV_CALL, but not locally binding. */
|
|
HOWTO (R_RISCV_CALL_PLT, /* type */
|
|
0, /* rightshift */
|
|
8, /* size */
|
|
64, /* bitsize */
|
|
true, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_CALL_PLT", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_UTYPE_IMM (-1U) | ((bfd_vma) ENCODE_ITYPE_IMM (-1U) << 32),
|
|
/* dst_mask */
|
|
true), /* pcrel_offset */
|
|
|
|
/* High 20 bits of 32-bit PC-relative GOT access. */
|
|
HOWTO (R_RISCV_GOT_HI20, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
true, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_GOT_HI20", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_UTYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* High 20 bits of 32-bit PC-relative TLS IE GOT access. */
|
|
HOWTO (R_RISCV_TLS_GOT_HI20, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
true, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TLS_GOT_HI20", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_UTYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* High 20 bits of 32-bit PC-relative TLS GD GOT reference. */
|
|
HOWTO (R_RISCV_TLS_GD_HI20, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
true, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TLS_GD_HI20", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_UTYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* High 20 bits of 32-bit PC-relative reference. */
|
|
HOWTO (R_RISCV_PCREL_HI20, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
true, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_PCREL_HI20", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_UTYPE_IMM (-1U), /* dst_mask */
|
|
true), /* pcrel_offset */
|
|
|
|
/* Low 12 bits of a 32-bit PC-relative load or add. */
|
|
HOWTO (R_RISCV_PCREL_LO12_I, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_PCREL_LO12_I", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_ITYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* Low 12 bits of a 32-bit PC-relative store. */
|
|
HOWTO (R_RISCV_PCREL_LO12_S, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_PCREL_LO12_S", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_STYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* High 20 bits of 32-bit absolute address. */
|
|
HOWTO (R_RISCV_HI20, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_HI20", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_UTYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* High 12 bits of 32-bit load or add. */
|
|
HOWTO (R_RISCV_LO12_I, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_LO12_I", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_ITYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* High 12 bits of 32-bit store. */
|
|
HOWTO (R_RISCV_LO12_S, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_LO12_S", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_STYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* High 20 bits of TLS LE thread pointer offset. */
|
|
HOWTO (R_RISCV_TPREL_HI20, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_signed, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TPREL_HI20", /* name */
|
|
true, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_UTYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* Low 12 bits of TLS LE thread pointer offset for loads and adds. */
|
|
HOWTO (R_RISCV_TPREL_LO12_I, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_signed, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TPREL_LO12_I", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_ITYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* Low 12 bits of TLS LE thread pointer offset for stores. */
|
|
HOWTO (R_RISCV_TPREL_LO12_S, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_signed, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TPREL_LO12_S", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_STYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* TLS LE thread pointer usage. May be relaxed. */
|
|
HOWTO (R_RISCV_TPREL_ADD, /* type */
|
|
0, /* rightshift */
|
|
0, /* size */
|
|
0, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TPREL_ADD", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 8-bit in-place addition, for local label subtraction. */
|
|
HOWTO (R_RISCV_ADD8, /* type */
|
|
0, /* rightshift */
|
|
1, /* size */
|
|
8, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
riscv_elf_add_sub_reloc, /* special_function */
|
|
"R_RISCV_ADD8", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 16-bit in-place addition, for local label subtraction. */
|
|
HOWTO (R_RISCV_ADD16, /* type */
|
|
0, /* rightshift */
|
|
2, /* size */
|
|
16, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
riscv_elf_add_sub_reloc, /* special_function */
|
|
"R_RISCV_ADD16", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xffff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 32-bit in-place addition, for local label subtraction. */
|
|
HOWTO (R_RISCV_ADD32, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
riscv_elf_add_sub_reloc, /* special_function */
|
|
"R_RISCV_ADD32", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xffffffff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 64-bit in-place addition, for local label subtraction. */
|
|
HOWTO (R_RISCV_ADD64, /* type */
|
|
0, /* rightshift */
|
|
8, /* size */
|
|
64, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
riscv_elf_add_sub_reloc, /* special_function */
|
|
"R_RISCV_ADD64", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
MINUS_ONE, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 8-bit in-place addition, for local label subtraction. */
|
|
HOWTO (R_RISCV_SUB8, /* type */
|
|
0, /* rightshift */
|
|
1, /* size */
|
|
8, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
riscv_elf_add_sub_reloc, /* special_function */
|
|
"R_RISCV_SUB8", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 16-bit in-place addition, for local label subtraction. */
|
|
HOWTO (R_RISCV_SUB16, /* type */
|
|
0, /* rightshift */
|
|
2, /* size */
|
|
16, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
riscv_elf_add_sub_reloc, /* special_function */
|
|
"R_RISCV_SUB16", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xffff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 32-bit in-place addition, for local label subtraction. */
|
|
HOWTO (R_RISCV_SUB32, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
riscv_elf_add_sub_reloc, /* special_function */
|
|
"R_RISCV_SUB32", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xffffffff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 64-bit in-place addition, for local label subtraction. */
|
|
HOWTO (R_RISCV_SUB64, /* type */
|
|
0, /* rightshift */
|
|
8, /* size */
|
|
64, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
riscv_elf_add_sub_reloc, /* special_function */
|
|
"R_RISCV_SUB64", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
MINUS_ONE, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* GNU extension to record C++ vtable hierarchy */
|
|
HOWTO (R_RISCV_GNU_VTINHERIT, /* type */
|
|
0, /* rightshift */
|
|
8, /* size */
|
|
0, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
NULL, /* special_function */
|
|
"R_RISCV_GNU_VTINHERIT", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* GNU extension to record C++ vtable member usage */
|
|
HOWTO (R_RISCV_GNU_VTENTRY, /* type */
|
|
0, /* rightshift */
|
|
8, /* size */
|
|
0, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
_bfd_elf_rel_vtable_reloc_fn, /* special_function */
|
|
"R_RISCV_GNU_VTENTRY", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* Indicates an alignment statement. The addend field encodes how many
|
|
bytes of NOPs follow the statement. The desired alignment is the
|
|
addend rounded up to the next power of two. */
|
|
HOWTO (R_RISCV_ALIGN, /* type */
|
|
0, /* rightshift */
|
|
0, /* size */
|
|
0, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_ALIGN", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 8-bit PC-relative branch offset. */
|
|
HOWTO (R_RISCV_RVC_BRANCH, /* type */
|
|
0, /* rightshift */
|
|
2, /* size */
|
|
16, /* bitsize */
|
|
true, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_signed, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_RVC_BRANCH", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_CBTYPE_IMM (-1U), /* dst_mask */
|
|
true), /* pcrel_offset */
|
|
|
|
/* 11-bit PC-relative jump offset. */
|
|
HOWTO (R_RISCV_RVC_JUMP, /* type */
|
|
0, /* rightshift */
|
|
2, /* size */
|
|
16, /* bitsize */
|
|
true, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_RVC_JUMP", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_CJTYPE_IMM (-1U), /* dst_mask */
|
|
true), /* pcrel_offset */
|
|
|
|
/* High 6 bits of 18-bit absolute address. */
|
|
HOWTO (R_RISCV_RVC_LUI, /* type */
|
|
0, /* rightshift */
|
|
2, /* size */
|
|
16, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_RVC_LUI", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_CITYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* GP-relative load. */
|
|
HOWTO (R_RISCV_GPREL_I, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_GPREL_I", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_ITYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* GP-relative store. */
|
|
HOWTO (R_RISCV_GPREL_S, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_GPREL_S", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_STYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* TP-relative TLS LE load. */
|
|
HOWTO (R_RISCV_TPREL_I, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_signed, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TPREL_I", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_ITYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* TP-relative TLS LE store. */
|
|
HOWTO (R_RISCV_TPREL_S, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_signed, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_TPREL_S", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
ENCODE_STYPE_IMM (-1U), /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* The paired relocation may be relaxed. */
|
|
HOWTO (R_RISCV_RELAX, /* type */
|
|
0, /* rightshift */
|
|
0, /* size */
|
|
0, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_RELAX", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 6-bit in-place addition, for local label subtraction. */
|
|
HOWTO (R_RISCV_SUB6, /* type */
|
|
0, /* rightshift */
|
|
1, /* size */
|
|
8, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
riscv_elf_add_sub_reloc, /* special_function */
|
|
"R_RISCV_SUB6", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0x3f, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 6-bit in-place setting, for local label subtraction. */
|
|
HOWTO (R_RISCV_SET6, /* type */
|
|
0, /* rightshift */
|
|
1, /* size */
|
|
8, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_SET6", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0x3f, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 8-bit in-place setting, for local label subtraction. */
|
|
HOWTO (R_RISCV_SET8, /* type */
|
|
0, /* rightshift */
|
|
1, /* size */
|
|
8, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_SET8", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 16-bit in-place setting, for local label subtraction. */
|
|
HOWTO (R_RISCV_SET16, /* type */
|
|
0, /* rightshift */
|
|
2, /* size */
|
|
16, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_SET16", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xffff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 32-bit in-place setting, for local label subtraction. */
|
|
HOWTO (R_RISCV_SET32, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_SET32", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xffffffff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* 32-bit PC relative. */
|
|
HOWTO (R_RISCV_32_PCREL, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
true, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_32_PCREL", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xffffffff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
|
|
/* Relocation against a local ifunc symbol in a shared object. */
|
|
HOWTO (R_RISCV_IRELATIVE, /* type */
|
|
0, /* rightshift */
|
|
4, /* size */
|
|
32, /* bitsize */
|
|
false, /* pc_relative */
|
|
0, /* bitpos */
|
|
complain_overflow_dont, /* complain_on_overflow */
|
|
bfd_elf_generic_reloc, /* special_function */
|
|
"R_RISCV_IRELATIVE", /* name */
|
|
false, /* partial_inplace */
|
|
0, /* src_mask */
|
|
0xffffffff, /* dst_mask */
|
|
false), /* pcrel_offset */
|
|
};
|
|
|
|
/* A mapping from BFD reloc types to RISC-V ELF reloc types. */
|
|
struct elf_reloc_map
|
|
{
|
|
bfd_reloc_code_real_type bfd_val;
|
|
enum elf_riscv_reloc_type elf_val;
|
|
};
|
|
|
|
static const struct elf_reloc_map riscv_reloc_map[] =
|
|
{
|
|
{ BFD_RELOC_NONE, R_RISCV_NONE },
|
|
{ BFD_RELOC_32, R_RISCV_32 },
|
|
{ BFD_RELOC_64, R_RISCV_64 },
|
|
{ BFD_RELOC_RISCV_ADD8, R_RISCV_ADD8 },
|
|
{ BFD_RELOC_RISCV_ADD16, R_RISCV_ADD16 },
|
|
{ BFD_RELOC_RISCV_ADD32, R_RISCV_ADD32 },
|
|
{ BFD_RELOC_RISCV_ADD64, R_RISCV_ADD64 },
|
|
{ BFD_RELOC_RISCV_SUB8, R_RISCV_SUB8 },
|
|
{ BFD_RELOC_RISCV_SUB16, R_RISCV_SUB16 },
|
|
{ BFD_RELOC_RISCV_SUB32, R_RISCV_SUB32 },
|
|
{ BFD_RELOC_RISCV_SUB64, R_RISCV_SUB64 },
|
|
{ BFD_RELOC_CTOR, R_RISCV_64 },
|
|
{ BFD_RELOC_12_PCREL, R_RISCV_BRANCH },
|
|
{ BFD_RELOC_RISCV_HI20, R_RISCV_HI20 },
|
|
{ BFD_RELOC_RISCV_LO12_I, R_RISCV_LO12_I },
|
|
{ BFD_RELOC_RISCV_LO12_S, R_RISCV_LO12_S },
|
|
{ BFD_RELOC_RISCV_PCREL_LO12_I, R_RISCV_PCREL_LO12_I },
|
|
{ BFD_RELOC_RISCV_PCREL_LO12_S, R_RISCV_PCREL_LO12_S },
|
|
{ BFD_RELOC_RISCV_CALL, R_RISCV_CALL },
|
|
{ BFD_RELOC_RISCV_CALL_PLT, R_RISCV_CALL_PLT },
|
|
{ BFD_RELOC_RISCV_PCREL_HI20, R_RISCV_PCREL_HI20 },
|
|
{ BFD_RELOC_RISCV_JMP, R_RISCV_JAL },
|
|
{ BFD_RELOC_RISCV_GOT_HI20, R_RISCV_GOT_HI20 },
|
|
{ BFD_RELOC_RISCV_TLS_DTPMOD32, R_RISCV_TLS_DTPMOD32 },
|
|
{ BFD_RELOC_RISCV_TLS_DTPREL32, R_RISCV_TLS_DTPREL32 },
|
|
{ BFD_RELOC_RISCV_TLS_DTPMOD64, R_RISCV_TLS_DTPMOD64 },
|
|
{ BFD_RELOC_RISCV_TLS_DTPREL64, R_RISCV_TLS_DTPREL64 },
|
|
{ BFD_RELOC_RISCV_TLS_TPREL32, R_RISCV_TLS_TPREL32 },
|
|
{ BFD_RELOC_RISCV_TLS_TPREL64, R_RISCV_TLS_TPREL64 },
|
|
{ BFD_RELOC_RISCV_TPREL_HI20, R_RISCV_TPREL_HI20 },
|
|
{ BFD_RELOC_RISCV_TPREL_ADD, R_RISCV_TPREL_ADD },
|
|
{ BFD_RELOC_RISCV_TPREL_LO12_S, R_RISCV_TPREL_LO12_S },
|
|
{ BFD_RELOC_RISCV_TPREL_LO12_I, R_RISCV_TPREL_LO12_I },
|
|
{ BFD_RELOC_RISCV_TLS_GOT_HI20, R_RISCV_TLS_GOT_HI20 },
|
|
{ BFD_RELOC_RISCV_TLS_GD_HI20, R_RISCV_TLS_GD_HI20 },
|
|
{ BFD_RELOC_RISCV_ALIGN, R_RISCV_ALIGN },
|
|
{ BFD_RELOC_RISCV_RVC_BRANCH, R_RISCV_RVC_BRANCH },
|
|
{ BFD_RELOC_RISCV_RVC_JUMP, R_RISCV_RVC_JUMP },
|
|
{ BFD_RELOC_RISCV_RVC_LUI, R_RISCV_RVC_LUI },
|
|
{ BFD_RELOC_RISCV_GPREL_I, R_RISCV_GPREL_I },
|
|
{ BFD_RELOC_RISCV_GPREL_S, R_RISCV_GPREL_S },
|
|
{ BFD_RELOC_RISCV_TPREL_I, R_RISCV_TPREL_I },
|
|
{ BFD_RELOC_RISCV_TPREL_S, R_RISCV_TPREL_S },
|
|
{ BFD_RELOC_RISCV_RELAX, R_RISCV_RELAX },
|
|
{ BFD_RELOC_RISCV_SUB6, R_RISCV_SUB6 },
|
|
{ BFD_RELOC_RISCV_SET6, R_RISCV_SET6 },
|
|
{ BFD_RELOC_RISCV_SET8, R_RISCV_SET8 },
|
|
{ BFD_RELOC_RISCV_SET16, R_RISCV_SET16 },
|
|
{ BFD_RELOC_RISCV_SET32, R_RISCV_SET32 },
|
|
{ BFD_RELOC_RISCV_32_PCREL, R_RISCV_32_PCREL },
|
|
};
|
|
|
|
/* Given a BFD reloc type, return a howto structure. */
|
|
|
|
reloc_howto_type *
|
|
riscv_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
|
|
bfd_reloc_code_real_type code)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE (riscv_reloc_map); i++)
|
|
if (riscv_reloc_map[i].bfd_val == code)
|
|
return &howto_table[(int) riscv_reloc_map[i].elf_val];
|
|
|
|
bfd_set_error (bfd_error_bad_value);
|
|
return NULL;
|
|
}
|
|
|
|
reloc_howto_type *
|
|
riscv_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, const char *r_name)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE (howto_table); i++)
|
|
if (howto_table[i].name && strcasecmp (howto_table[i].name, r_name) == 0)
|
|
return &howto_table[i];
|
|
|
|
return NULL;
|
|
}
|
|
|
|
reloc_howto_type *
|
|
riscv_elf_rtype_to_howto (bfd *abfd, unsigned int r_type)
|
|
{
|
|
if (r_type >= ARRAY_SIZE (howto_table))
|
|
{
|
|
(*_bfd_error_handler) (_("%pB: unsupported relocation type %#x"),
|
|
abfd, r_type);
|
|
bfd_set_error (bfd_error_bad_value);
|
|
return NULL;
|
|
}
|
|
return &howto_table[r_type];
|
|
}
|
|
|
|
/* Special_function of RISCV_ADD and RISCV_SUB relocations. */
|
|
|
|
static bfd_reloc_status_type
|
|
riscv_elf_add_sub_reloc (bfd *abfd,
|
|
arelent *reloc_entry,
|
|
asymbol *symbol,
|
|
void *data,
|
|
asection *input_section,
|
|
bfd *output_bfd,
|
|
char **error_message ATTRIBUTE_UNUSED)
|
|
{
|
|
reloc_howto_type *howto = reloc_entry->howto;
|
|
bfd_vma relocation;
|
|
|
|
if (output_bfd != NULL
|
|
&& (symbol->flags & BSF_SECTION_SYM) == 0
|
|
&& (!reloc_entry->howto->partial_inplace || reloc_entry->addend == 0))
|
|
{
|
|
reloc_entry->address += input_section->output_offset;
|
|
return bfd_reloc_ok;
|
|
}
|
|
|
|
if (output_bfd != NULL)
|
|
return bfd_reloc_continue;
|
|
|
|
relocation = symbol->value + symbol->section->output_section->vma
|
|
+ symbol->section->output_offset + reloc_entry->addend;
|
|
|
|
bfd_size_type octets = reloc_entry->address
|
|
* bfd_octets_per_byte (abfd, input_section);
|
|
if (!bfd_reloc_offset_in_range (reloc_entry->howto, abfd,
|
|
input_section, octets))
|
|
return bfd_reloc_outofrange;
|
|
|
|
bfd_vma old_value = bfd_get (howto->bitsize, abfd,
|
|
data + reloc_entry->address);
|
|
|
|
switch (howto->type)
|
|
{
|
|
case R_RISCV_ADD8:
|
|
case R_RISCV_ADD16:
|
|
case R_RISCV_ADD32:
|
|
case R_RISCV_ADD64:
|
|
relocation = old_value + relocation;
|
|
break;
|
|
case R_RISCV_SUB6:
|
|
case R_RISCV_SUB8:
|
|
case R_RISCV_SUB16:
|
|
case R_RISCV_SUB32:
|
|
case R_RISCV_SUB64:
|
|
relocation = old_value - relocation;
|
|
break;
|
|
}
|
|
bfd_put (howto->bitsize, abfd, relocation, data + reloc_entry->address);
|
|
|
|
return bfd_reloc_ok;
|
|
}
|
|
|
|
/* Always add the IMPLICIT for the SUBSET. */
|
|
|
|
static bool
|
|
check_implicit_always (const char *implicit ATTRIBUTE_UNUSED,
|
|
riscv_subset_t *subset ATTRIBUTE_UNUSED)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/* Add the IMPLICIT only when the version of SUBSET less than 2.1. */
|
|
|
|
static bool
|
|
check_implicit_for_i (const char *implicit ATTRIBUTE_UNUSED,
|
|
riscv_subset_t *subset)
|
|
{
|
|
return (subset->major_version < 2
|
|
|| (subset->major_version == 2
|
|
&& subset->minor_version < 1));
|
|
}
|
|
|
|
/* Record all implicit information for the subsets. */
|
|
struct riscv_implicit_subset
|
|
{
|
|
const char *subset_name;
|
|
const char *implicit_name;
|
|
/* A function to determine if we need to add the implicit subset. */
|
|
bool (*check_func) (const char *, riscv_subset_t *);
|
|
};
|
|
static struct riscv_implicit_subset riscv_implicit_subsets[] =
|
|
{
|
|
{"e", "i", check_implicit_always},
|
|
{"i", "zicsr", check_implicit_for_i},
|
|
{"i", "zifencei", check_implicit_for_i},
|
|
{"g", "i", check_implicit_always},
|
|
{"g", "m", check_implicit_always},
|
|
{"g", "a", check_implicit_always},
|
|
{"g", "f", check_implicit_always},
|
|
{"g", "d", check_implicit_always},
|
|
{"g", "zicsr", check_implicit_always},
|
|
{"g", "zifencei", check_implicit_always},
|
|
{"q", "d", check_implicit_always},
|
|
{"v", "d", check_implicit_always},
|
|
{"v", "zve64d", check_implicit_always},
|
|
{"v", "zvl128b", check_implicit_always},
|
|
{"zve64d", "d", check_implicit_always},
|
|
{"zve64d", "zve64f", check_implicit_always},
|
|
{"zve64f", "zve32f", check_implicit_always},
|
|
{"zve64f", "zve64x", check_implicit_always},
|
|
{"zve64f", "zvl64b", check_implicit_always},
|
|
{"zve32f", "f", check_implicit_always},
|
|
{"zve32f", "zvl32b", check_implicit_always},
|
|
{"zve32f", "zve32x", check_implicit_always},
|
|
{"zve64x", "zve32x", check_implicit_always},
|
|
{"zve64x", "zvl64b", check_implicit_always},
|
|
{"zve32x", "zvl32b", check_implicit_always},
|
|
{"zvl65536b", "zvl32768b", check_implicit_always},
|
|
{"zvl32768b", "zvl16384b", check_implicit_always},
|
|
{"zvl16384b", "zvl8192b", check_implicit_always},
|
|
{"zvl8192b", "zvl4096b", check_implicit_always},
|
|
{"zvl4096b", "zvl2048b", check_implicit_always},
|
|
{"zvl2048b", "zvl1024b", check_implicit_always},
|
|
{"zvl1024b", "zvl512b", check_implicit_always},
|
|
{"zvl512b", "zvl256b", check_implicit_always},
|
|
{"zvl256b", "zvl128b", check_implicit_always},
|
|
{"zvl128b", "zvl64b", check_implicit_always},
|
|
{"zvl64b", "zvl32b", check_implicit_always},
|
|
{"d", "f", check_implicit_always},
|
|
{"zfh", "zfhmin", check_implicit_always},
|
|
{"zfhmin", "f", check_implicit_always},
|
|
{"f", "zicsr", check_implicit_always},
|
|
{"zqinx", "zdinx", check_implicit_always},
|
|
{"zdinx", "zfinx", check_implicit_always},
|
|
{"zhinx", "zhinxmin", check_implicit_always},
|
|
{"zhinxmin", "zfinx", check_implicit_always},
|
|
{"zfinx", "zicsr", check_implicit_always},
|
|
{"zk", "zkn", check_implicit_always},
|
|
{"zk", "zkr", check_implicit_always},
|
|
{"zk", "zkt", check_implicit_always},
|
|
{"zkn", "zbkb", check_implicit_always},
|
|
{"zkn", "zbkc", check_implicit_always},
|
|
{"zkn", "zbkx", check_implicit_always},
|
|
{"zkn", "zkne", check_implicit_always},
|
|
{"zkn", "zknd", check_implicit_always},
|
|
{"zkn", "zknh", check_implicit_always},
|
|
{"zks", "zbkb", check_implicit_always},
|
|
{"zks", "zbkc", check_implicit_always},
|
|
{"zks", "zbkx", check_implicit_always},
|
|
{"zks", "zksed", check_implicit_always},
|
|
{"zks", "zksh", check_implicit_always},
|
|
{NULL, NULL, NULL}
|
|
};
|
|
|
|
/* For default_enable field, decide if the extension should
|
|
be enbaled by default. */
|
|
|
|
#define EXT_DEFAULT 0x1
|
|
|
|
/* List all extensions that binutils should know about. */
|
|
|
|
struct riscv_supported_ext
|
|
{
|
|
const char *name;
|
|
enum riscv_spec_class isa_spec_class;
|
|
int major_version;
|
|
int minor_version;
|
|
unsigned long default_enable;
|
|
};
|
|
|
|
/* The standard extensions must be added in canonical order. */
|
|
|
|
static struct riscv_supported_ext riscv_supported_std_ext[] =
|
|
{
|
|
{"e", ISA_SPEC_CLASS_20191213, 1, 9, 0 },
|
|
{"e", ISA_SPEC_CLASS_20190608, 1, 9, 0 },
|
|
{"e", ISA_SPEC_CLASS_2P2, 1, 9, 0 },
|
|
{"i", ISA_SPEC_CLASS_20191213, 2, 1, 0 },
|
|
{"i", ISA_SPEC_CLASS_20190608, 2, 1, 0 },
|
|
{"i", ISA_SPEC_CLASS_2P2, 2, 0, 0 },
|
|
/* The g is a special case which we don't want to output it,
|
|
but still need it when adding implicit extensions. */
|
|
{"g", ISA_SPEC_CLASS_NONE, RISCV_UNKNOWN_VERSION, RISCV_UNKNOWN_VERSION, EXT_DEFAULT },
|
|
{"m", ISA_SPEC_CLASS_20191213, 2, 0, 0 },
|
|
{"m", ISA_SPEC_CLASS_20190608, 2, 0, 0 },
|
|
{"m", ISA_SPEC_CLASS_2P2, 2, 0, 0 },
|
|
{"a", ISA_SPEC_CLASS_20191213, 2, 1, 0 },
|
|
{"a", ISA_SPEC_CLASS_20190608, 2, 0, 0 },
|
|
{"a", ISA_SPEC_CLASS_2P2, 2, 0, 0 },
|
|
{"f", ISA_SPEC_CLASS_20191213, 2, 2, 0 },
|
|
{"f", ISA_SPEC_CLASS_20190608, 2, 2, 0 },
|
|
{"f", ISA_SPEC_CLASS_2P2, 2, 0, 0 },
|
|
{"d", ISA_SPEC_CLASS_20191213, 2, 2, 0 },
|
|
{"d", ISA_SPEC_CLASS_20190608, 2, 2, 0 },
|
|
{"d", ISA_SPEC_CLASS_2P2, 2, 0, 0 },
|
|
{"q", ISA_SPEC_CLASS_20191213, 2, 2, 0 },
|
|
{"q", ISA_SPEC_CLASS_20190608, 2, 2, 0 },
|
|
{"q", ISA_SPEC_CLASS_2P2, 2, 0, 0 },
|
|
{"c", ISA_SPEC_CLASS_20191213, 2, 0, 0 },
|
|
{"c", ISA_SPEC_CLASS_20190608, 2, 0, 0 },
|
|
{"c", ISA_SPEC_CLASS_2P2, 2, 0, 0 },
|
|
{"v", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"h", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{NULL, 0, 0, 0, 0}
|
|
};
|
|
|
|
static struct riscv_supported_ext riscv_supported_std_z_ext[] =
|
|
{
|
|
{"zicbom", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zicbop", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zicboz", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zicsr", ISA_SPEC_CLASS_20191213, 2, 0, 0 },
|
|
{"zicsr", ISA_SPEC_CLASS_20190608, 2, 0, 0 },
|
|
{"zifencei", ISA_SPEC_CLASS_20191213, 2, 0, 0 },
|
|
{"zifencei", ISA_SPEC_CLASS_20190608, 2, 0, 0 },
|
|
{"zihintpause", ISA_SPEC_CLASS_DRAFT, 2, 0, 0 },
|
|
{"zfh", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zfhmin", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zfinx", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zdinx", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zqinx", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zhinx", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zhinxmin", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zbb", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zba", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zbc", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zbs", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zbkb", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zbkc", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zbkx", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zk", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zkn", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zknd", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zkne", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zknh", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zkr", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zks", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zksed", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zksh", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zkt", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zve32x", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zve32f", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zve32d", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zve64x", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zve64f", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zve64d", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvl32b", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvl64b", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvl128b", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvl256b", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvl512b", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvl1024b", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvl2048b", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvl4096b", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvl8192b", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvl16384b", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvl32768b", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"zvl65536b", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{NULL, 0, 0, 0, 0}
|
|
};
|
|
|
|
static struct riscv_supported_ext riscv_supported_std_s_ext[] =
|
|
{
|
|
{"smstateen", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"sscofpmf", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"sstc", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{"svinval", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
|
{NULL, 0, 0, 0, 0}
|
|
};
|
|
|
|
static struct riscv_supported_ext riscv_supported_std_zxm_ext[] =
|
|
{
|
|
{NULL, 0, 0, 0, 0}
|
|
};
|
|
|
|
const struct riscv_supported_ext *riscv_all_supported_ext[] =
|
|
{
|
|
riscv_supported_std_ext,
|
|
riscv_supported_std_z_ext,
|
|
riscv_supported_std_s_ext,
|
|
riscv_supported_std_zxm_ext,
|
|
NULL
|
|
};
|
|
|
|
/* ISA extension prefixed name class. Must define them in parsing order. */
|
|
enum riscv_prefix_ext_class
|
|
{
|
|
RV_ISA_CLASS_Z = 1,
|
|
RV_ISA_CLASS_S,
|
|
RV_ISA_CLASS_ZXM,
|
|
RV_ISA_CLASS_X,
|
|
RV_ISA_CLASS_UNKNOWN
|
|
};
|
|
|
|
/* Record the strings of the prefixed extensions, and their corresponding
|
|
classes. The more letters of the prefix string, the more forward it must
|
|
be defined. Otherwise, the riscv_get_prefix_class will map it to the
|
|
wrong classes. */
|
|
struct riscv_parse_prefix_config
|
|
{
|
|
/* Class of the extension. */
|
|
enum riscv_prefix_ext_class class;
|
|
|
|
/* Prefix string for error printing and internal parser usage. */
|
|
const char *prefix;
|
|
};
|
|
static const struct riscv_parse_prefix_config parse_config[] =
|
|
{
|
|
{RV_ISA_CLASS_ZXM, "zxm"},
|
|
{RV_ISA_CLASS_Z, "z"},
|
|
{RV_ISA_CLASS_S, "s"},
|
|
{RV_ISA_CLASS_X, "x"},
|
|
{RV_ISA_CLASS_UNKNOWN, NULL}
|
|
};
|
|
|
|
/* Get the prefixed name class for the extensions, the class also
|
|
means the order of the prefixed extensions. */
|
|
|
|
static enum riscv_prefix_ext_class
|
|
riscv_get_prefix_class (const char *arch)
|
|
{
|
|
int i = 0;
|
|
while (parse_config[i].class != RV_ISA_CLASS_UNKNOWN)
|
|
{
|
|
if (strncmp (arch, parse_config[i].prefix,
|
|
strlen (parse_config[i].prefix)) == 0)
|
|
return parse_config[i].class;
|
|
i++;
|
|
}
|
|
return RV_ISA_CLASS_UNKNOWN;
|
|
}
|
|
|
|
/* Check KNOWN_EXTS to see if the EXT is supported. */
|
|
|
|
static bool
|
|
riscv_known_prefixed_ext (const char *ext,
|
|
struct riscv_supported_ext *known_exts)
|
|
{
|
|
size_t i;
|
|
for (i = 0; known_exts[i].name != NULL; ++i)
|
|
if (strcmp (ext, known_exts[i].name) == 0)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/* Check whether the prefixed extension is recognized or not. Return
|
|
true if recognized, otehrwise return false. */
|
|
|
|
static bool
|
|
riscv_recognized_prefixed_ext (const char *ext)
|
|
{
|
|
enum riscv_prefix_ext_class class = riscv_get_prefix_class (ext);
|
|
switch (class)
|
|
{
|
|
case RV_ISA_CLASS_Z:
|
|
return riscv_known_prefixed_ext (ext, riscv_supported_std_z_ext);
|
|
case RV_ISA_CLASS_ZXM:
|
|
return riscv_known_prefixed_ext (ext, riscv_supported_std_zxm_ext);
|
|
case RV_ISA_CLASS_S:
|
|
return riscv_known_prefixed_ext (ext, riscv_supported_std_s_ext);
|
|
case RV_ISA_CLASS_X:
|
|
/* Only the single x is unrecognized. */
|
|
if (strcmp (ext, "x") != 0)
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* Canonical order for single letter extensions. */
|
|
static const char riscv_ext_canonical_order[] = "eigmafdqlcbkjtpvnh";
|
|
|
|
/* Array is used to compare the orders of standard extensions quickly. */
|
|
static int riscv_ext_order[26] = {0};
|
|
|
|
/* Init the riscv_ext_order array. */
|
|
|
|
static void
|
|
riscv_init_ext_order (void)
|
|
{
|
|
static bool inited = false;
|
|
if (inited)
|
|
return;
|
|
|
|
/* The orders of all standard extensions are positive. */
|
|
int order = 1;
|
|
|
|
for (const char *ext = &riscv_ext_canonical_order[0]; *ext; ++ext)
|
|
riscv_ext_order[(*ext - 'a')] = order++;
|
|
|
|
/* Some of the prefixed keyword are not single letter, so we set
|
|
their prefixed orders in the riscv_compare_subsets directly,
|
|
not through the riscv_ext_order. */
|
|
|
|
inited = true;
|
|
}
|
|
|
|
/* Similar to the strcmp. It returns an integer less than, equal to,
|
|
or greater than zero if `subset2` is found, respectively, to be less
|
|
than, to match, or be greater than `subset1`.
|
|
|
|
The order values,
|
|
Zero: Preserved keywords.
|
|
Positive number: Standard extensions.
|
|
Negative number: Prefixed keywords. */
|
|
|
|
int
|
|
riscv_compare_subsets (const char *subset1, const char *subset2)
|
|
{
|
|
int order1 = riscv_ext_order[(*subset1 - 'a')];
|
|
int order2 = riscv_ext_order[(*subset2 - 'a')];
|
|
|
|
/* Compare the standard extension first. */
|
|
if (order1 > 0 && order2 > 0)
|
|
return order1 - order2;
|
|
|
|
/* Set the prefixed orders to negative numbers. */
|
|
enum riscv_prefix_ext_class class1 = riscv_get_prefix_class (subset1);
|
|
enum riscv_prefix_ext_class class2 = riscv_get_prefix_class (subset2);
|
|
|
|
if (class1 != RV_ISA_CLASS_UNKNOWN)
|
|
order1 = - (int) class1;
|
|
if (class2 != RV_ISA_CLASS_UNKNOWN)
|
|
order2 = - (int) class2;
|
|
|
|
if (order1 == order2)
|
|
{
|
|
/* Compare the standard addition z extensions. */
|
|
if (class1 == RV_ISA_CLASS_Z)
|
|
{
|
|
order1 = riscv_ext_order[(*++subset1 - 'a')];
|
|
order2 = riscv_ext_order[(*++subset2 - 'a')];
|
|
if (order1 != order2)
|
|
return order1 - order2;
|
|
}
|
|
return strcasecmp (++subset1, ++subset2);
|
|
}
|
|
|
|
return order2 - order1;
|
|
}
|
|
|
|
/* Find subset in the list. Return TRUE and set `current` to the subset
|
|
if it is found. Otherwise, return FALSE and set `current` to the place
|
|
where we should insert the subset. However, return FALSE with the NULL
|
|
`current` means we should insert the subset at the head of subset list,
|
|
if needed. */
|
|
|
|
bool
|
|
riscv_lookup_subset (const riscv_subset_list_t *subset_list,
|
|
const char *subset,
|
|
riscv_subset_t **current)
|
|
{
|
|
riscv_subset_t *s, *pre_s = NULL;
|
|
|
|
/* If the subset is added in order, then just add it at the tail. */
|
|
if (subset_list->tail != NULL
|
|
&& riscv_compare_subsets (subset_list->tail->name, subset) < 0)
|
|
{
|
|
*current = subset_list->tail;
|
|
return false;
|
|
}
|
|
|
|
for (s = subset_list->head;
|
|
s != NULL;
|
|
pre_s = s, s = s->next)
|
|
{
|
|
int cmp = riscv_compare_subsets (s->name, subset);
|
|
if (cmp == 0)
|
|
{
|
|
*current = s;
|
|
return true;
|
|
}
|
|
else if (cmp > 0)
|
|
break;
|
|
}
|
|
*current = pre_s;
|
|
|
|
return false;
|
|
}
|
|
|
|
/* Add the extension to the subset list. Search the
|
|
list first, and then find the right place to add. */
|
|
|
|
void
|
|
riscv_add_subset (riscv_subset_list_t *subset_list,
|
|
const char *subset,
|
|
int major,
|
|
int minor)
|
|
{
|
|
riscv_subset_t *current, *new;
|
|
|
|
if (riscv_lookup_subset (subset_list, subset, ¤t))
|
|
return;
|
|
|
|
new = xmalloc (sizeof *new);
|
|
new->name = xstrdup (subset);
|
|
new->major_version = major;
|
|
new->minor_version = minor;
|
|
new->next = NULL;
|
|
|
|
if (current != NULL)
|
|
{
|
|
new->next = current->next;
|
|
current->next = new;
|
|
}
|
|
else
|
|
{
|
|
new->next = subset_list->head;
|
|
subset_list->head = new;
|
|
}
|
|
|
|
if (new->next == NULL)
|
|
subset_list->tail = new;
|
|
}
|
|
|
|
/* Get the default versions from the riscv_supported_*ext tables. */
|
|
|
|
static void
|
|
riscv_get_default_ext_version (enum riscv_spec_class *default_isa_spec,
|
|
const char *name,
|
|
int *major_version,
|
|
int *minor_version)
|
|
{
|
|
if (name == NULL
|
|
|| default_isa_spec == NULL
|
|
|| *default_isa_spec == ISA_SPEC_CLASS_NONE)
|
|
return;
|
|
|
|
struct riscv_supported_ext *table = NULL;
|
|
enum riscv_prefix_ext_class class = riscv_get_prefix_class (name);
|
|
switch (class)
|
|
{
|
|
case RV_ISA_CLASS_ZXM: table = riscv_supported_std_zxm_ext; break;
|
|
case RV_ISA_CLASS_Z: table = riscv_supported_std_z_ext; break;
|
|
case RV_ISA_CLASS_S: table = riscv_supported_std_s_ext; break;
|
|
case RV_ISA_CLASS_X:
|
|
break;
|
|
default:
|
|
table = riscv_supported_std_ext;
|
|
}
|
|
|
|
int i = 0;
|
|
while (table != NULL && table[i].name != NULL)
|
|
{
|
|
if (strcmp (table[i].name, name) == 0
|
|
&& (table[i].isa_spec_class == ISA_SPEC_CLASS_DRAFT
|
|
|| table[i].isa_spec_class == *default_isa_spec))
|
|
{
|
|
*major_version = table[i].major_version;
|
|
*minor_version = table[i].minor_version;
|
|
return;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
/* Find the default versions for the extension before adding them to
|
|
the subset list, if their versions are RISCV_UNKNOWN_VERSION.
|
|
Afterwards, report errors if we can not find their default versions. */
|
|
|
|
static void
|
|
riscv_parse_add_subset (riscv_parse_subset_t *rps,
|
|
const char *subset,
|
|
int major,
|
|
int minor,
|
|
bool implicit)
|
|
{
|
|
int major_version = major;
|
|
int minor_version = minor;
|
|
|
|
if (major_version == RISCV_UNKNOWN_VERSION
|
|
|| minor_version == RISCV_UNKNOWN_VERSION)
|
|
riscv_get_default_ext_version (rps->isa_spec, subset,
|
|
&major_version, &minor_version);
|
|
|
|
/* We don't care the versions of the implicit extensions. */
|
|
if (!implicit
|
|
&& (major_version == RISCV_UNKNOWN_VERSION
|
|
|| minor_version == RISCV_UNKNOWN_VERSION))
|
|
{
|
|
if (subset[0] == 'x')
|
|
rps->error_handler
|
|
(_("x ISA extension `%s' must be set with the versions"),
|
|
subset);
|
|
/* Allow old ISA spec can recognize zicsr and zifencei. */
|
|
else if (strcmp (subset, "zicsr") != 0
|
|
&& strcmp (subset, "zifencei") != 0)
|
|
rps->error_handler
|
|
(_("cannot find default versions of the ISA extension `%s'"),
|
|
subset);
|
|
return;
|
|
}
|
|
|
|
riscv_add_subset (rps->subset_list, subset,
|
|
major_version, minor_version);
|
|
}
|
|
|
|
/* Release subset list. */
|
|
|
|
void
|
|
riscv_release_subset_list (riscv_subset_list_t *subset_list)
|
|
{
|
|
while (subset_list->head != NULL)
|
|
{
|
|
riscv_subset_t *next = subset_list->head->next;
|
|
free ((void *)subset_list->head->name);
|
|
free (subset_list->head);
|
|
subset_list->head = next;
|
|
}
|
|
|
|
subset_list->tail = NULL;
|
|
}
|
|
|
|
/* Parsing extension version.
|
|
|
|
Return Value:
|
|
Points to the end of version
|
|
|
|
Arguments:
|
|
`p`: Curent parsing position.
|
|
`major_version`: Parsed major version.
|
|
`minor_version`: Parsed minor version. */
|
|
|
|
static const char *
|
|
riscv_parsing_subset_version (const char *p,
|
|
int *major_version,
|
|
int *minor_version)
|
|
{
|
|
bool major_p = true;
|
|
int version = 0;
|
|
char np;
|
|
|
|
*major_version = 0;
|
|
*minor_version = 0;
|
|
for (; *p; ++p)
|
|
{
|
|
if (*p == 'p')
|
|
{
|
|
np = *(p + 1);
|
|
|
|
/* Might be beginning of `p` extension. */
|
|
if (!ISDIGIT (np))
|
|
break;
|
|
|
|
*major_version = version;
|
|
major_p = false;
|
|
version = 0;
|
|
}
|
|
else if (ISDIGIT (*p))
|
|
version = (version * 10) + (*p - '0');
|
|
else
|
|
break;
|
|
}
|
|
|
|
if (major_p)
|
|
*major_version = version;
|
|
else
|
|
*minor_version = version;
|
|
|
|
/* We can not find any version in string. */
|
|
if (*major_version == 0 && *minor_version == 0)
|
|
{
|
|
*major_version = RISCV_UNKNOWN_VERSION;
|
|
*minor_version = RISCV_UNKNOWN_VERSION;
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
/* Parsing function for standard extensions.
|
|
|
|
Return Value:
|
|
Points to the end of extensions.
|
|
|
|
Arguments:
|
|
`rps`: Hooks and status for parsing extensions.
|
|
`arch`: Full ISA string.
|
|
`p`: Curent parsing position. */
|
|
|
|
static const char *
|
|
riscv_parse_std_ext (riscv_parse_subset_t *rps,
|
|
const char *arch,
|
|
const char *p)
|
|
{
|
|
/* First letter must start with i, e or g. */
|
|
if (*p != 'e' && *p != 'i' && *p != 'g')
|
|
{
|
|
rps->error_handler
|
|
(_("%s: first ISA extension must be `e', `i' or `g'"),
|
|
arch);
|
|
return NULL;
|
|
}
|
|
|
|
while (p != NULL && *p != '\0')
|
|
{
|
|
/* Stop when we parsed the known prefix class. */
|
|
enum riscv_prefix_ext_class class = riscv_get_prefix_class (p);
|
|
if (class != RV_ISA_CLASS_UNKNOWN)
|
|
break;
|
|
|
|
if (*p == '_')
|
|
{
|
|
p++;
|
|
continue;
|
|
}
|
|
|
|
bool implicit = false;
|
|
int major = RISCV_UNKNOWN_VERSION;
|
|
int minor = RISCV_UNKNOWN_VERSION;
|
|
char subset[2] = {0, 0};
|
|
|
|
subset[0] = *p;
|
|
|
|
/* Check if the standard extension is supported. */
|
|
if (riscv_ext_order[(subset[0] - 'a')] == 0)
|
|
{
|
|
rps->error_handler
|
|
(_("%s: unknown standard ISA extension `%c'"),
|
|
arch, subset[0]);
|
|
return NULL;
|
|
}
|
|
|
|
/* Checking canonical order. */
|
|
if (rps->subset_list->tail != NULL
|
|
&& riscv_compare_subsets (rps->subset_list->tail->name, subset) > 0)
|
|
{
|
|
rps->error_handler
|
|
(_("%s: standard ISA extension `%c' is not "
|
|
"in canonical order"), arch, subset[0]);
|
|
return NULL;
|
|
}
|
|
|
|
p = riscv_parsing_subset_version (++p, &major, &minor);
|
|
/* Added g as an implicit extension. */
|
|
if (subset[0] == 'g')
|
|
{
|
|
implicit = true;
|
|
major = RISCV_UNKNOWN_VERSION;
|
|
minor = RISCV_UNKNOWN_VERSION;
|
|
}
|
|
riscv_parse_add_subset (rps, subset, major, minor, implicit);
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
/* Parsing function for prefixed extensions.
|
|
|
|
Return Value:
|
|
Points to the end of extension.
|
|
|
|
Arguments:
|
|
`rps`: Hooks and status for parsing extensions.
|
|
`arch`: Full ISA string.
|
|
`p`: Curent parsing position. */
|
|
|
|
static const char *
|
|
riscv_parse_prefixed_ext (riscv_parse_subset_t *rps,
|
|
const char *arch,
|
|
const char *p)
|
|
{
|
|
int major_version;
|
|
int minor_version;
|
|
enum riscv_prefix_ext_class class;
|
|
|
|
while (*p)
|
|
{
|
|
if (*p == '_')
|
|
{
|
|
p++;
|
|
continue;
|
|
}
|
|
|
|
class = riscv_get_prefix_class (p);
|
|
if (class == RV_ISA_CLASS_UNKNOWN)
|
|
{
|
|
rps->error_handler
|
|
(_("%s: unknown prefix class for the ISA extension `%s'"),
|
|
arch, p);
|
|
return NULL;
|
|
}
|
|
|
|
char *subset = xstrdup (p);
|
|
char *q = subset;
|
|
const char *end_of_version;
|
|
|
|
/* Extract the whole prefixed extension by '_'. */
|
|
while (*++q != '\0' && *q != '_')
|
|
;
|
|
/* Look forward to the first letter which is not <major>p<minor>. */
|
|
bool find_any_version = false;
|
|
bool find_minor_version = false;
|
|
while (1)
|
|
{
|
|
q--;
|
|
if (ISDIGIT (*q))
|
|
find_any_version = true;
|
|
else if (find_any_version
|
|
&& !find_minor_version
|
|
&& *q == 'p'
|
|
&& ISDIGIT (*(q - 1)))
|
|
find_minor_version = true;
|
|
else
|
|
break;
|
|
}
|
|
q++;
|
|
|
|
/* Check if the end of extension is 'p' or not. If yes, then
|
|
the second letter from the end cannot be number. */
|
|
if (*(q - 1) == 'p' && ISDIGIT (*(q - 2)))
|
|
{
|
|
*q = '\0';
|
|
rps->error_handler
|
|
(_("%s: invalid prefixed ISA extension `%s' ends with <number>p"),
|
|
arch, subset);
|
|
free (subset);
|
|
return NULL;
|
|
}
|
|
|
|
end_of_version =
|
|
riscv_parsing_subset_version (q, &major_version, &minor_version);
|
|
*q = '\0';
|
|
if (end_of_version == NULL)
|
|
{
|
|
free (subset);
|
|
return NULL;
|
|
}
|
|
|
|
/* Check that the extension name is well-formed. */
|
|
if (rps->check_unknown_prefixed_ext
|
|
&& !riscv_recognized_prefixed_ext (subset))
|
|
{
|
|
rps->error_handler
|
|
(_("%s: unknown prefixed ISA extension `%s'"),
|
|
arch, subset);
|
|
free (subset);
|
|
return NULL;
|
|
}
|
|
|
|
riscv_parse_add_subset (rps, subset,
|
|
major_version,
|
|
minor_version, false);
|
|
p += end_of_version - subset;
|
|
free (subset);
|
|
|
|
if (*p != '\0' && *p != '_')
|
|
{
|
|
rps->error_handler
|
|
(_("%s: prefixed ISA extension must separate with _"),
|
|
arch);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
/* Add the implicit extensions. */
|
|
|
|
static void
|
|
riscv_parse_add_implicit_subsets (riscv_parse_subset_t *rps)
|
|
{
|
|
struct riscv_implicit_subset *t = riscv_implicit_subsets;
|
|
for (; t->subset_name; t++)
|
|
{
|
|
riscv_subset_t *subset = NULL;
|
|
if (riscv_lookup_subset (rps->subset_list, t->subset_name, &subset)
|
|
&& t->check_func (t->implicit_name, subset))
|
|
riscv_parse_add_subset (rps, t->implicit_name,
|
|
RISCV_UNKNOWN_VERSION,
|
|
RISCV_UNKNOWN_VERSION, true);
|
|
}
|
|
}
|
|
|
|
/* Check extensions conflicts. */
|
|
|
|
static bool
|
|
riscv_parse_check_conflicts (riscv_parse_subset_t *rps)
|
|
{
|
|
riscv_subset_t *subset = NULL;
|
|
int xlen = *rps->xlen;
|
|
bool no_conflict = true;
|
|
|
|
if (riscv_lookup_subset (rps->subset_list, "e", &subset)
|
|
&& xlen > 32)
|
|
{
|
|
rps->error_handler
|
|
(_("rv%d does not support the `e' extension"), xlen);
|
|
no_conflict = false;
|
|
}
|
|
if (riscv_lookup_subset (rps->subset_list, "q", &subset)
|
|
&& (subset->major_version < 2 || (subset->major_version == 2
|
|
&& subset->minor_version < 2))
|
|
&& xlen < 64)
|
|
{
|
|
rps->error_handler (_("rv%d does not support the `q' extension"), xlen);
|
|
no_conflict = false;
|
|
}
|
|
if (riscv_lookup_subset (rps->subset_list, "e", &subset)
|
|
&& riscv_lookup_subset (rps->subset_list, "f", &subset))
|
|
{
|
|
rps->error_handler
|
|
(_("rv32e does not support the `f' extension"));
|
|
no_conflict = false;
|
|
}
|
|
if (riscv_lookup_subset (rps->subset_list, "zfinx", &subset)
|
|
&& riscv_lookup_subset (rps->subset_list, "f", &subset))
|
|
{
|
|
rps->error_handler
|
|
(_("`zfinx' is conflict with the `f/d/q/zfh/zfhmin' extension"));
|
|
no_conflict = false;
|
|
}
|
|
|
|
bool support_zve = false;
|
|
bool support_zvl = false;
|
|
riscv_subset_t *s = rps->subset_list->head;
|
|
for (; s != NULL; s = s->next)
|
|
{
|
|
if (!support_zve
|
|
&& strncmp (s->name, "zve", 3) == 0)
|
|
support_zve = true;
|
|
if (!support_zvl
|
|
&& strncmp (s->name, "zvl", 3) == 0)
|
|
support_zvl = true;
|
|
if (support_zve && support_zvl)
|
|
break;
|
|
}
|
|
if (support_zvl && !support_zve)
|
|
{
|
|
rps->error_handler
|
|
(_("zvl*b extensions need to enable either `v' or `zve' extension"));
|
|
no_conflict = false;
|
|
}
|
|
|
|
return no_conflict;
|
|
}
|
|
|
|
/* Set the default subset list according to the default_enable field
|
|
of riscv_supported_*ext tables. */
|
|
|
|
static void
|
|
riscv_set_default_arch (riscv_parse_subset_t *rps)
|
|
{
|
|
unsigned long enable = EXT_DEFAULT;
|
|
int i, j;
|
|
for (i = 0; riscv_all_supported_ext[i] != NULL; i++)
|
|
{
|
|
const struct riscv_supported_ext *table = riscv_all_supported_ext[i];
|
|
for (j = 0; table[j].name != NULL; j++)
|
|
{
|
|
bool implicit = false;
|
|
if (strcmp (table[j].name, "g") == 0)
|
|
implicit = true;
|
|
if (table[j].default_enable & enable)
|
|
riscv_parse_add_subset (rps, table[j].name,
|
|
RISCV_UNKNOWN_VERSION,
|
|
RISCV_UNKNOWN_VERSION, implicit);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Function for parsing ISA string.
|
|
|
|
Return Value:
|
|
Return TRUE on success.
|
|
|
|
Arguments:
|
|
`rps`: Hooks and status for parsing extensions.
|
|
`arch`: Full ISA string. */
|
|
|
|
bool
|
|
riscv_parse_subset (riscv_parse_subset_t *rps,
|
|
const char *arch)
|
|
{
|
|
const char *p;
|
|
|
|
/* Init the riscv_ext_order array to compare the order of extensions
|
|
quickly. */
|
|
riscv_init_ext_order ();
|
|
|
|
if (arch == NULL)
|
|
{
|
|
riscv_set_default_arch (rps);
|
|
riscv_parse_add_implicit_subsets (rps);
|
|
return riscv_parse_check_conflicts (rps);
|
|
}
|
|
|
|
for (p = arch; *p != '\0'; p++)
|
|
{
|
|
if (ISUPPER (*p))
|
|
{
|
|
rps->error_handler
|
|
(_("%s: ISA string cannot contain uppercase letters"),
|
|
arch);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
p = arch;
|
|
if (startswith (p, "rv32"))
|
|
{
|
|
*rps->xlen = 32;
|
|
p += 4;
|
|
}
|
|
else if (startswith (p, "rv64"))
|
|
{
|
|
*rps->xlen = 64;
|
|
p += 4;
|
|
}
|
|
else
|
|
{
|
|
/* ISA string shouldn't be NULL or empty here. For linker,
|
|
it might be empty when we failed to merge the ISA string
|
|
in the riscv_merge_attributes. For assembler, we might
|
|
give an empty string by .attribute arch, "" or -march=.
|
|
However, We have already issued the correct error message
|
|
in another side, so do not issue this error when the ISA
|
|
string is empty. */
|
|
if (strlen (arch))
|
|
rps->error_handler (
|
|
_("%s: ISA string must begin with rv32 or rv64"),
|
|
arch);
|
|
return false;
|
|
}
|
|
|
|
/* Parsing standard extension. */
|
|
p = riscv_parse_std_ext (rps, arch, p);
|
|
|
|
if (p == NULL)
|
|
return false;
|
|
|
|
/* Parse prefixed extensions. */
|
|
p = riscv_parse_prefixed_ext (rps, arch, p);
|
|
|
|
if (p == NULL)
|
|
return false;
|
|
|
|
/* Finally add implicit extensions according to the current
|
|
extensions. */
|
|
riscv_parse_add_implicit_subsets (rps);
|
|
|
|
/* Check the conflicts. */
|
|
return riscv_parse_check_conflicts (rps);
|
|
}
|
|
|
|
/* Return the number of digits for the input. */
|
|
|
|
size_t
|
|
riscv_estimate_digit (unsigned num)
|
|
{
|
|
size_t digit = 0;
|
|
if (num == 0)
|
|
return 1;
|
|
|
|
for (digit = 0; num ; num /= 10)
|
|
digit++;
|
|
|
|
return digit;
|
|
}
|
|
|
|
/* Auxiliary function to estimate string length of subset list. */
|
|
|
|
static size_t
|
|
riscv_estimate_arch_strlen1 (const riscv_subset_t *subset)
|
|
{
|
|
if (subset == NULL)
|
|
return 6; /* For rv32/rv64/rv128 and string terminator. */
|
|
|
|
return riscv_estimate_arch_strlen1 (subset->next)
|
|
+ strlen (subset->name)
|
|
+ riscv_estimate_digit (subset->major_version)
|
|
+ 1 /* For version seperator 'p'. */
|
|
+ riscv_estimate_digit (subset->minor_version)
|
|
+ 1 /* For underscore. */;
|
|
}
|
|
|
|
/* Estimate the string length of this subset list. */
|
|
|
|
static size_t
|
|
riscv_estimate_arch_strlen (const riscv_subset_list_t *subset_list)
|
|
{
|
|
return riscv_estimate_arch_strlen1 (subset_list->head);
|
|
}
|
|
|
|
/* Auxiliary function to convert subset info to string. */
|
|
|
|
static void
|
|
riscv_arch_str1 (riscv_subset_t *subset,
|
|
char *attr_str, char *buf, size_t bufsz)
|
|
{
|
|
const char *underline = "_";
|
|
riscv_subset_t *subset_t = subset;
|
|
|
|
if (subset_t == NULL)
|
|
return;
|
|
|
|
/* No underline between rvXX and i/e. */
|
|
if ((strcasecmp (subset_t->name, "i") == 0)
|
|
|| (strcasecmp (subset_t->name, "e") == 0))
|
|
underline = "";
|
|
|
|
snprintf (buf, bufsz, "%s%s%dp%d",
|
|
underline,
|
|
subset_t->name,
|
|
subset_t->major_version,
|
|
subset_t->minor_version);
|
|
|
|
strncat (attr_str, buf, bufsz);
|
|
|
|
/* Skip 'i' extension after 'e', or skip extensions which
|
|
versions are unknown. */
|
|
while (subset_t->next
|
|
&& ((strcmp (subset_t->name, "e") == 0
|
|
&& strcmp (subset_t->next->name, "i") == 0)
|
|
|| subset_t->next->major_version == RISCV_UNKNOWN_VERSION
|
|
|| subset_t->next->minor_version == RISCV_UNKNOWN_VERSION))
|
|
subset_t = subset_t->next;
|
|
|
|
riscv_arch_str1 (subset_t->next, attr_str, buf, bufsz);
|
|
}
|
|
|
|
/* Convert subset information into string with explicit versions. */
|
|
|
|
char *
|
|
riscv_arch_str (unsigned xlen, const riscv_subset_list_t *subset)
|
|
{
|
|
size_t arch_str_len = riscv_estimate_arch_strlen (subset);
|
|
char *attr_str = xmalloc (arch_str_len);
|
|
char *buf = xmalloc (arch_str_len);
|
|
|
|
snprintf (attr_str, arch_str_len, "rv%u", xlen);
|
|
|
|
riscv_arch_str1 (subset->head, attr_str, buf, arch_str_len);
|
|
free (buf);
|
|
|
|
return attr_str;
|
|
}
|
|
|
|
/* Copy the subset in the subset list. */
|
|
|
|
static struct riscv_subset_t *
|
|
riscv_copy_subset (riscv_subset_list_t *subset_list,
|
|
riscv_subset_t *subset)
|
|
{
|
|
if (subset == NULL)
|
|
return NULL;
|
|
|
|
riscv_subset_t *new = xmalloc (sizeof *new);
|
|
new->name = xstrdup (subset->name);
|
|
new->major_version = subset->major_version;
|
|
new->minor_version = subset->minor_version;
|
|
new->next = riscv_copy_subset (subset_list, subset->next);
|
|
|
|
if (subset->next == NULL)
|
|
subset_list->tail = new;
|
|
|
|
return new;
|
|
}
|
|
|
|
/* Copy the subset list. */
|
|
|
|
riscv_subset_list_t *
|
|
riscv_copy_subset_list (riscv_subset_list_t *subset_list)
|
|
{
|
|
riscv_subset_list_t *new = xmalloc (sizeof *new);
|
|
new->head = riscv_copy_subset (new, subset_list->head);
|
|
return new;
|
|
}
|
|
|
|
/* Remove the SUBSET from the subset list. */
|
|
|
|
static void
|
|
riscv_remove_subset (riscv_subset_list_t *subset_list,
|
|
const char *subset)
|
|
{
|
|
riscv_subset_t *current = subset_list->head;
|
|
riscv_subset_t *pre = NULL;
|
|
for (; current != NULL; pre = current, current = current->next)
|
|
{
|
|
if (strcmp (current->name, subset) == 0)
|
|
{
|
|
if (pre == NULL)
|
|
subset_list->head = current->next;
|
|
else
|
|
pre->next = current->next;
|
|
if (current->next == NULL)
|
|
subset_list->tail = pre;
|
|
free ((void *) current->name);
|
|
free (current);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Add/Remove an extension to/from the subset list. This is used for
|
|
the .option rvc or norvc, and .option arch directives. */
|
|
|
|
bool
|
|
riscv_update_subset (riscv_parse_subset_t *rps,
|
|
const char *str)
|
|
{
|
|
const char *p = str;
|
|
|
|
do
|
|
{
|
|
int major_version = RISCV_UNKNOWN_VERSION;
|
|
int minor_version = RISCV_UNKNOWN_VERSION;
|
|
|
|
bool removed = false;
|
|
switch (*p)
|
|
{
|
|
case '+': removed = false; break;
|
|
case '-': removed = true; break;
|
|
default:
|
|
riscv_release_subset_list (rps->subset_list);
|
|
return riscv_parse_subset (rps, p);
|
|
}
|
|
++p;
|
|
|
|
char *subset = xstrdup (p);
|
|
char *q = subset;
|
|
const char *end_of_version;
|
|
/* Extract the whole prefixed extension by ','. */
|
|
while (*q != '\0' && *q != ',')
|
|
q++;
|
|
|
|
/* Look forward to the first letter which is not <major>p<minor>. */
|
|
bool find_any_version = false;
|
|
bool find_minor_version = false;
|
|
size_t len = q - subset;
|
|
size_t i;
|
|
for (i = len; i > 0; i--)
|
|
{
|
|
q--;
|
|
if (ISDIGIT (*q))
|
|
find_any_version = true;
|
|
else if (find_any_version
|
|
&& !find_minor_version
|
|
&& *q == 'p'
|
|
&& ISDIGIT (*(q - 1)))
|
|
find_minor_version = true;
|
|
else
|
|
break;
|
|
}
|
|
if (len > 0)
|
|
q++;
|
|
|
|
/* Check if the end of extension is 'p' or not. If yes, then
|
|
the second letter from the end cannot be number. */
|
|
if (len > 1 && *(q - 1) == 'p' && ISDIGIT (*(q - 2)))
|
|
{
|
|
*q = '\0';
|
|
rps->error_handler
|
|
(_("invalid ISA extension ends with <number>p "
|
|
"in .option arch `%s'"), str);
|
|
free (subset);
|
|
return false;
|
|
}
|
|
|
|
end_of_version =
|
|
riscv_parsing_subset_version (q, &major_version, &minor_version);
|
|
*q = '\0';
|
|
if (end_of_version == NULL)
|
|
{
|
|
free (subset);
|
|
return false;
|
|
}
|
|
|
|
if (strlen (subset) == 0
|
|
|| (strlen (subset) == 1
|
|
&& riscv_ext_order[(*subset - 'a')] == 0)
|
|
|| (strlen (subset) > 1
|
|
&& rps->check_unknown_prefixed_ext
|
|
&& !riscv_recognized_prefixed_ext (subset)))
|
|
{
|
|
rps->error_handler
|
|
(_("unknown ISA extension `%s' in .option arch `%s'"),
|
|
subset, str);
|
|
free (subset);
|
|
return false;
|
|
}
|
|
|
|
if (strcmp (subset, "i") == 0
|
|
|| strcmp (subset, "e") == 0
|
|
|| strcmp (subset, "g") == 0)
|
|
{
|
|
rps->error_handler
|
|
(_("cannot + or - base extension `%s' in .option "
|
|
"arch `%s'"), subset, str);
|
|
free (subset);
|
|
return false;
|
|
}
|
|
|
|
if (removed)
|
|
riscv_remove_subset (rps->subset_list, subset);
|
|
else
|
|
riscv_parse_add_subset (rps, subset, major_version, minor_version, true);
|
|
p += end_of_version - subset;
|
|
free (subset);
|
|
}
|
|
while (*p++ == ',');
|
|
|
|
riscv_parse_add_implicit_subsets (rps);
|
|
return riscv_parse_check_conflicts (rps);
|
|
}
|
|
|
|
/* Check if the FEATURE subset is supported or not in the subset list.
|
|
Return true if it is supported; Otherwise, return false. */
|
|
|
|
bool
|
|
riscv_subset_supports (riscv_parse_subset_t *rps,
|
|
const char *feature)
|
|
{
|
|
struct riscv_subset_t *subset;
|
|
return riscv_lookup_subset (rps->subset_list, feature, &subset);
|
|
}
|
|
|
|
/* Each instuction is belonged to an instruction class INSN_CLASS_*.
|
|
Call riscv_subset_supports to make sure if the instuction is valid. */
|
|
|
|
bool
|
|
riscv_multi_subset_supports (riscv_parse_subset_t *rps,
|
|
enum riscv_insn_class insn_class)
|
|
{
|
|
switch (insn_class)
|
|
{
|
|
case INSN_CLASS_I:
|
|
return riscv_subset_supports (rps, "i");
|
|
case INSN_CLASS_ZICBOM:
|
|
return riscv_subset_supports (rps, "zicbom");
|
|
case INSN_CLASS_ZICBOP:
|
|
return riscv_subset_supports (rps, "zicbop");
|
|
case INSN_CLASS_ZICBOZ:
|
|
return riscv_subset_supports (rps, "zicboz");
|
|
case INSN_CLASS_ZICSR:
|
|
return riscv_subset_supports (rps, "zicsr");
|
|
case INSN_CLASS_ZIFENCEI:
|
|
return riscv_subset_supports (rps, "zifencei");
|
|
case INSN_CLASS_ZIHINTPAUSE:
|
|
return riscv_subset_supports (rps, "zihintpause");
|
|
case INSN_CLASS_M:
|
|
return riscv_subset_supports (rps, "m");
|
|
case INSN_CLASS_A:
|
|
return riscv_subset_supports (rps, "a");
|
|
case INSN_CLASS_F:
|
|
return riscv_subset_supports (rps, "f");
|
|
case INSN_CLASS_D:
|
|
return riscv_subset_supports (rps, "d");
|
|
case INSN_CLASS_Q:
|
|
return riscv_subset_supports (rps, "q");
|
|
case INSN_CLASS_C:
|
|
return riscv_subset_supports (rps, "c");
|
|
case INSN_CLASS_F_AND_C:
|
|
return (riscv_subset_supports (rps, "f")
|
|
&& riscv_subset_supports (rps, "c"));
|
|
case INSN_CLASS_D_AND_C:
|
|
return (riscv_subset_supports (rps, "d")
|
|
&& riscv_subset_supports (rps, "c"));
|
|
case INSN_CLASS_F_OR_ZFINX:
|
|
return (riscv_subset_supports (rps, "f")
|
|
|| riscv_subset_supports (rps, "zfinx"));
|
|
case INSN_CLASS_D_OR_ZDINX:
|
|
return (riscv_subset_supports (rps, "d")
|
|
|| riscv_subset_supports (rps, "zdinx"));
|
|
case INSN_CLASS_Q_OR_ZQINX:
|
|
return (riscv_subset_supports (rps, "q")
|
|
|| riscv_subset_supports (rps, "zqinx"));
|
|
case INSN_CLASS_ZFH_OR_ZHINX:
|
|
return (riscv_subset_supports (rps, "zfh")
|
|
|| riscv_subset_supports (rps, "zhinx"));
|
|
case INSN_CLASS_ZFHMIN:
|
|
return riscv_subset_supports (rps, "zfhmin");
|
|
case INSN_CLASS_ZFHMIN_OR_ZHINXMIN:
|
|
return (riscv_subset_supports (rps, "zfhmin")
|
|
|| riscv_subset_supports (rps, "zhinxmin"));
|
|
case INSN_CLASS_ZFHMIN_AND_D:
|
|
return ((riscv_subset_supports (rps, "zfhmin")
|
|
&& riscv_subset_supports (rps, "d"))
|
|
|| (riscv_subset_supports (rps, "zhinxmin")
|
|
&& riscv_subset_supports (rps, "zdinx")));
|
|
case INSN_CLASS_ZFHMIN_AND_Q:
|
|
return ((riscv_subset_supports (rps, "zfhmin")
|
|
&& riscv_subset_supports (rps, "q"))
|
|
|| (riscv_subset_supports (rps, "zhinxmin")
|
|
&& riscv_subset_supports (rps, "zqinx")));
|
|
case INSN_CLASS_ZBA:
|
|
return riscv_subset_supports (rps, "zba");
|
|
case INSN_CLASS_ZBB:
|
|
return riscv_subset_supports (rps, "zbb");
|
|
case INSN_CLASS_ZBC:
|
|
return riscv_subset_supports (rps, "zbc");
|
|
case INSN_CLASS_ZBS:
|
|
return riscv_subset_supports (rps, "zbs");
|
|
case INSN_CLASS_ZBKB:
|
|
return riscv_subset_supports (rps, "zbkb");
|
|
case INSN_CLASS_ZBKC:
|
|
return riscv_subset_supports (rps, "zbkc");
|
|
case INSN_CLASS_ZBKX:
|
|
return riscv_subset_supports (rps, "zbkx");
|
|
case INSN_CLASS_ZBB_OR_ZBKB:
|
|
return (riscv_subset_supports (rps, "zbb")
|
|
|| riscv_subset_supports (rps, "zbkb"));
|
|
case INSN_CLASS_ZBC_OR_ZBKC:
|
|
return (riscv_subset_supports (rps, "zbc")
|
|
|| riscv_subset_supports (rps, "zbkc"));
|
|
case INSN_CLASS_ZKND:
|
|
return riscv_subset_supports (rps, "zknd");
|
|
case INSN_CLASS_ZKNE:
|
|
return riscv_subset_supports (rps, "zkne");
|
|
case INSN_CLASS_ZKNH:
|
|
return riscv_subset_supports (rps, "zknh");
|
|
case INSN_CLASS_ZKND_OR_ZKNE:
|
|
return (riscv_subset_supports (rps, "zknd")
|
|
|| riscv_subset_supports (rps, "zkne"));
|
|
case INSN_CLASS_ZKSED:
|
|
return riscv_subset_supports (rps, "zksed");
|
|
case INSN_CLASS_ZKSH:
|
|
return riscv_subset_supports (rps, "zksh");
|
|
case INSN_CLASS_V:
|
|
return (riscv_subset_supports (rps, "v")
|
|
|| riscv_subset_supports (rps, "zve64x")
|
|
|| riscv_subset_supports (rps, "zve32x"));
|
|
case INSN_CLASS_ZVEF:
|
|
return (riscv_subset_supports (rps, "v")
|
|
|| riscv_subset_supports (rps, "zve64d")
|
|
|| riscv_subset_supports (rps, "zve64f")
|
|
|| riscv_subset_supports (rps, "zve32f"));
|
|
case INSN_CLASS_SVINVAL:
|
|
return riscv_subset_supports (rps, "svinval");
|
|
case INSN_CLASS_H:
|
|
return riscv_subset_supports (rps, "h");
|
|
default:
|
|
rps->error_handler
|
|
(_("internal: unreachable INSN_CLASS_*"));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* Each instuction is belonged to an instruction class INSN_CLASS_*.
|
|
Call riscv_subset_supports_ext to determine the missing extension. */
|
|
|
|
const char *
|
|
riscv_multi_subset_supports_ext (riscv_parse_subset_t *rps,
|
|
enum riscv_insn_class insn_class)
|
|
{
|
|
switch (insn_class)
|
|
{
|
|
case INSN_CLASS_I:
|
|
return "i";
|
|
case INSN_CLASS_ZICSR:
|
|
return "zicsr";
|
|
case INSN_CLASS_ZIFENCEI:
|
|
return "zifencei";
|
|
case INSN_CLASS_ZIHINTPAUSE:
|
|
return "zihintpause";
|
|
case INSN_CLASS_M:
|
|
return "m";
|
|
case INSN_CLASS_A:
|
|
return "a";
|
|
case INSN_CLASS_F:
|
|
return "f";
|
|
case INSN_CLASS_D:
|
|
return "d";
|
|
case INSN_CLASS_Q:
|
|
return "q";
|
|
case INSN_CLASS_C:
|
|
return "c";
|
|
case INSN_CLASS_F_AND_C:
|
|
if (!riscv_subset_supports (rps, "f")
|
|
&& !riscv_subset_supports (rps, "c"))
|
|
return _("f' and `c");
|
|
else if (!riscv_subset_supports (rps, "f"))
|
|
return "f";
|
|
else
|
|
return "c";
|
|
case INSN_CLASS_D_AND_C:
|
|
if (!riscv_subset_supports (rps, "d")
|
|
&& !riscv_subset_supports (rps, "c"))
|
|
return _("d' and `c");
|
|
else if (!riscv_subset_supports (rps, "d"))
|
|
return "d";
|
|
else
|
|
return "c";
|
|
case INSN_CLASS_F_OR_ZFINX:
|
|
/* i18n: Formatted like "extension `f' or `zfinx' required". */
|
|
return _("f' or `zfinx");
|
|
case INSN_CLASS_D_OR_ZDINX:
|
|
return _("d' or `zdinx");
|
|
case INSN_CLASS_Q_OR_ZQINX:
|
|
return _("q' or `zqinx");
|
|
case INSN_CLASS_ZFH_OR_ZHINX:
|
|
return _("zfh' or `zhinx");
|
|
case INSN_CLASS_ZFHMIN:
|
|
return "zfhmin";
|
|
case INSN_CLASS_ZFHMIN_OR_ZHINXMIN:
|
|
return _("zfhmin' or `zhinxmin");
|
|
case INSN_CLASS_ZFHMIN_AND_D:
|
|
if (riscv_subset_supports (rps, "zfhmin"))
|
|
return "d";
|
|
else if (riscv_subset_supports (rps, "d"))
|
|
return "zfhmin";
|
|
else if (riscv_subset_supports (rps, "zhinxmin"))
|
|
return "zdinx";
|
|
else if (riscv_subset_supports (rps, "zdinx"))
|
|
return "zhinxmin";
|
|
else
|
|
return _("zfhmin' and `d', or `zhinxmin' and `zdinx");
|
|
case INSN_CLASS_ZFHMIN_AND_Q:
|
|
if (riscv_subset_supports (rps, "zfhmin"))
|
|
return "q";
|
|
else if (riscv_subset_supports (rps, "q"))
|
|
return "zfhmin";
|
|
else if (riscv_subset_supports (rps, "zhinxmin"))
|
|
return "zqinx";
|
|
else if (riscv_subset_supports (rps, "zqinx"))
|
|
return "zhinxmin";
|
|
else
|
|
return _("zfhmin' and `q', or `zhinxmin' and `zqinx");
|
|
case INSN_CLASS_ZBA:
|
|
return "zba";
|
|
case INSN_CLASS_ZBB:
|
|
return "zbb";
|
|
case INSN_CLASS_ZBC:
|
|
return "zbc";
|
|
case INSN_CLASS_ZBS:
|
|
return "zbs";
|
|
case INSN_CLASS_ZBKB:
|
|
return "zbkb";
|
|
case INSN_CLASS_ZBKC:
|
|
return "zbkc";
|
|
case INSN_CLASS_ZBKX:
|
|
return "zbkx";
|
|
case INSN_CLASS_ZBB_OR_ZBKB:
|
|
return _("zbb' or `zbkb");
|
|
case INSN_CLASS_ZBC_OR_ZBKC:
|
|
return _("zbc' or `zbkc");
|
|
case INSN_CLASS_ZKND:
|
|
return "zknd";
|
|
case INSN_CLASS_ZKNE:
|
|
return "zkne";
|
|
case INSN_CLASS_ZKNH:
|
|
return "zknh";
|
|
case INSN_CLASS_ZKND_OR_ZKNE:
|
|
return _("zknd' or `zkne");
|
|
case INSN_CLASS_ZKSED:
|
|
return "zksed";
|
|
case INSN_CLASS_ZKSH:
|
|
return "zksh";
|
|
case INSN_CLASS_V:
|
|
return _("v' or `zve64x' or `zve32x");
|
|
case INSN_CLASS_ZVEF:
|
|
return _("v' or `zve64d' or `zve64f' or `zve32f");
|
|
case INSN_CLASS_SVINVAL:
|
|
return "svinval";
|
|
case INSN_CLASS_H:
|
|
return _("h");
|
|
default:
|
|
rps->error_handler
|
|
(_("internal: unreachable INSN_CLASS_*"));
|
|
return NULL;
|
|
}
|
|
}
|