| /* RISC-V-specific support for ELF. | 
 |    Copyright (C) 2011-2022 Free Software Foundation, Inc. | 
 |  | 
 |    Contributed by Andrew Waterman (andrew@sifive.com). | 
 |    Based on TILE-Gx and MIPS targets. | 
 |  | 
 |    This file is part of BFD, the Binary File Descriptor library. | 
 |  | 
 |    This program is free software; you can redistribute it and/or modify | 
 |    it under the terms of the GNU General Public License as published by | 
 |    the Free Software Foundation; either version 3 of the License, or | 
 |    (at your option) any later version. | 
 |  | 
 |    This program is distributed in the hope that it will be useful, | 
 |    but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 |    GNU General Public License for more details. | 
 |  | 
 |    You should have received a copy of the GNU General Public License | 
 |    along with this program; see the file COPYING3. If not, | 
 |    see <http://www.gnu.org/licenses/>.  */ | 
 |  | 
 | #include "sysdep.h" | 
 | #include "bfd.h" | 
 | #include "libbfd.h" | 
 | #include "elf-bfd.h" | 
 | #include "elf/riscv.h" | 
 | #include "opcode/riscv.h" | 
 | #include "libiberty.h" | 
 | #include "elfxx-riscv.h" | 
 | #include "safe-ctype.h" | 
 |  | 
 | #define MINUS_ONE ((bfd_vma)0 - 1) | 
 |  | 
 | /* Special handler for ADD/SUB relocations that allows them to be filled out | 
 |    both in the pre-linked and post-linked file.  This is necessary to make | 
 |    pre-linked debug info work, as due to linker relaxations we need to emit | 
 |    relocations for the debug info.  */ | 
 | static bfd_reloc_status_type riscv_elf_add_sub_reloc | 
 |   (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); | 
 |  | 
 | /* The relocation table used for SHT_RELA sections.  */ | 
 |  | 
 | static reloc_howto_type howto_table[] = | 
 | { | 
 |   /* No relocation.  */ | 
 |   HOWTO (R_RISCV_NONE,			/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 0,				/* size */ | 
 | 	 0,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_NONE",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 0,				/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* 32 bit relocation.  */ | 
 |   HOWTO (R_RISCV_32,			/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 4,				/* size */ | 
 | 	 32,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_32",			/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 0xffffffff,			/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* 64 bit relocation.  */ | 
 |   HOWTO (R_RISCV_64,			/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 8,				/* size */ | 
 | 	 64,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_64",			/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 MINUS_ONE,			/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* Relocation against a local symbol in a shared object.  */ | 
 |   HOWTO (R_RISCV_RELATIVE,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 4,				/* size */ | 
 | 	 32,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_RELATIVE",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 0xffffffff,			/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   HOWTO (R_RISCV_COPY,			/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 0,				/* this one is variable size */ | 
 | 	 0,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_bitfield,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_COPY",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 0,				/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   HOWTO (R_RISCV_JUMP_SLOT,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 8,				/* size */ | 
 | 	 64,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_bitfield,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_JUMP_SLOT",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 0,				/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* Dynamic TLS relocations.  */ | 
 |   HOWTO (R_RISCV_TLS_DTPMOD32,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 4,				/* size */ | 
 | 	 32,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_TLS_DTPMOD32",	/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 0xffffffff,			/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   HOWTO (R_RISCV_TLS_DTPMOD64,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 8,				/* size */ | 
 | 	 64,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_TLS_DTPMOD64",	/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 MINUS_ONE,			/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   HOWTO (R_RISCV_TLS_DTPREL32,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 4,				/* size */ | 
 | 	 32,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_TLS_DTPREL32",	/* name */ | 
 | 	 true,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 0xffffffff,			/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   HOWTO (R_RISCV_TLS_DTPREL64,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 8,				/* size */ | 
 | 	 64,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_TLS_DTPREL64",	/* name */ | 
 | 	 true,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 MINUS_ONE,			/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   HOWTO (R_RISCV_TLS_TPREL32,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 4,				/* size */ | 
 | 	 32,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_TLS_TPREL32",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 0xffffffff,			/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   HOWTO (R_RISCV_TLS_TPREL64,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 8,				/* size */ | 
 | 	 64,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_TLS_TPREL64",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 MINUS_ONE,			/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* Reserved for future relocs that the dynamic linker must understand.  */ | 
 |   EMPTY_HOWTO (12), | 
 |   EMPTY_HOWTO (13), | 
 |   EMPTY_HOWTO (14), | 
 |   EMPTY_HOWTO (15), | 
 |  | 
 |   /* 12-bit PC-relative branch offset.  */ | 
 |   HOWTO (R_RISCV_BRANCH,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 4,				/* size */ | 
 | 	 32,				/* bitsize */ | 
 | 	 true,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_signed,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_BRANCH",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 ENCODE_BTYPE_IMM (-1U),	/* dst_mask */ | 
 | 	 true),				/* pcrel_offset */ | 
 |  | 
 |   /* 20-bit PC-relative jump offset.  */ | 
 |   HOWTO (R_RISCV_JAL,			/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 4,				/* size */ | 
 | 	 32,				/* bitsize */ | 
 | 	 true,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_JAL",			/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 ENCODE_JTYPE_IMM (-1U),	/* dst_mask */ | 
 | 	 true),				/* pcrel_offset */ | 
 |  | 
 |   /* 32-bit PC-relative function call (AUIPC/JALR).  */ | 
 |   HOWTO (R_RISCV_CALL,			/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 8,				/* size */ | 
 | 	 64,				/* bitsize */ | 
 | 	 true,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_CALL",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 ENCODE_UTYPE_IMM (-1U) | ((bfd_vma) ENCODE_ITYPE_IMM (-1U) << 32), | 
 | 					/* dst_mask */ | 
 | 	 true),				/* pcrel_offset */ | 
 |  | 
 |   /* Like R_RISCV_CALL, but not locally binding.  */ | 
 |   HOWTO (R_RISCV_CALL_PLT,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 8,				/* size */ | 
 | 	 64,				/* bitsize */ | 
 | 	 true,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_CALL_PLT",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 ENCODE_UTYPE_IMM (-1U) | ((bfd_vma) ENCODE_ITYPE_IMM (-1U) << 32), | 
 | 					/* dst_mask */ | 
 | 	 true),				/* pcrel_offset */ | 
 |  | 
 |   /* High 20 bits of 32-bit PC-relative GOT access.  */ | 
 |   HOWTO (R_RISCV_GOT_HI20,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 4,				/* size */ | 
 | 	 32,				/* bitsize */ | 
 | 	 true,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_GOT_HI20",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 ENCODE_UTYPE_IMM (-1U),	/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* High 20 bits of 32-bit PC-relative TLS IE GOT access.  */ | 
 |   HOWTO (R_RISCV_TLS_GOT_HI20,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 4,				/* size */ | 
 | 	 32,				/* bitsize */ | 
 | 	 true,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_TLS_GOT_HI20",	/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 ENCODE_UTYPE_IMM (-1U),	/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* High 20 bits of 32-bit PC-relative TLS GD GOT reference.  */ | 
 |   HOWTO (R_RISCV_TLS_GD_HI20,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 4,				/* size */ | 
 | 	 32,				/* bitsize */ | 
 | 	 true,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_TLS_GD_HI20",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 ENCODE_UTYPE_IMM (-1U),	/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* High 20 bits of 32-bit PC-relative reference.  */ | 
 |   HOWTO (R_RISCV_PCREL_HI20,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 4,				/* size */ | 
 | 	 32,				/* bitsize */ | 
 | 	 true,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_PCREL_HI20",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 ENCODE_UTYPE_IMM (-1U),	/* dst_mask */ | 
 | 	 true),				/* pcrel_offset */ | 
 |  | 
 |   /* Low 12 bits of a 32-bit PC-relative load or add.  */ | 
 |   HOWTO (R_RISCV_PCREL_LO12_I,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 4,				/* size */ | 
 | 	 32,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_PCREL_LO12_I",	/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 ENCODE_ITYPE_IMM (-1U),	/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* Low 12 bits of a 32-bit PC-relative store.  */ | 
 |   HOWTO (R_RISCV_PCREL_LO12_S,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 4,				/* size */ | 
 | 	 32,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_PCREL_LO12_S",	/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 ENCODE_STYPE_IMM (-1U),	/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* High 20 bits of 32-bit absolute address.  */ | 
 |   HOWTO (R_RISCV_HI20,			/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 4,				/* size */ | 
 | 	 32,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_HI20",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 ENCODE_UTYPE_IMM (-1U),	/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* High 12 bits of 32-bit load or add.  */ | 
 |   HOWTO (R_RISCV_LO12_I,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 4,				/* size */ | 
 | 	 32,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_LO12_I",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 ENCODE_ITYPE_IMM (-1U),	/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* High 12 bits of 32-bit store.  */ | 
 |   HOWTO (R_RISCV_LO12_S,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 4,				/* size */ | 
 | 	 32,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_LO12_S",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 ENCODE_STYPE_IMM (-1U),	/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* High 20 bits of TLS LE thread pointer offset.  */ | 
 |   HOWTO (R_RISCV_TPREL_HI20,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 4,				/* size */ | 
 | 	 32,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_signed,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_TPREL_HI20",		/* name */ | 
 | 	 true,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 ENCODE_UTYPE_IMM (-1U),	/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* Low 12 bits of TLS LE thread pointer offset for loads and adds.  */ | 
 |   HOWTO (R_RISCV_TPREL_LO12_I,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 4,				/* size */ | 
 | 	 32,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_signed,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_TPREL_LO12_I",	/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 ENCODE_ITYPE_IMM (-1U),	/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* Low 12 bits of TLS LE thread pointer offset for stores.  */ | 
 |   HOWTO (R_RISCV_TPREL_LO12_S,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 4,				/* size */ | 
 | 	 32,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_signed,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_TPREL_LO12_S",	/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 ENCODE_STYPE_IMM (-1U),	/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* TLS LE thread pointer usage.  May be relaxed.  */ | 
 |   HOWTO (R_RISCV_TPREL_ADD,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 0,				/* size */ | 
 | 	 0,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_TPREL_ADD",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 0,				/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* 8-bit in-place addition, for local label subtraction.  */ | 
 |   HOWTO (R_RISCV_ADD8,			/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 1,				/* size */ | 
 | 	 8,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 riscv_elf_add_sub_reloc,	/* special_function */ | 
 | 	 "R_RISCV_ADD8",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 0xff,				/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* 16-bit in-place addition, for local label subtraction.  */ | 
 |   HOWTO (R_RISCV_ADD16,			/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 2,				/* size */ | 
 | 	 16,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 riscv_elf_add_sub_reloc,	/* special_function */ | 
 | 	 "R_RISCV_ADD16",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 0xffff,			/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* 32-bit in-place addition, for local label subtraction.  */ | 
 |   HOWTO (R_RISCV_ADD32,			/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 4,				/* size */ | 
 | 	 32,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 riscv_elf_add_sub_reloc,	/* special_function */ | 
 | 	 "R_RISCV_ADD32",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 0xffffffff,			/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* 64-bit in-place addition, for local label subtraction.  */ | 
 |   HOWTO (R_RISCV_ADD64,			/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 8,				/* size */ | 
 | 	 64,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 riscv_elf_add_sub_reloc,	/* special_function */ | 
 | 	 "R_RISCV_ADD64",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 MINUS_ONE,			/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* 8-bit in-place addition, for local label subtraction.  */ | 
 |   HOWTO (R_RISCV_SUB8,			/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 1,				/* size */ | 
 | 	 8,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 riscv_elf_add_sub_reloc,	/* special_function */ | 
 | 	 "R_RISCV_SUB8",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 0xff,				/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* 16-bit in-place addition, for local label subtraction.  */ | 
 |   HOWTO (R_RISCV_SUB16,			/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 2,				/* size */ | 
 | 	 16,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 riscv_elf_add_sub_reloc,	/* special_function */ | 
 | 	 "R_RISCV_SUB16",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 0xffff,			/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* 32-bit in-place addition, for local label subtraction.  */ | 
 |   HOWTO (R_RISCV_SUB32,			/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 4,				/* size */ | 
 | 	 32,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 riscv_elf_add_sub_reloc,	/* special_function */ | 
 | 	 "R_RISCV_SUB32",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 0xffffffff,			/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* 64-bit in-place addition, for local label subtraction.  */ | 
 |   HOWTO (R_RISCV_SUB64,			/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 8,				/* size */ | 
 | 	 64,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 riscv_elf_add_sub_reloc,	/* special_function */ | 
 | 	 "R_RISCV_SUB64",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 MINUS_ONE,			/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* GNU extension to record C++ vtable hierarchy */ | 
 |   HOWTO (R_RISCV_GNU_VTINHERIT,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 8,				/* size */ | 
 | 	 0,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 NULL,				/* special_function */ | 
 | 	 "R_RISCV_GNU_VTINHERIT",	/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 0,				/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* GNU extension to record C++ vtable member usage */ | 
 |   HOWTO (R_RISCV_GNU_VTENTRY,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 8,				/* size */ | 
 | 	 0,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 _bfd_elf_rel_vtable_reloc_fn,	/* special_function */ | 
 | 	 "R_RISCV_GNU_VTENTRY",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 0,				/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* Indicates an alignment statement.  The addend field encodes how many | 
 |      bytes of NOPs follow the statement.  The desired alignment is the | 
 |      addend rounded up to the next power of two.  */ | 
 |   HOWTO (R_RISCV_ALIGN,			/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 0,				/* size */ | 
 | 	 0,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_ALIGN",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 0,				/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* 8-bit PC-relative branch offset.  */ | 
 |   HOWTO (R_RISCV_RVC_BRANCH,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 2,				/* size */ | 
 | 	 16,				/* bitsize */ | 
 | 	 true,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_signed,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_RVC_BRANCH",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 ENCODE_CBTYPE_IMM (-1U),	/* dst_mask */ | 
 | 	 true),				/* pcrel_offset */ | 
 |  | 
 |   /* 11-bit PC-relative jump offset.  */ | 
 |   HOWTO (R_RISCV_RVC_JUMP,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 2,				/* size */ | 
 | 	 16,				/* bitsize */ | 
 | 	 true,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_RVC_JUMP",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 ENCODE_CJTYPE_IMM (-1U),	/* dst_mask */ | 
 | 	 true),				/* pcrel_offset */ | 
 |  | 
 |   /* High 6 bits of 18-bit absolute address.  */ | 
 |   HOWTO (R_RISCV_RVC_LUI,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 2,				/* size */ | 
 | 	 16,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_RVC_LUI",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 ENCODE_CITYPE_IMM (-1U),	/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* GP-relative load.  */ | 
 |   HOWTO (R_RISCV_GPREL_I,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 4,				/* size */ | 
 | 	 32,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_GPREL_I",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 ENCODE_ITYPE_IMM (-1U),	/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* GP-relative store.  */ | 
 |   HOWTO (R_RISCV_GPREL_S,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 4,				/* size */ | 
 | 	 32,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_GPREL_S",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 ENCODE_STYPE_IMM (-1U),	/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* TP-relative TLS LE load.  */ | 
 |   HOWTO (R_RISCV_TPREL_I,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 4,				/* size */ | 
 | 	 32,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_signed,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_TPREL_I",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 ENCODE_ITYPE_IMM (-1U),	/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* TP-relative TLS LE store.  */ | 
 |   HOWTO (R_RISCV_TPREL_S,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 4,				/* size */ | 
 | 	 32,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_signed,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_TPREL_S",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 ENCODE_STYPE_IMM (-1U),	/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* The paired relocation may be relaxed.  */ | 
 |   HOWTO (R_RISCV_RELAX,			/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 0,				/* size */ | 
 | 	 0,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_RELAX",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 0,				/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* 6-bit in-place addition, for local label subtraction.  */ | 
 |   HOWTO (R_RISCV_SUB6,			/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 1,				/* size */ | 
 | 	 8,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 riscv_elf_add_sub_reloc,	/* special_function */ | 
 | 	 "R_RISCV_SUB6",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 0x3f,				/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* 6-bit in-place setting, for local label subtraction.  */ | 
 |   HOWTO (R_RISCV_SET6,			/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 1,				/* size */ | 
 | 	 8,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_SET6",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 0x3f,				/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* 8-bit in-place setting, for local label subtraction.  */ | 
 |   HOWTO (R_RISCV_SET8,			/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 1,				/* size */ | 
 | 	 8,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_SET8",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 0xff,				/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* 16-bit in-place setting, for local label subtraction.  */ | 
 |   HOWTO (R_RISCV_SET16,			/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 2,				/* size */ | 
 | 	 16,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_SET16",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 0xffff,			/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* 32-bit in-place setting, for local label subtraction.  */ | 
 |   HOWTO (R_RISCV_SET32,			/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 4,				/* size */ | 
 | 	 32,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_SET32",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 0xffffffff,			/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* 32-bit PC relative.  */ | 
 |   HOWTO (R_RISCV_32_PCREL,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 4,				/* size */ | 
 | 	 32,				/* bitsize */ | 
 | 	 true,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_32_PCREL",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 0xffffffff,			/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 |  | 
 |   /* Relocation against a local ifunc symbol in a shared object.  */ | 
 |   HOWTO (R_RISCV_IRELATIVE,		/* type */ | 
 | 	 0,				/* rightshift */ | 
 | 	 4,				/* size */ | 
 | 	 32,				/* bitsize */ | 
 | 	 false,				/* pc_relative */ | 
 | 	 0,				/* bitpos */ | 
 | 	 complain_overflow_dont,	/* complain_on_overflow */ | 
 | 	 bfd_elf_generic_reloc,		/* special_function */ | 
 | 	 "R_RISCV_IRELATIVE",		/* name */ | 
 | 	 false,				/* partial_inplace */ | 
 | 	 0,				/* src_mask */ | 
 | 	 0xffffffff,			/* dst_mask */ | 
 | 	 false),			/* pcrel_offset */ | 
 | }; | 
 |  | 
 | /* A mapping from BFD reloc types to RISC-V ELF reloc types.  */ | 
 | struct elf_reloc_map | 
 | { | 
 |   bfd_reloc_code_real_type bfd_val; | 
 |   enum elf_riscv_reloc_type elf_val; | 
 | }; | 
 |  | 
 | static const struct elf_reloc_map riscv_reloc_map[] = | 
 | { | 
 |   { BFD_RELOC_NONE, R_RISCV_NONE }, | 
 |   { BFD_RELOC_32, R_RISCV_32 }, | 
 |   { BFD_RELOC_64, R_RISCV_64 }, | 
 |   { BFD_RELOC_RISCV_ADD8, R_RISCV_ADD8 }, | 
 |   { BFD_RELOC_RISCV_ADD16, R_RISCV_ADD16 }, | 
 |   { BFD_RELOC_RISCV_ADD32, R_RISCV_ADD32 }, | 
 |   { BFD_RELOC_RISCV_ADD64, R_RISCV_ADD64 }, | 
 |   { BFD_RELOC_RISCV_SUB8, R_RISCV_SUB8 }, | 
 |   { BFD_RELOC_RISCV_SUB16, R_RISCV_SUB16 }, | 
 |   { BFD_RELOC_RISCV_SUB32, R_RISCV_SUB32 }, | 
 |   { BFD_RELOC_RISCV_SUB64, R_RISCV_SUB64 }, | 
 |   { BFD_RELOC_CTOR, R_RISCV_64 }, | 
 |   { BFD_RELOC_12_PCREL, R_RISCV_BRANCH }, | 
 |   { BFD_RELOC_RISCV_HI20, R_RISCV_HI20 }, | 
 |   { BFD_RELOC_RISCV_LO12_I, R_RISCV_LO12_I }, | 
 |   { BFD_RELOC_RISCV_LO12_S, R_RISCV_LO12_S }, | 
 |   { BFD_RELOC_RISCV_PCREL_LO12_I, R_RISCV_PCREL_LO12_I }, | 
 |   { BFD_RELOC_RISCV_PCREL_LO12_S, R_RISCV_PCREL_LO12_S }, | 
 |   { BFD_RELOC_RISCV_CALL, R_RISCV_CALL }, | 
 |   { BFD_RELOC_RISCV_CALL_PLT, R_RISCV_CALL_PLT }, | 
 |   { BFD_RELOC_RISCV_PCREL_HI20, R_RISCV_PCREL_HI20 }, | 
 |   { BFD_RELOC_RISCV_JMP, R_RISCV_JAL }, | 
 |   { BFD_RELOC_RISCV_GOT_HI20, R_RISCV_GOT_HI20 }, | 
 |   { BFD_RELOC_RISCV_TLS_DTPMOD32, R_RISCV_TLS_DTPMOD32 }, | 
 |   { BFD_RELOC_RISCV_TLS_DTPREL32, R_RISCV_TLS_DTPREL32 }, | 
 |   { BFD_RELOC_RISCV_TLS_DTPMOD64, R_RISCV_TLS_DTPMOD64 }, | 
 |   { BFD_RELOC_RISCV_TLS_DTPREL64, R_RISCV_TLS_DTPREL64 }, | 
 |   { BFD_RELOC_RISCV_TLS_TPREL32, R_RISCV_TLS_TPREL32 }, | 
 |   { BFD_RELOC_RISCV_TLS_TPREL64, R_RISCV_TLS_TPREL64 }, | 
 |   { BFD_RELOC_RISCV_TPREL_HI20, R_RISCV_TPREL_HI20 }, | 
 |   { BFD_RELOC_RISCV_TPREL_ADD, R_RISCV_TPREL_ADD }, | 
 |   { BFD_RELOC_RISCV_TPREL_LO12_S, R_RISCV_TPREL_LO12_S }, | 
 |   { BFD_RELOC_RISCV_TPREL_LO12_I, R_RISCV_TPREL_LO12_I }, | 
 |   { BFD_RELOC_RISCV_TLS_GOT_HI20, R_RISCV_TLS_GOT_HI20 }, | 
 |   { BFD_RELOC_RISCV_TLS_GD_HI20, R_RISCV_TLS_GD_HI20 }, | 
 |   { BFD_RELOC_RISCV_ALIGN, R_RISCV_ALIGN }, | 
 |   { BFD_RELOC_RISCV_RVC_BRANCH, R_RISCV_RVC_BRANCH }, | 
 |   { BFD_RELOC_RISCV_RVC_JUMP, R_RISCV_RVC_JUMP }, | 
 |   { BFD_RELOC_RISCV_RVC_LUI, R_RISCV_RVC_LUI }, | 
 |   { BFD_RELOC_RISCV_GPREL_I, R_RISCV_GPREL_I }, | 
 |   { BFD_RELOC_RISCV_GPREL_S, R_RISCV_GPREL_S }, | 
 |   { BFD_RELOC_RISCV_TPREL_I, R_RISCV_TPREL_I }, | 
 |   { BFD_RELOC_RISCV_TPREL_S, R_RISCV_TPREL_S }, | 
 |   { BFD_RELOC_RISCV_RELAX, R_RISCV_RELAX }, | 
 |   { BFD_RELOC_RISCV_SUB6, R_RISCV_SUB6 }, | 
 |   { BFD_RELOC_RISCV_SET6, R_RISCV_SET6 }, | 
 |   { BFD_RELOC_RISCV_SET8, R_RISCV_SET8 }, | 
 |   { BFD_RELOC_RISCV_SET16, R_RISCV_SET16 }, | 
 |   { BFD_RELOC_RISCV_SET32, R_RISCV_SET32 }, | 
 |   { BFD_RELOC_RISCV_32_PCREL, R_RISCV_32_PCREL }, | 
 | }; | 
 |  | 
 | /* Given a BFD reloc type, return a howto structure.  */ | 
 |  | 
 | reloc_howto_type * | 
 | riscv_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED, | 
 | 			 bfd_reloc_code_real_type code) | 
 | { | 
 |   unsigned int i; | 
 |  | 
 |   for (i = 0; i < ARRAY_SIZE (riscv_reloc_map); i++) | 
 |     if (riscv_reloc_map[i].bfd_val == code) | 
 |       return &howto_table[(int) riscv_reloc_map[i].elf_val]; | 
 |  | 
 |   bfd_set_error (bfd_error_bad_value); | 
 |   return NULL; | 
 | } | 
 |  | 
 | reloc_howto_type * | 
 | riscv_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, const char *r_name) | 
 | { | 
 |   unsigned int i; | 
 |  | 
 |   for (i = 0; i < ARRAY_SIZE (howto_table); i++) | 
 |     if (howto_table[i].name && strcasecmp (howto_table[i].name, r_name) == 0) | 
 |       return &howto_table[i]; | 
 |  | 
 |   return NULL; | 
 | } | 
 |  | 
 | reloc_howto_type * | 
 | riscv_elf_rtype_to_howto (bfd *abfd, unsigned int r_type) | 
 | { | 
 |   if (r_type >= ARRAY_SIZE (howto_table)) | 
 |     { | 
 |       (*_bfd_error_handler) (_("%pB: unsupported relocation type %#x"), | 
 | 			     abfd, r_type); | 
 |       bfd_set_error (bfd_error_bad_value); | 
 |       return NULL; | 
 |     } | 
 |   return &howto_table[r_type]; | 
 | } | 
 |  | 
 | /* Special_function of RISCV_ADD and RISCV_SUB relocations.  */ | 
 |  | 
 | static bfd_reloc_status_type | 
 | riscv_elf_add_sub_reloc (bfd *abfd, | 
 | 			 arelent *reloc_entry, | 
 | 			 asymbol *symbol, | 
 | 			 void *data, | 
 | 			 asection *input_section, | 
 | 			 bfd *output_bfd, | 
 | 			 char **error_message ATTRIBUTE_UNUSED) | 
 | { | 
 |   reloc_howto_type *howto = reloc_entry->howto; | 
 |   bfd_vma relocation; | 
 |  | 
 |   if (output_bfd != NULL | 
 |       && (symbol->flags & BSF_SECTION_SYM) == 0 | 
 |       && (!reloc_entry->howto->partial_inplace || reloc_entry->addend == 0)) | 
 |     { | 
 |       reloc_entry->address += input_section->output_offset; | 
 |       return bfd_reloc_ok; | 
 |     } | 
 |  | 
 |   if (output_bfd != NULL) | 
 |     return bfd_reloc_continue; | 
 |  | 
 |   relocation = symbol->value + symbol->section->output_section->vma | 
 |     + symbol->section->output_offset + reloc_entry->addend; | 
 |  | 
 |   bfd_size_type octets = reloc_entry->address | 
 |     * bfd_octets_per_byte (abfd, input_section); | 
 |   if (!bfd_reloc_offset_in_range (reloc_entry->howto, abfd, | 
 | 				  input_section, octets)) | 
 |     return bfd_reloc_outofrange; | 
 |  | 
 |   bfd_vma old_value = bfd_get (howto->bitsize, abfd, | 
 | 			       data + reloc_entry->address); | 
 |  | 
 |   switch (howto->type) | 
 |     { | 
 |     case R_RISCV_ADD8: | 
 |     case R_RISCV_ADD16: | 
 |     case R_RISCV_ADD32: | 
 |     case R_RISCV_ADD64: | 
 |       relocation = old_value + relocation; | 
 |       break; | 
 |     case R_RISCV_SUB6: | 
 |     case R_RISCV_SUB8: | 
 |     case R_RISCV_SUB16: | 
 |     case R_RISCV_SUB32: | 
 |     case R_RISCV_SUB64: | 
 |       relocation = old_value - relocation; | 
 |       break; | 
 |     } | 
 |   bfd_put (howto->bitsize, abfd, relocation, data + reloc_entry->address); | 
 |  | 
 |   return bfd_reloc_ok; | 
 | } | 
 |  | 
 | /* Always add the IMPLICIT for the SUBSET.  */ | 
 |  | 
 | static bool | 
 | check_implicit_always (const char *implicit ATTRIBUTE_UNUSED, | 
 | 		       riscv_subset_t *subset ATTRIBUTE_UNUSED) | 
 | { | 
 |   return true; | 
 | } | 
 |  | 
 | /* Add the IMPLICIT only when the version of SUBSET less than 2.1.  */ | 
 |  | 
 | static bool | 
 | check_implicit_for_i (const char *implicit ATTRIBUTE_UNUSED, | 
 | 		      riscv_subset_t *subset) | 
 | { | 
 |   return (subset->major_version < 2 | 
 | 	  || (subset->major_version == 2 | 
 | 	      && subset->minor_version < 1)); | 
 | } | 
 |  | 
 | /* Record all implicit information for the subsets.  */ | 
 | struct riscv_implicit_subset | 
 | { | 
 |   const char *subset_name; | 
 |   const char *implicit_name; | 
 |   /* A function to determine if we need to add the implicit subset.  */ | 
 |   bool (*check_func) (const char *, riscv_subset_t *); | 
 | }; | 
 | static struct riscv_implicit_subset riscv_implicit_subsets[] = | 
 | { | 
 |   {"e", "i",		check_implicit_always}, | 
 |   {"i", "zicsr",	check_implicit_for_i}, | 
 |   {"i", "zifencei",	check_implicit_for_i}, | 
 |   {"g", "i",		check_implicit_always}, | 
 |   {"g", "m",		check_implicit_always}, | 
 |   {"g", "a",		check_implicit_always}, | 
 |   {"g", "f",		check_implicit_always}, | 
 |   {"g", "d",		check_implicit_always}, | 
 |   {"g", "zicsr",	check_implicit_always}, | 
 |   {"g", "zifencei",	check_implicit_always}, | 
 |   {"q", "d",		check_implicit_always}, | 
 |   {"v", "d",		check_implicit_always}, | 
 |   {"v", "zve64d",	check_implicit_always}, | 
 |   {"v", "zvl128b",	check_implicit_always}, | 
 |   {"zve64d", "d",	check_implicit_always}, | 
 |   {"zve64d", "zve64f",	check_implicit_always}, | 
 |   {"zve64f", "zve32f",	check_implicit_always}, | 
 |   {"zve64f", "zve64x",	check_implicit_always}, | 
 |   {"zve64f", "zvl64b",	check_implicit_always}, | 
 |   {"zve32f", "f",	check_implicit_always}, | 
 |   {"zve32f", "zvl32b",	check_implicit_always}, | 
 |   {"zve32f", "zve32x",	check_implicit_always}, | 
 |   {"zve64x", "zve32x",	check_implicit_always}, | 
 |   {"zve64x", "zvl64b",	check_implicit_always}, | 
 |   {"zve32x", "zvl32b",	check_implicit_always}, | 
 |   {"zvl65536b", "zvl32768b",	check_implicit_always}, | 
 |   {"zvl32768b", "zvl16384b",	check_implicit_always}, | 
 |   {"zvl16384b", "zvl8192b",	check_implicit_always}, | 
 |   {"zvl8192b", "zvl4096b",	check_implicit_always}, | 
 |   {"zvl4096b", "zvl2048b",	check_implicit_always}, | 
 |   {"zvl2048b", "zvl1024b",	check_implicit_always}, | 
 |   {"zvl1024b", "zvl512b",	check_implicit_always}, | 
 |   {"zvl512b", "zvl256b",	check_implicit_always}, | 
 |   {"zvl256b", "zvl128b",	check_implicit_always}, | 
 |   {"zvl128b", "zvl64b",		check_implicit_always}, | 
 |   {"zvl64b", "zvl32b",		check_implicit_always}, | 
 |   {"d", "f",		check_implicit_always}, | 
 |   {"f", "zicsr",	check_implicit_always}, | 
 |   {"zfh", "f",		check_implicit_always}, | 
 |   {"zfh", "zicsr",	check_implicit_always}, | 
 |   {"zqinx", "zdinx",	check_implicit_always}, | 
 |   {"zdinx", "zfinx",	check_implicit_always}, | 
 |   {"zfinx", "zicsr",	check_implicit_always}, | 
 |   {"zhinx", "zfinx",	check_implicit_always}, | 
 |   {"zhinx", "zicsr",	check_implicit_always}, | 
 |   {"zk", "zkn",		check_implicit_always}, | 
 |   {"zk", "zkr",		check_implicit_always}, | 
 |   {"zk", "zkt",		check_implicit_always}, | 
 |   {"zkn", "zbkb",	check_implicit_always}, | 
 |   {"zkn", "zbkc",	check_implicit_always}, | 
 |   {"zkn", "zbkx",	check_implicit_always}, | 
 |   {"zkn", "zkne",	check_implicit_always}, | 
 |   {"zkn", "zknd",	check_implicit_always}, | 
 |   {"zkn", "zknh",	check_implicit_always}, | 
 |   {"zks", "zbkb",	check_implicit_always}, | 
 |   {"zks", "zbkc",	check_implicit_always}, | 
 |   {"zks", "zbkx",	check_implicit_always}, | 
 |   {"zks", "zksed",	check_implicit_always}, | 
 |   {"zks", "zksh",	check_implicit_always}, | 
 |   {NULL, NULL, NULL} | 
 | }; | 
 |  | 
 | /* For default_enable field, decide if the extension should | 
 |    be enbaled by default.  */ | 
 |  | 
 | #define EXT_DEFAULT   0x1 | 
 |  | 
 | /* List all extensions that binutils should know about.  */ | 
 |  | 
 | struct riscv_supported_ext | 
 | { | 
 |   const char *name; | 
 |   enum riscv_spec_class isa_spec_class; | 
 |   int major_version; | 
 |   int minor_version; | 
 |   unsigned long default_enable; | 
 | }; | 
 |  | 
 | /* The standard extensions must be added in canonical order.  */ | 
 |  | 
 | static struct riscv_supported_ext riscv_supported_std_ext[] = | 
 | { | 
 |   {"e",		ISA_SPEC_CLASS_20191213,	1, 9, 0 }, | 
 |   {"e",		ISA_SPEC_CLASS_20190608,	1, 9, 0 }, | 
 |   {"e",		ISA_SPEC_CLASS_2P2,		1, 9, 0 }, | 
 |   {"i",		ISA_SPEC_CLASS_20191213,	2, 1, 0 }, | 
 |   {"i",		ISA_SPEC_CLASS_20190608,	2, 1, 0 }, | 
 |   {"i",		ISA_SPEC_CLASS_2P2,		2, 0, 0 }, | 
 |   /* The g is a special case which we don't want to output it, | 
 |      but still need it when adding implicit extensions.  */ | 
 |   {"g",		ISA_SPEC_CLASS_NONE, RISCV_UNKNOWN_VERSION, RISCV_UNKNOWN_VERSION, EXT_DEFAULT }, | 
 |   {"m",		ISA_SPEC_CLASS_20191213,	2, 0, 0 }, | 
 |   {"m",		ISA_SPEC_CLASS_20190608,	2, 0, 0 }, | 
 |   {"m",		ISA_SPEC_CLASS_2P2,		2, 0, 0 }, | 
 |   {"a",		ISA_SPEC_CLASS_20191213,	2, 1, 0 }, | 
 |   {"a",		ISA_SPEC_CLASS_20190608,	2, 0, 0 }, | 
 |   {"a",		ISA_SPEC_CLASS_2P2,		2, 0, 0 }, | 
 |   {"f",		ISA_SPEC_CLASS_20191213,	2, 2, 0 }, | 
 |   {"f",		ISA_SPEC_CLASS_20190608,	2, 2, 0 }, | 
 |   {"f",		ISA_SPEC_CLASS_2P2,		2, 0, 0 }, | 
 |   {"d",		ISA_SPEC_CLASS_20191213,	2, 2, 0 }, | 
 |   {"d",		ISA_SPEC_CLASS_20190608,	2, 2, 0 }, | 
 |   {"d",		ISA_SPEC_CLASS_2P2,		2, 0, 0 }, | 
 |   {"q",		ISA_SPEC_CLASS_20191213,	2, 2, 0 }, | 
 |   {"q",		ISA_SPEC_CLASS_20190608,	2, 2, 0 }, | 
 |   {"q",		ISA_SPEC_CLASS_2P2,		2, 0, 0 }, | 
 |   {"c",		ISA_SPEC_CLASS_20191213,	2, 0, 0 }, | 
 |   {"c",		ISA_SPEC_CLASS_20190608,	2, 0, 0 }, | 
 |   {"c",		ISA_SPEC_CLASS_2P2,		2, 0, 0 }, | 
 |   {"v",		ISA_SPEC_CLASS_DRAFT,		1, 0, 0 }, | 
 |   {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 }, | 
 |   {"zfh",		ISA_SPEC_CLASS_DRAFT,		1, 0,  0 }, | 
 |   {"zfinx",		ISA_SPEC_CLASS_DRAFT,		1, 0,  0 }, | 
 |   {"zdinx",		ISA_SPEC_CLASS_DRAFT,		1, 0,  0 }, | 
 |   {"zqinx",		ISA_SPEC_CLASS_DRAFT,		1, 0,  0 }, | 
 |   {"zhinx",		ISA_SPEC_CLASS_DRAFT,		1, 0,  0 }, | 
 |   {"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[] = "eigmafdqlcbkjtpvn"; | 
 |  | 
 | /* 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) | 
 |       && (subset->major_version < 2 || (subset->major_version == 2 | 
 | 					&& subset->minor_version < 2)) | 
 |       && xlen < 64) | 
 |     { | 
 |       rps->error_handler (_("rv%d does not support the `q' extension"), xlen); | 
 |       no_conflict = false; | 
 |     } | 
 |   if (riscv_lookup_subset (rps->subset_list, "e", &subset) | 
 |       && riscv_lookup_subset (rps->subset_list, "f", &subset)) | 
 |     { | 
 |       rps->error_handler | 
 |         (_("rv32e does not support the `f' extension")); | 
 |       no_conflict = false; | 
 |     } | 
 |   if (riscv_lookup_subset (rps->subset_list, "zfinx", &subset) | 
 |       && riscv_lookup_subset (rps->subset_list, "f", &subset)) | 
 |     { | 
 |       rps->error_handler | 
 | 	(_("`zfinx' is conflict with the `f/d/q' extension")); | 
 |       no_conflict = false; | 
 |     } | 
 |  | 
 |   bool support_zve = false; | 
 |   bool support_zvl = false; | 
 |   riscv_subset_t *s = rps->subset_list->head; | 
 |   for (; s != NULL; s = s->next) | 
 |     { | 
 |       if (!support_zve | 
 | 	  && strncmp (s->name, "zve", 3) == 0) | 
 | 	support_zve = true; | 
 |       if (!support_zvl | 
 | 	  && strncmp (s->name, "zvl", 3) == 0) | 
 | 	support_zvl = true; | 
 |       if (support_zve && support_zvl) | 
 | 	break; | 
 |     } | 
 |   if (support_zvl && !support_zve) | 
 |     { | 
 |       rps->error_handler | 
 | 	(_("zvl*b extensions need to enable either `v' or `zve' extension")); | 
 |       no_conflict = false; | 
 |     } | 
 |  | 
 |   return no_conflict; | 
 | } | 
 |  | 
 | /* Set the default subset list according to the default_enable field | 
 |    of riscv_supported_*ext tables.  */ | 
 |  | 
 | static void | 
 | riscv_set_default_arch (riscv_parse_subset_t *rps) | 
 | { | 
 |   unsigned long enable = EXT_DEFAULT; | 
 |   int i, j; | 
 |   for (i = 0; riscv_all_supported_ext[i] != NULL; i++) | 
 |     { | 
 |       const struct riscv_supported_ext *table = riscv_all_supported_ext[i]; | 
 |       for (j = 0; table[j].name != NULL; j++) | 
 | 	{ | 
 | 	  bool implicit = false; | 
 | 	  if (strcmp (table[j].name, "g") == 0) | 
 | 	    implicit = true; | 
 | 	  if (table[j].default_enable & enable) | 
 | 	    riscv_parse_add_subset (rps, table[j].name, | 
 | 				    RISCV_UNKNOWN_VERSION, | 
 | 				    RISCV_UNKNOWN_VERSION, implicit); | 
 | 	} | 
 |     } | 
 | } | 
 |  | 
 | /* Function for parsing ISA string. | 
 |  | 
 |    Return Value: | 
 |      Return TRUE on success. | 
 |  | 
 |    Arguments: | 
 |      `rps`: Hooks and status for parsing extensions. | 
 |      `arch`: Full ISA string.  */ | 
 |  | 
 | bool | 
 | riscv_parse_subset (riscv_parse_subset_t *rps, | 
 | 		    const char *arch) | 
 | { | 
 |   const char *p; | 
 |  | 
 |   /* Init the riscv_ext_order array to compare the order of extensions | 
 |      quickly.  */ | 
 |   riscv_init_ext_order (); | 
 |  | 
 |   if (arch == NULL) | 
 |     { | 
 |       riscv_set_default_arch (rps); | 
 |       riscv_parse_add_implicit_subsets (rps); | 
 |       return riscv_parse_check_conflicts (rps); | 
 |     } | 
 |  | 
 |   for (p = arch; *p != '\0'; p++) | 
 |     { | 
 |       if (ISUPPER (*p)) | 
 | 	{ | 
 | 	  rps->error_handler | 
 | 	    (_("%s: ISA string cannot contain uppercase letters"), | 
 | 	     arch); | 
 | 	  return false; | 
 | 	} | 
 |     } | 
 |  | 
 |   p = arch; | 
 |   if (startswith (p, "rv32")) | 
 |     { | 
 |       *rps->xlen = 32; | 
 |       p += 4; | 
 |     } | 
 |   else if (startswith (p, "rv64")) | 
 |     { | 
 |       *rps->xlen = 64; | 
 |       p += 4; | 
 |     } | 
 |   else | 
 |     { | 
 |       /* ISA string shouldn't be NULL or empty here.  For linker, | 
 | 	 it might be empty when we failed to merge the ISA string | 
 | 	 in the riscv_merge_attributes.  For assembler, we might | 
 | 	 give an empty string by .attribute arch, "" or -march=. | 
 | 	 However, We have already issued the correct error message | 
 | 	 in another side, so do not issue this error when the ISA | 
 | 	 string is empty.  */ | 
 |       if (strlen (arch)) | 
 | 	rps->error_handler ( | 
 | 	  _("%s: ISA string must begin with rv32 or rv64"), | 
 | 	  arch); | 
 |       return false; | 
 |     } | 
 |  | 
 |   /* Parsing standard extension.  */ | 
 |   p = riscv_parse_std_ext (rps, arch, p); | 
 |  | 
 |   if (p == NULL) | 
 |     return false; | 
 |  | 
 |   /* Parse prefixed extensions.  */ | 
 |   p = riscv_parse_prefixed_ext (rps, arch, p); | 
 |  | 
 |   if (p == NULL) | 
 |     return false; | 
 |  | 
 |   /* Finally add implicit extensions according to the current | 
 |      extensions.  */ | 
 |   riscv_parse_add_implicit_subsets (rps); | 
 |  | 
 |   /* Check the conflicts.  */ | 
 |   return riscv_parse_check_conflicts (rps); | 
 | } | 
 |  | 
 | /* Return the number of digits for the input.  */ | 
 |  | 
 | size_t | 
 | riscv_estimate_digit (unsigned num) | 
 | { | 
 |   size_t digit = 0; | 
 |   if (num == 0) | 
 |     return 1; | 
 |  | 
 |   for (digit = 0; num ; num /= 10) | 
 |     digit++; | 
 |  | 
 |   return digit; | 
 | } | 
 |  | 
 | /* Auxiliary function to estimate string length of subset list.  */ | 
 |  | 
 | static size_t | 
 | riscv_estimate_arch_strlen1 (const riscv_subset_t *subset) | 
 | { | 
 |   if (subset == NULL) | 
 |     return 6; /* For rv32/rv64/rv128 and string terminator.  */ | 
 |  | 
 |   return riscv_estimate_arch_strlen1 (subset->next) | 
 | 	 + strlen (subset->name) | 
 | 	 + riscv_estimate_digit (subset->major_version) | 
 | 	 + 1 /* For version seperator 'p'.  */ | 
 | 	 + riscv_estimate_digit (subset->minor_version) | 
 | 	 + 1 /* For underscore.  */; | 
 | } | 
 |  | 
 | /* Estimate the string length of this subset list.  */ | 
 |  | 
 | static size_t | 
 | riscv_estimate_arch_strlen (const riscv_subset_list_t *subset_list) | 
 | { | 
 |   return riscv_estimate_arch_strlen1 (subset_list->head); | 
 | } | 
 |  | 
 | /* Auxiliary function to convert subset info to string.  */ | 
 |  | 
 | static void | 
 | riscv_arch_str1 (riscv_subset_t *subset, | 
 | 		 char *attr_str, char *buf, size_t bufsz) | 
 | { | 
 |   const char *underline = "_"; | 
 |   riscv_subset_t *subset_t = subset; | 
 |  | 
 |   if (subset_t == NULL) | 
 |     return; | 
 |  | 
 |   /* No underline between rvXX and i/e.  */ | 
 |   if ((strcasecmp (subset_t->name, "i") == 0) | 
 |       || (strcasecmp (subset_t->name, "e") == 0)) | 
 |     underline = ""; | 
 |  | 
 |   snprintf (buf, bufsz, "%s%s%dp%d", | 
 | 	    underline, | 
 | 	    subset_t->name, | 
 | 	    subset_t->major_version, | 
 | 	    subset_t->minor_version); | 
 |  | 
 |   strncat (attr_str, buf, bufsz); | 
 |  | 
 |   /* Skip 'i' extension after 'e', or skip extensions which | 
 |      versions are unknown.  */ | 
 |   while (subset_t->next | 
 | 	 && ((strcmp (subset_t->name, "e") == 0 | 
 | 	      && strcmp (subset_t->next->name, "i") == 0) | 
 | 	     || subset_t->next->major_version == RISCV_UNKNOWN_VERSION | 
 | 	     || subset_t->next->minor_version == RISCV_UNKNOWN_VERSION)) | 
 |     subset_t = subset_t->next; | 
 |  | 
 |   riscv_arch_str1 (subset_t->next, attr_str, buf, bufsz); | 
 | } | 
 |  | 
 | /* Convert subset information into string with explicit versions.  */ | 
 |  | 
 | char * | 
 | riscv_arch_str (unsigned xlen, const riscv_subset_list_t *subset) | 
 | { | 
 |   size_t arch_str_len = riscv_estimate_arch_strlen (subset); | 
 |   char *attr_str = xmalloc (arch_str_len); | 
 |   char *buf = xmalloc (arch_str_len); | 
 |  | 
 |   snprintf (attr_str, arch_str_len, "rv%u", xlen); | 
 |  | 
 |   riscv_arch_str1 (subset->head, attr_str, buf, arch_str_len); | 
 |   free (buf); | 
 |  | 
 |   return attr_str; | 
 | } | 
 |  | 
 | /* Copy the subset in the subset list.  */ | 
 |  | 
 | static struct riscv_subset_t * | 
 | riscv_copy_subset (riscv_subset_list_t *subset_list, | 
 | 		   riscv_subset_t *subset) | 
 | { | 
 |   if (subset == NULL) | 
 |     return NULL; | 
 |  | 
 |   riscv_subset_t *new = xmalloc (sizeof *new); | 
 |   new->name = xstrdup (subset->name); | 
 |   new->major_version = subset->major_version; | 
 |   new->minor_version = subset->minor_version; | 
 |   new->next = riscv_copy_subset (subset_list, subset->next); | 
 |  | 
 |   if (subset->next == NULL) | 
 |     subset_list->tail = new; | 
 |  | 
 |   return new; | 
 | } | 
 |  | 
 | /* Copy the subset list.  */ | 
 |  | 
 | riscv_subset_list_t * | 
 | riscv_copy_subset_list (riscv_subset_list_t *subset_list) | 
 | { | 
 |   riscv_subset_list_t *new = xmalloc (sizeof *new); | 
 |   new->head = riscv_copy_subset (new, subset_list->head); | 
 |   return new; | 
 | } | 
 |  | 
 | /* Remove the SUBSET from the subset list.  */ | 
 |  | 
 | static void | 
 | riscv_remove_subset (riscv_subset_list_t *subset_list, | 
 | 		     const char *subset) | 
 | { | 
 |   riscv_subset_t *current = subset_list->head; | 
 |   riscv_subset_t *pre = NULL; | 
 |   for (; current != NULL; pre = current, current = current->next) | 
 |     { | 
 |       if (strcmp (current->name, subset) == 0) | 
 | 	{ | 
 | 	  if (pre == NULL) | 
 | 	    subset_list->head = current->next; | 
 | 	  else | 
 | 	    pre->next = current->next; | 
 | 	  if (current->next == NULL) | 
 | 	    subset_list->tail = pre; | 
 | 	  free ((void *) current->name); | 
 | 	  free (current); | 
 | 	  break; | 
 | 	} | 
 |     } | 
 | } | 
 |  | 
 | /* Add/Remove an extension to/from the subset list.  This is used for | 
 |    the .option rvc or norvc, and .option arch directives.  */ | 
 |  | 
 | bool | 
 | riscv_update_subset (riscv_parse_subset_t *rps, | 
 | 		     const char *str) | 
 | { | 
 |   const char *p = str; | 
 |  | 
 |   do | 
 |     { | 
 |       int major_version = RISCV_UNKNOWN_VERSION; | 
 |       int minor_version = RISCV_UNKNOWN_VERSION; | 
 |  | 
 |       bool removed = false; | 
 |       switch (*p) | 
 | 	{ | 
 | 	case '+': removed = false; break; | 
 | 	case '-': removed = true; break; | 
 | 	default: | 
 | 	  riscv_release_subset_list (rps->subset_list); | 
 | 	  return riscv_parse_subset (rps, p); | 
 | 	} | 
 |       ++p; | 
 |  | 
 |       char *subset = xstrdup (p); | 
 |       char *q = subset; | 
 |       const char *end_of_version; | 
 |       /* Extract the whole prefixed extension by ','.  */ | 
 |       while (*q != '\0' && *q != ',') | 
 |         q++; | 
 |  | 
 |       /* Look forward to the first letter which is not <major>p<minor>.  */ | 
 |       bool find_any_version = false; | 
 |       bool find_minor_version = false; | 
 |       size_t len = q - subset; | 
 |       size_t i; | 
 |       for (i = len; i > 0; i--) | 
 |         { | 
 | 	  q--; | 
 | 	  if (ISDIGIT (*q)) | 
 | 	    find_any_version = true; | 
 | 	  else if (find_any_version | 
 | 		   && !find_minor_version | 
 | 		   && *q == 'p' | 
 | 		   && ISDIGIT (*(q - 1))) | 
 | 	    find_minor_version = true; | 
 | 	  else | 
 | 	    break; | 
 | 	} | 
 |       if (len > 0) | 
 | 	q++; | 
 |  | 
 |       /* Check if the end of extension is 'p' or not.  If yes, then | 
 | 	 the second letter from the end cannot be number.  */ | 
 |       if (len > 1 && *(q - 1) == 'p' && ISDIGIT (*(q - 2))) | 
 | 	{ | 
 | 	  *q = '\0'; | 
 | 	  rps->error_handler | 
 | 	    (_("invalid ISA extension ends with <number>p " | 
 | 	       "in .option arch `%s'"), str); | 
 | 	  free (subset); | 
 | 	  return false; | 
 | 	} | 
 |  | 
 |       end_of_version = | 
 | 	riscv_parsing_subset_version (q, &major_version, &minor_version); | 
 |       *q = '\0'; | 
 |       if (end_of_version == NULL) | 
 | 	{ | 
 | 	  free (subset); | 
 | 	  return false; | 
 | 	} | 
 |  | 
 |       if (strlen (subset) == 0 | 
 | 	  || (strlen (subset) == 1 | 
 | 	      && riscv_ext_order[(*subset - 'a')] == 0) | 
 | 	  || (strlen (subset) > 1 | 
 | 	      && rps->check_unknown_prefixed_ext | 
 | 	      && !riscv_recognized_prefixed_ext (subset))) | 
 | 	{ | 
 | 	  rps->error_handler | 
 | 	    (_("unknown ISA extension `%s' in .option arch `%s'"), | 
 | 	     subset, str); | 
 | 	  free (subset); | 
 | 	  return false; | 
 | 	} | 
 |  | 
 |       if (strcmp (subset, "i") == 0 | 
 | 	  || strcmp (subset, "e") == 0 | 
 | 	  || strcmp (subset, "g") == 0) | 
 | 	{ | 
 | 	  rps->error_handler | 
 | 	    (_("cannot + or - base extension `%s' in .option " | 
 | 	       "arch `%s'"), subset, str); | 
 | 	  free (subset); | 
 | 	  return false; | 
 | 	} | 
 |  | 
 |       if (removed) | 
 | 	riscv_remove_subset (rps->subset_list, subset); | 
 |       else | 
 | 	riscv_parse_add_subset (rps, subset, major_version, minor_version, true); | 
 |       p += end_of_version - subset; | 
 |       free (subset); | 
 |     } | 
 |   while (*p++ == ','); | 
 |  | 
 |   riscv_parse_add_implicit_subsets (rps); | 
 |   return riscv_parse_check_conflicts (rps); | 
 | } | 
 |  | 
 | /* Check if the FEATURE subset is supported or not in the subset list. | 
 |    Return true if it is supported; Otherwise, return false.  */ | 
 |  | 
 | bool | 
 | riscv_subset_supports (riscv_parse_subset_t *rps, | 
 | 		       const char *feature) | 
 | { | 
 |   struct riscv_subset_t *subset; | 
 |   return riscv_lookup_subset (rps->subset_list, feature, &subset); | 
 | } | 
 |  | 
 | /* Each instuction is belonged to an instruction class INSN_CLASS_*. | 
 |    Call riscv_subset_supports to make sure if the instuction is valid.  */ | 
 |  | 
 | bool | 
 | riscv_multi_subset_supports (riscv_parse_subset_t *rps, | 
 | 			     enum riscv_insn_class insn_class) | 
 | { | 
 |   switch (insn_class) | 
 |     { | 
 |     case INSN_CLASS_I: | 
 |       return riscv_subset_supports (rps, "i"); | 
 |     case INSN_CLASS_ZICBOM: | 
 |       return riscv_subset_supports (rps, "zicbom"); | 
 |     case INSN_CLASS_ZICBOP: | 
 |       return riscv_subset_supports (rps, "zicbop"); | 
 |     case INSN_CLASS_ZICBOZ: | 
 |       return riscv_subset_supports (rps, "zicboz"); | 
 |     case INSN_CLASS_ZICSR: | 
 |       return riscv_subset_supports (rps, "zicsr"); | 
 |     case INSN_CLASS_ZIFENCEI: | 
 |       return riscv_subset_supports (rps, "zifencei"); | 
 |     case INSN_CLASS_ZIHINTPAUSE: | 
 |       return riscv_subset_supports (rps, "zihintpause"); | 
 |     case INSN_CLASS_M: | 
 |       return riscv_subset_supports (rps, "m"); | 
 |     case INSN_CLASS_A: | 
 |       return riscv_subset_supports (rps, "a"); | 
 |     case INSN_CLASS_F: | 
 |       return riscv_subset_supports (rps, "f"); | 
 |     case INSN_CLASS_D: | 
 |       return riscv_subset_supports (rps, "d"); | 
 |     case INSN_CLASS_Q: | 
 |       return riscv_subset_supports (rps, "q"); | 
 |     case INSN_CLASS_C: | 
 |       return riscv_subset_supports (rps, "c"); | 
 |     case INSN_CLASS_F_AND_C: | 
 |       return (riscv_subset_supports (rps, "f") | 
 | 	      && riscv_subset_supports (rps, "c")); | 
 |     case INSN_CLASS_D_AND_C: | 
 |       return (riscv_subset_supports (rps, "d") | 
 | 	      && riscv_subset_supports (rps, "c")); | 
 |     case INSN_CLASS_F_OR_ZFINX: | 
 |       return (riscv_subset_supports (rps, "f") | 
 | 	      || riscv_subset_supports (rps, "zfinx")); | 
 |     case INSN_CLASS_D_OR_ZDINX: | 
 |       return (riscv_subset_supports (rps, "d") | 
 | 	      || riscv_subset_supports (rps, "zdinx")); | 
 |     case INSN_CLASS_Q_OR_ZQINX: | 
 |       return (riscv_subset_supports (rps, "q") | 
 | 	      || riscv_subset_supports (rps, "zqinx")); | 
 |     case INSN_CLASS_ZFH: | 
 |       return riscv_subset_supports (rps, "zfh"); | 
 |     case INSN_CLASS_ZFH_OR_ZHINX: | 
 |       return riscv_subset_supports (rps, "zfh") | 
 |         || riscv_subset_supports (rps, "zhinx"); | 
 |     case INSN_CLASS_D_AND_ZFH_INX: | 
 |       return (riscv_subset_supports (rps, "d") | 
 | 	      && riscv_subset_supports (rps, "zfh")) | 
 |            || riscv_subset_supports (rps, "zhinx"); | 
 |     case INSN_CLASS_Q_AND_ZFH_INX: | 
 |       return (riscv_subset_supports (rps, "q") | 
 | 	      && riscv_subset_supports (rps, "zfh")) | 
 |            || riscv_subset_supports (rps, "zhinx"); | 
 |     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"; | 
 |     case INSN_CLASS_ZFH: | 
 |       return "zfh"; | 
 |     case INSN_CLASS_ZFH_OR_ZHINX: | 
 |       return "zfh' or 'zhinx"; | 
 |     case INSN_CLASS_D_AND_ZFH_INX: | 
 |       return "('d' and 'zfh') or 'zhinx"; | 
 |     case INSN_CLASS_Q_AND_ZFH_INX: | 
 |       return "('q' and 'zfh') or 'zhinx"; | 
 |     default: | 
 |       rps->error_handler | 
 |         (_("internal: unreachable INSN_CLASS_*")); | 
 |       return NULL; | 
 |     } | 
 | } |