|  | /* 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,		0, 93, 0 }, | 
|  | {"zba",		ISA_SPEC_CLASS_DRAFT,		0, 93, 0 }, | 
|  | {"zbc",		ISA_SPEC_CLASS_DRAFT,		0, 93, 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 valid or not.  Return | 
|  | true if valid, otehrwise return false.  */ | 
|  |  | 
|  | static bool | 
|  | riscv_valid_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 invalid.  */ | 
|  | 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: | 
|  | `rps`: Hooks and status for parsing extensions. | 
|  | `arch`: Full ISA string. | 
|  | `p`: Curent parsing position. | 
|  | `major_version`: Parsed major version. | 
|  | `minor_version`: Parsed minor version. | 
|  | `std_ext_p`: True if parsing standard extension.  */ | 
|  |  | 
|  | static const char * | 
|  | riscv_parsing_subset_version (riscv_parse_subset_t *rps, | 
|  | const char *arch, | 
|  | const char *p, | 
|  | int *major_version, | 
|  | int *minor_version, | 
|  | bool std_ext_p) | 
|  | { | 
|  | bool major_p = true; | 
|  | int version = 0; | 
|  | char np; | 
|  |  | 
|  | *major_version = 0; | 
|  | *minor_version = 0; | 
|  | for (; *p; ++p) | 
|  | { | 
|  | if (*p == 'p') | 
|  | { | 
|  | np = *(p + 1); | 
|  |  | 
|  | if (!ISDIGIT (np)) | 
|  | { | 
|  | /* Might be beginning of `p` extension.  */ | 
|  | if (std_ext_p) | 
|  | { | 
|  | *major_version = version; | 
|  | *minor_version = 0; | 
|  | return p; | 
|  | } | 
|  | else | 
|  | { | 
|  | rps->error_handler | 
|  | (_("%s: expect number after `%dp'"), | 
|  | arch, version); | 
|  | return NULL; | 
|  | } | 
|  | } | 
|  |  | 
|  | *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 (rps, arch, ++p, &major, &minor, true); | 
|  | /* 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; | 
|  |  | 
|  | while (*++q != '\0' && *q != '_' && !ISDIGIT (*q)) | 
|  | ; | 
|  |  | 
|  | end_of_version = | 
|  | riscv_parsing_subset_version (rps, arch, q, | 
|  | &major_version, | 
|  | &minor_version, false); | 
|  | *q = '\0'; | 
|  |  | 
|  | if (end_of_version == NULL) | 
|  | { | 
|  | free (subset); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /* Check if the prefix extension is known. | 
|  | For 'x', anything goes but it cannot simply be 'x'. | 
|  | For other prefixed extensions, it must be known from a list | 
|  | and cannot simply be the prefixed name.  */ | 
|  |  | 
|  | /* Check that the extension name is well-formed.  */ | 
|  | if (rps->check_unknown_prefixed_ext | 
|  | && !riscv_valid_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; | 
|  | } |