| /* RISC-V-specific support for ELF. |
| Copyright (C) 2011-2021 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}, |
| {"d", "f", check_implicit_always}, |
| {"f", "zicsr", 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 }, |
| {"l", ISA_SPEC_CLASS_NONE, RISCV_UNKNOWN_VERSION, RISCV_UNKNOWN_VERSION, 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 }, |
| {"b", ISA_SPEC_CLASS_NONE, RISCV_UNKNOWN_VERSION, RISCV_UNKNOWN_VERSION, 0 }, |
| {"j", ISA_SPEC_CLASS_NONE, RISCV_UNKNOWN_VERSION, RISCV_UNKNOWN_VERSION, 0 }, |
| {"t", ISA_SPEC_CLASS_NONE, RISCV_UNKNOWN_VERSION, RISCV_UNKNOWN_VERSION, 0 }, |
| {"p", ISA_SPEC_CLASS_NONE, RISCV_UNKNOWN_VERSION, RISCV_UNKNOWN_VERSION, 0 }, |
| {"v", ISA_SPEC_CLASS_NONE, RISCV_UNKNOWN_VERSION, RISCV_UNKNOWN_VERSION, 0 }, |
| {"n", ISA_SPEC_CLASS_NONE, RISCV_UNKNOWN_VERSION, RISCV_UNKNOWN_VERSION, 0 }, |
| {NULL, 0, 0, 0, 0} |
| }; |
| |
| static struct riscv_supported_ext riscv_supported_std_z_ext[] = |
| { |
| {"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 }, |
| {"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 }, |
| {NULL, 0, 0, 0, 0} |
| }; |
| |
| static struct riscv_supported_ext riscv_supported_std_s_ext[] = |
| { |
| {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; |
| } |
| |
| /* 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; |
| |
| int i = 0; |
| while (riscv_supported_std_ext[i].name != NULL) |
| { |
| const char *ext = riscv_supported_std_ext[i].name; |
| riscv_ext_order[(*ext - 'a')] = order++; |
| i++; |
| while (riscv_supported_std_ext[i].name |
| && strcmp (ext, riscv_supported_std_ext[i].name) == 0) |
| i++; |
| } |
| |
| /* 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 == 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); |
| else |
| 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; |
| } |
| 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 the different classes of extensions in the specified order. */ |
| while (*p != '\0') |
| { |
| 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; |
| } |