2505 lines
72 KiB
C
2505 lines
72 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 */
|
||
|
3, /* 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 */
|
||
|
2, /* 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 */
|
||
|
4, /* 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 */
|
||
|
2, /* 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 */
|
||
|
4, /* 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 */
|
||
|
2, /* 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 */
|
||
|
4, /* 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 */
|
||
|
2, /* 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 */
|
||
|
4, /* 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 */
|
||
|
2, /* 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 */
|
||
|
4, /* 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 */
|
||
|
2, /* 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 */
|
||
|
2, /* 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 */
|
||
|
4, /* 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 */
|
||
|
4, /* 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 */
|
||
|
2, /* 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 */
|
||
|
2, /* 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 */
|
||
|
2, /* 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 */
|
||
|
2, /* 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 */
|
||
|
2, /* 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 */
|
||
|
2, /* 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 */
|
||
|
2, /* 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 */
|
||
|
2, /* 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 */
|
||
|
2, /* 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 */
|
||
|
2, /* 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 */
|
||
|
2, /* 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 */
|
||
|
2, /* 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 */
|
||
|
3, /* 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 */
|
||
|
0, /* 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 */
|
||
|
1, /* 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 */
|
||
|
2, /* 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 */
|
||
|
4, /* 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 */
|
||
|
0, /* 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 */
|
||
|
1, /* 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 */
|
||
|
2, /* 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 */
|
||
|
4, /* 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 */
|
||
|
4, /* 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 */
|
||
|
4, /* 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 */
|
||
|
3, /* 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 */
|
||
|
1, /* 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 */
|
||
|
1, /* 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 */
|
||
|
1, /* 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 */
|
||
|
2, /* 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 */
|
||
|
2, /* 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 */
|
||
|
2, /* 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 */
|
||
|
2, /* 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 */
|
||
|
3, /* 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 */
|
||
|
0, /* 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 */
|
||
|
0, /* 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 */
|
||
|
0, /* 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 */
|
||
|
1, /* 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 */
|
||
|
2, /* 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 */
|
||
|
2, /* 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 */
|
||
|
2, /* 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},
|
||
|
{"f", "zicsr", check_implicit_always},
|
||
|
{"zqinx", "zdinx", check_implicit_always},
|
||
|
{"zdinx", "zfinx", 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 },
|
||
|
{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, 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 },
|
||
|
{"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[] =
|
||
|
{
|
||
|
{"svinval", ISA_SPEC_CLASS_DRAFT, 1, 0, 0 },
|
||
|
{NULL, 0, 0, 0, 0}
|
||
|
};
|
||
|
|
||
|
static struct riscv_supported_ext riscv_supported_std_h_ext[] =
|
||
|
{
|
||
|
{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_h_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_H,
|
||
|
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_H, "h"},
|
||
|
{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_H:
|
||
|
return riscv_known_prefixed_ext (ext, riscv_supported_std_h_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[] = "eigmafdqlcbjktpvn";
|
||
|
|
||
|
/* 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_H: table = riscv_supported_std_h_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;
|
||
|
const char *last_name;
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/* Check that the extension isn't duplicate. */
|
||
|
last_name = rps->subset_list->tail->name;
|
||
|
if (!strcasecmp (last_name, subset))
|
||
|
{
|
||
|
rps->error_handler
|
||
|
(_("%s: duplicate prefixed ISA extension `%s'"),
|
||
|
arch, subset);
|
||
|
free (subset);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/* Check that the extension is in expected order. */
|
||
|
if (riscv_compare_subsets (last_name, subset) > 0)
|
||
|
{
|
||
|
rps->error_handler
|
||
|
(_("%s: prefixed ISA extension `%s' is not in expected "
|
||
|
"order. It must come before `%s'"),
|
||
|
arch, subset, last_name);
|
||
|
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)
|
||
|
&& 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' 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_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");
|
||
|
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:
|
||
|
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_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";
|
||
|
default:
|
||
|
rps->error_handler
|
||
|
(_("internal: unreachable INSN_CLASS_*"));
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|