/* RISC-V-specific support for ELF.
- Copyright (C) 2011-2018 Free Software Foundation, Inc.
+ Copyright (C) 2011-2021 Free Software Foundation, Inc.
Contributed by Andrew Waterman (andrew@sifive.com).
Based on TILE-Gx and MIPS targets.
#include "libiberty.h"
#include "elfxx-riscv.h"
#include "safe-ctype.h"
+#include "cpu-riscv.h"
#define MINUS_ONE ((bfd_vma)0 - 1)
0, /* rightshift */
3, /* size */
0, /* bitsize */
- FALSE, /* pc_relative */
+ false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_RISCV_NONE", /* name */
- FALSE, /* partial_inplace */
+ false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
- FALSE), /* pcrel_offset */
+ false), /* pcrel_offset */
/* 32 bit relocation. */
HOWTO (R_RISCV_32, /* type */
0, /* rightshift */
2, /* size */
32, /* bitsize */
- FALSE, /* pc_relative */
+ false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_RISCV_32", /* name */
- FALSE, /* partial_inplace */
+ false, /* partial_inplace */
0, /* src_mask */
- MINUS_ONE, /* dst_mask */
- FALSE), /* pcrel_offset */
+ 0xffffffff, /* dst_mask */
+ false), /* pcrel_offset */
/* 64 bit relocation. */
HOWTO (R_RISCV_64, /* type */
0, /* rightshift */
4, /* size */
64, /* bitsize */
- FALSE, /* pc_relative */
+ false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_RISCV_64", /* name */
- FALSE, /* partial_inplace */
+ false, /* partial_inplace */
0, /* src_mask */
MINUS_ONE, /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_RISCV_RELATIVE", /* name */
- FALSE, /* partial_inplace */
+ false, /* partial_inplace */
0, /* src_mask */
- MINUS_ONE, /* dst_mask */
- FALSE), /* pcrel_offset */
+ 0xffffffff, /* dst_mask */
+ false), /* pcrel_offset */
HOWTO (R_RISCV_COPY, /* type */
0, /* rightshift */
0, /* this one is variable size */
0, /* bitsize */
- FALSE, /* pc_relative */
+ false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_RISCV_COPY", /* name */
- FALSE, /* partial_inplace */
+ false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
- FALSE), /* pcrel_offset */
+ false), /* pcrel_offset */
HOWTO (R_RISCV_JUMP_SLOT, /* type */
0, /* rightshift */
4, /* size */
64, /* bitsize */
- FALSE, /* pc_relative */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
- FALSE), /* pcrel_offset */
+ false), /* pcrel_offset */
/* Dynamic TLS relocations. */
HOWTO (R_RISCV_TLS_DTPMOD32, /* type */
0, /* rightshift */
- 4, /* size */
+ 2, /* size */
32, /* bitsize */
- FALSE, /* pc_relative */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
- MINUS_ONE, /* dst_mask */
- FALSE), /* pcrel_offset */
+ 0xffffffff, /* dst_mask */
+ false), /* pcrel_offset */
HOWTO (R_RISCV_TLS_DTPMOD64, /* type */
0, /* rightshift */
4, /* size */
64, /* bitsize */
- FALSE, /* pc_relative */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
MINUS_ONE, /* dst_mask */
- FALSE), /* pcrel_offset */
+ false), /* pcrel_offset */
HOWTO (R_RISCV_TLS_DTPREL32, /* type */
0, /* rightshift */
- 4, /* size */
+ 2, /* size */
32, /* bitsize */
- FALSE, /* pc_relative */
+ 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 */
+ true, /* partial_inplace */
0, /* src_mask */
- MINUS_ONE, /* dst_mask */
- FALSE), /* pcrel_offset */
+ 0xffffffff, /* dst_mask */
+ false), /* pcrel_offset */
HOWTO (R_RISCV_TLS_DTPREL64, /* type */
0, /* rightshift */
4, /* size */
64, /* bitsize */
- FALSE, /* pc_relative */
+ 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 */
+ true, /* partial_inplace */
0, /* src_mask */
MINUS_ONE, /* dst_mask */
- FALSE), /* pcrel_offset */
+ false), /* pcrel_offset */
HOWTO (R_RISCV_TLS_TPREL32, /* type */
0, /* rightshift */
2, /* size */
32, /* bitsize */
- FALSE, /* pc_relative */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
- MINUS_ONE, /* dst_mask */
- FALSE), /* pcrel_offset */
+ 0xffffffff, /* dst_mask */
+ false), /* pcrel_offset */
HOWTO (R_RISCV_TLS_TPREL64, /* type */
0, /* rightshift */
4, /* size */
64, /* bitsize */
- FALSE, /* pc_relative */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
MINUS_ONE, /* dst_mask */
- FALSE), /* pcrel_offset */
+ false), /* pcrel_offset */
/* Reserved for future relocs that the dynamic linker must understand. */
EMPTY_HOWTO (12),
0, /* rightshift */
2, /* size */
32, /* bitsize */
- TRUE, /* pc_relative */
+ true, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_RISCV_BRANCH", /* name */
- FALSE, /* partial_inplace */
+ false, /* partial_inplace */
0, /* src_mask */
- ENCODE_SBTYPE_IMM (-1U), /* dst_mask */
- TRUE), /* pcrel_offset */
+ 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 */
+ true, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_RISCV_JAL", /* name */
- FALSE, /* partial_inplace */
+ false, /* partial_inplace */
0, /* src_mask */
- ENCODE_UJTYPE_IMM (-1U), /* dst_mask */
- TRUE), /* pcrel_offset */
+ ENCODE_JTYPE_IMM (-1U), /* dst_mask */
+ true), /* pcrel_offset */
/* 32-bit PC-relative function call (AUIPC/JALR). */
HOWTO (R_RISCV_CALL, /* type */
0, /* rightshift */
- 2, /* size */
+ 4, /* size */
64, /* bitsize */
- TRUE, /* pc_relative */
+ true, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_RISCV_CALL", /* name */
- FALSE, /* partial_inplace */
+ false, /* partial_inplace */
0, /* src_mask */
ENCODE_UTYPE_IMM (-1U) | ((bfd_vma) ENCODE_ITYPE_IMM (-1U) << 32),
/* dst_mask */
- TRUE), /* pcrel_offset */
+ true), /* pcrel_offset */
/* Like R_RISCV_CALL, but not locally binding. */
HOWTO (R_RISCV_CALL_PLT, /* type */
0, /* rightshift */
- 2, /* size */
+ 4, /* size */
64, /* bitsize */
- TRUE, /* pc_relative */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
ENCODE_UTYPE_IMM (-1U) | ((bfd_vma) ENCODE_ITYPE_IMM (-1U) << 32),
/* dst_mask */
- TRUE), /* pcrel_offset */
+ 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 */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
ENCODE_UTYPE_IMM (-1U), /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
ENCODE_UTYPE_IMM (-1U), /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
ENCODE_UTYPE_IMM (-1U), /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
ENCODE_UTYPE_IMM (-1U), /* dst_mask */
- TRUE), /* pcrel_offset */
+ 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 */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
ENCODE_ITYPE_IMM (-1U), /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
ENCODE_STYPE_IMM (-1U), /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_RISCV_HI20", /* name */
- FALSE, /* partial_inplace */
+ false, /* partial_inplace */
0, /* src_mask */
ENCODE_UTYPE_IMM (-1U), /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
ENCODE_ITYPE_IMM (-1U), /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
ENCODE_STYPE_IMM (-1U), /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ 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 */
+ true, /* partial_inplace */
0, /* src_mask */
ENCODE_UTYPE_IMM (-1U), /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
ENCODE_ITYPE_IMM (-1U), /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
ENCODE_STYPE_IMM (-1U), /* dst_mask */
- FALSE), /* pcrel_offset */
+ false), /* pcrel_offset */
/* TLS LE thread pointer usage. May be relaxed. */
HOWTO (R_RISCV_TPREL_ADD, /* type */
0, /* rightshift */
- 2, /* size */
- 32, /* bitsize */
- FALSE, /* pc_relative */
+ 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 */
- TRUE, /* partial_inplace */
+ false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
- MINUS_ONE, /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
- MINUS_ONE, /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
- MINUS_ONE, /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
MINUS_ONE, /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
- MINUS_ONE, /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
- MINUS_ONE, /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
- MINUS_ONE, /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
MINUS_ONE, /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
NULL, /* special_function */
"R_RISCV_GNU_VTINHERIT", /* name */
- FALSE, /* partial_inplace */
+ false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
- 2, /* size */
+ 3, /* size */
0, /* bitsize */
- FALSE, /* pc_relative */
+ false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_RISCV_ALIGN", /* name */
- FALSE, /* partial_inplace */
+ false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
- TRUE), /* pcrel_offset */
+ false), /* pcrel_offset */
/* 8-bit PC-relative branch offset. */
HOWTO (R_RISCV_RVC_BRANCH, /* type */
0, /* rightshift */
- 2, /* size */
- 32, /* bitsize */
- TRUE, /* pc_relative */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
- ENCODE_RVC_B_IMM (-1U), /* dst_mask */
- TRUE), /* pcrel_offset */
+ 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 */
- 32, /* bitsize */
- TRUE, /* pc_relative */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
- ENCODE_RVC_J_IMM (-1U), /* dst_mask */
- TRUE), /* pcrel_offset */
+ 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 */
- 32, /* bitsize */
- FALSE, /* pc_relative */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
- ENCODE_RVC_IMM (-1U), /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
ENCODE_ITYPE_IMM (-1U), /* dst_mask */
- FALSE), /* pcrel_offset */
+ false), /* pcrel_offset */
/* GP-relative store. */
HOWTO (R_RISCV_GPREL_S, /* type */
0, /* rightshift */
2, /* size */
32, /* bitsize */
- FALSE, /* pc_relative */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
ENCODE_STYPE_IMM (-1U), /* dst_mask */
- FALSE), /* pcrel_offset */
+ false), /* pcrel_offset */
/* TP-relative TLS LE load. */
HOWTO (R_RISCV_TPREL_I, /* type */
0, /* rightshift */
2, /* size */
32, /* bitsize */
- FALSE, /* pc_relative */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
ENCODE_ITYPE_IMM (-1U), /* dst_mask */
- FALSE), /* pcrel_offset */
+ false), /* pcrel_offset */
/* TP-relative TLS LE store. */
HOWTO (R_RISCV_TPREL_S, /* type */
0, /* rightshift */
2, /* size */
32, /* bitsize */
- FALSE, /* pc_relative */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
ENCODE_STYPE_IMM (-1U), /* dst_mask */
- FALSE), /* pcrel_offset */
+ false), /* pcrel_offset */
/* The paired relocation may be relaxed. */
HOWTO (R_RISCV_RELAX, /* type */
0, /* rightshift */
3, /* size */
0, /* bitsize */
- FALSE, /* pc_relative */
+ false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_RISCV_RELAX", /* name */
- FALSE, /* partial_inplace */
+ false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
0x3f, /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_RISCV_SET6", /* name */
- FALSE, /* partial_inplace */
+ false, /* partial_inplace */
0, /* src_mask */
0x3f, /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_RISCV_SET8", /* name */
- FALSE, /* partial_inplace */
+ false, /* partial_inplace */
0, /* src_mask */
- MINUS_ONE, /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_RISCV_SET16", /* name */
- FALSE, /* partial_inplace */
+ false, /* partial_inplace */
0, /* src_mask */
- MINUS_ONE, /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_RISCV_SET32", /* name */
- FALSE, /* partial_inplace */
+ false, /* partial_inplace */
0, /* src_mask */
- MINUS_ONE, /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ 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 */
+ false, /* partial_inplace */
0, /* src_mask */
- MINUS_ONE, /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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;
return bfd_reloc_ok;
}
-/* Parsing subset version.
+#define RISCV_UNKNOWN_VERSION -1
+
+/* Array is used to compare the orders of all extensions quickly.
+
+ Zero value: Preserved keyword.
+ Negative value: Prefixed keyword (s, h, x, z).
+ Positive value: Standard extension. */
+static int riscv_ext_order[26] = {0};
+
+/* 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`. */
+
+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;
+
+ if (order1 == order2 && order1 < 0)
+ {
+ /* Compare the standard addition z extensions. */
+ if (*subset1 == '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;
+
+ 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 extension from ISA string to the last of the subset list. */
+
+void
+riscv_add_subset (riscv_subset_list_t *subset_list,
+ const char *subset,
+ int major,
+ int minor)
+{
+ riscv_subset_t *s = xmalloc (sizeof *s);
+
+ if (subset_list->head == NULL)
+ subset_list->head = s;
+
+ s->name = xstrdup (subset);
+ s->major_version = major;
+ s->minor_version = minor;
+ s->next = NULL;
+
+ if (subset_list->tail != NULL)
+ subset_list->tail->next = s;
+ subset_list->tail = s;
+}
+
+/* Add the implicit extension to the subset list. Search the
+ list first, and then find the right place to add. */
+
+static void
+riscv_add_implicit_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;
+ }
+}
+
+/* These extensions are added to the subset list for special purposes,
+ with the explicit versions or the RISCV_UNKNOWN_VERSION versions.
+ Therefore, we won't output them to the output ISA string in the
+ riscv_arch_str1, if the versions are unknown. */
+
+static bool
+riscv_ext_dont_care_version (const char *subset)
+{
+ if (strcmp (subset, "g") == 0
+ || strcmp (subset, "zicsr") == 0
+ || strcmp (subset, "zifencei") == 0)
+ return true;
+ return false;
+}
+
+/* We have to add all extensions from ISA string first, and then start to
+ add their implicit extensions. The extensions from ISA string must be
+ set in order, so we can add them to the last of the subset list
+ directly, without searching.
+
+ 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)
+ && rps->get_default_version != NULL)
+ rps->get_default_version (subset, &major_version, &minor_version);
+
+ if (!riscv_ext_dont_care_version (subset)
+ && (major_version == RISCV_UNKNOWN_VERSION
+ || minor_version == RISCV_UNKNOWN_VERSION))
+ {
+ /* We only add the implicit extension if it is supported in the
+ chosen ISA spec. */
+ if (implicit)
+ return;
+
+ 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;
+ }
+
+ if (!implicit)
+ riscv_add_subset (rps->subset_list, subset,
+ major_version, minor_version);
+ else
+ riscv_add_implicit_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 subset.
- `march`: Full arch string.
+ `rps`: Hooks and status for parsing extensions.
+ `march`: Full ISA string.
`p`: Curent parsing position.
- `major_version`: Parsing result of major version, using
- default_major_version if version is not present in arch string.
- `minor_version`: Parsing result of minor version, set to 0 if version is
- not present in arch string, but set to `default_minor_version` if
- `major_version` using default_major_version.
- `default_major_version`: Default major version.
- `default_minor_version`: Default minor version.
- `std_ext_p`: True if parsing std extension. */
+ `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 *march,
const char *p,
- unsigned *major_version,
- unsigned *minor_version,
- unsigned default_major_version,
- unsigned default_minor_version,
- bfd_boolean std_ext_p)
+ int *major_version,
+ int *minor_version,
+ bool std_ext_p)
{
- bfd_boolean major_p = TRUE;
- unsigned version = 0;
- unsigned major = 0;
- unsigned minor = 0;
+ bool major_p = true;
+ int version = 0;
char np;
- for (;*p; ++p)
+ *major_version = 0;
+ *minor_version = 0;
+ for (; *p; ++p)
{
if (*p == 'p')
{
}
else
{
- rps->error_handler ("-march=%s: Expect number after `%dp'.",
- march, version);
+ rps->error_handler
+ (_("-march=%s: expect number after `%dp'"),
+ march, version);
return NULL;
}
}
- major = version;
- major_p = FALSE;
+ *major_version = version;
+ major_p = false;
version = 0;
}
else if (ISDIGIT (*p))
}
if (major_p)
- major = version;
+ *major_version = version;
else
- minor = version;
+ *minor_version = version;
- if (major == 0 && minor == 0)
- {
- /* We don't found any version string, use default version. */
- *major_version = default_major_version;
- *minor_version = default_minor_version;
- }
- else
+ /* We can not find any version in string. */
+ if (*major_version == 0 && *minor_version == 0)
{
- *major_version = major;
- *minor_version = minor;
+ *major_version = RISCV_UNKNOWN_VERSION;
+ *minor_version = RISCV_UNKNOWN_VERSION;
}
+
return p;
}
Points to the end of extensions.
Arguments:
- `rps`: Hooks and status for parsing subset.
- `march`: Full arch string.
+ `rps`: Hooks and status for parsing extensions.
+ `march`: Full ISA string.
`p`: Curent parsing position. */
static const char *
riscv_parse_std_ext (riscv_parse_subset_t *rps,
- const char *march, const char *p)
+ const char *march,
+ const char *p)
{
const char *all_std_exts = riscv_supported_std_ext ();
const char *std_exts = all_std_exts;
-
- unsigned major_version = 0;
- unsigned minor_version = 0;
- char std_ext = '\0';
+ int major_version;
+ int minor_version;
+ char subset[2] = {0, 0};
/* First letter must start with i, e or g. */
switch (*p)
{
case 'i':
- p++;
- p = riscv_parsing_subset_version (
- rps,
- march,
- p, &major_version, &minor_version,
- /* default_major_version= */ 2,
- /* default_minor_version= */ 0,
- /* std_ext_p= */TRUE);
- riscv_add_subset (rps->subset_list, "i", major_version, minor_version);
+ p = riscv_parsing_subset_version (rps, march, ++p,
+ &major_version,
+ &minor_version, true);
+ riscv_parse_add_subset (rps, "i",
+ major_version,
+ minor_version, false);
break;
case 'e':
- p++;
- p = riscv_parsing_subset_version (
- rps,
- march,
- p, &major_version, &minor_version,
- /* default_major_version= */ 1,
- /* default_minor_version= */ 9,
- /* std_ext_p= */TRUE);
-
- riscv_add_subset (rps->subset_list, "e", major_version, minor_version);
- riscv_add_subset (rps->subset_list, "i", 2, 0);
+ p = riscv_parsing_subset_version (rps, march, ++p,
+ &major_version,
+ &minor_version, true);
+ riscv_parse_add_subset (rps, "e",
+ major_version,
+ minor_version, false);
+ /* i-ext must be enabled. */
+ riscv_parse_add_subset (rps, "i",
+ RISCV_UNKNOWN_VERSION,
+ RISCV_UNKNOWN_VERSION, false);
if (*rps->xlen > 32)
{
- rps->error_handler ("-march=%s: rv%de is not a valid base ISA",
- march, *rps->xlen);
+ rps->error_handler
+ (_("-march=%s: rv%de is not a valid base ISA"),
+ march, *rps->xlen);
return NULL;
}
-
break;
case 'g':
- p++;
- p = riscv_parsing_subset_version (
- rps,
- march,
- p, &major_version, &minor_version,
- /* default_major_version= */ 2,
- /* default_minor_version= */ 0,
- /* std_ext_p= */TRUE);
- riscv_add_subset (rps->subset_list, "i", major_version, minor_version);
-
+ p = riscv_parsing_subset_version (rps, march, ++p,
+ &major_version,
+ &minor_version, true);
+ /* i-ext must be enabled. */
+ riscv_parse_add_subset (rps, "i",
+ RISCV_UNKNOWN_VERSION,
+ RISCV_UNKNOWN_VERSION, false);
+ /* g-ext is used to add the implicit extensions, but will
+ not be output to the ISA string. */
+ riscv_parse_add_subset (rps, "g",
+ major_version,
+ minor_version, false);
for ( ; *std_exts != 'q'; std_exts++)
{
- const char subset[] = {*std_exts, '\0'};
- riscv_add_subset (
- rps->subset_list, subset, major_version, minor_version);
+ subset[0] = *std_exts;
+ riscv_parse_add_subset (rps, subset,
+ RISCV_UNKNOWN_VERSION,
+ RISCV_UNKNOWN_VERSION, false);
}
break;
default:
- rps->error_handler (
- "-march=%s: first ISA subset must be `e', `i' or `g'", march);
+ rps->error_handler
+ (_("-march=%s: first ISA extension must be `e', `i' or `g'"),
+ march);
return NULL;
}
- while (*p)
+ while (p != NULL && *p != '\0')
{
- char subset[2] = {0, 0};
-
- if (*p == 'x' || *p == 's')
+ if (*p == 'x' || *p == 's' || *p == 'h' || *p == 'z')
break;
if (*p == '_')
continue;
}
- std_ext = *p;
-
/* Checking canonical order. */
- while (*std_exts && std_ext != *std_exts) std_exts++;
+ char std_ext = *p;
+ while (*std_exts && std_ext != *std_exts)
+ std_exts++;
if (std_ext != *std_exts)
{
if (strchr (all_std_exts, std_ext) == NULL)
- rps->error_handler (
- "-march=%s: unsupported ISA subset `%c'", march, *p);
+ rps->error_handler
+ (_("-march=%s: unknown standard ISA extension `%c'"),
+ march, std_ext);
else
- rps->error_handler (
- "-march=%s: ISA string is not in canonical order. `%c'",
- march, *p);
+ rps->error_handler
+ (_("-march=%s: standard ISA extension `%c' is not "
+ "in canonical order"), march, std_ext);
return NULL;
}
std_exts++;
+ subset[0] = std_ext;
+ p = riscv_parsing_subset_version (rps, march, ++p,
+ &major_version,
+ &minor_version, true);
+ riscv_parse_add_subset (rps, subset,
+ major_version,
+ minor_version, false);
+ }
- p++;
- p = riscv_parsing_subset_version (
- rps,
- march,
- p, &major_version, &minor_version,
- /* default_major_version= */ 2,
- /* default_minor_version= */ 0,
- /* std_ext_p= */TRUE);
+ return p;
+}
- subset[0] = std_ext;
+/* Classify ARCH into one of riscv_isa_ext_class_t. */
- riscv_add_subset (rps->subset_list, subset, major_version, minor_version);
+riscv_isa_ext_class_t
+riscv_get_prefix_class (const char *arch)
+{
+ switch (*arch)
+ {
+ case 's': return RV_ISA_CLASS_S;
+ case 'h': return RV_ISA_CLASS_H;
+ case 'x': return RV_ISA_CLASS_X;
+ case 'z': return RV_ISA_CLASS_Z;
+ default: return RV_ISA_CLASS_UNKNOWN;
}
- return p;
}
-/* Parsing function for non-standard and supervisor extensions.
+/* Structure describing parameters to use when parsing a particular
+ riscv_isa_ext_class_t. One of these should be provided for each
+ possible class, except RV_ISA_CLASS_UNKNOWN. */
+typedef struct riscv_parse_config
+{
+ /* Class of the extension. */
+ riscv_isa_ext_class_t class;
+
+ /* Prefix string for error printing and internal parser usage. */
+ const char *prefix;
+
+ /* Predicate which is used for checking whether this is a "known"
+ extension. For 'x', it always returns true since they are by
+ definition non-standard and cannot be known. */
+ bool (*ext_valid_p) (const char *);
+} riscv_parse_config_t;
+
+/* Parsing function for prefixed extensions.
Return Value:
- Points to the end of extensions.
+ Points to the end of extension.
Arguments:
- `rps`: Hooks and status for parsing subset.
- `march`: Full arch string.
+ `rps`: Hooks and status for parsing extensions.
+ `march`: Full ISA string.
`p`: Curent parsing position.
- `ext_type`: What kind of extensions, 'x', 's' or 'sx'.
- `ext_type_str`: Full name for kind of extension. */
+ `config`: What class and predicate function to use for the
+ extension. */
static const char *
-riscv_parse_sv_or_non_std_ext (riscv_parse_subset_t *rps,
- const char *march,
- const char *p,
- const char *ext_type,
- const char *ext_type_str)
+riscv_parse_prefixed_ext (riscv_parse_subset_t *rps,
+ const char *march,
+ const char *p,
+ const riscv_parse_config_t *config)
{
- unsigned major_version = 0;
- unsigned minor_version = 0;
- size_t ext_type_len = strlen (ext_type);
+ int major_version;
+ int minor_version;
+ const char *last_name;
+ riscv_isa_ext_class_t class;
while (*p)
{
continue;
}
- if (strncmp (p, ext_type, ext_type_len) != 0)
- break;
-
- /* It's non-standard supervisor extension if it prefix with sx. */
- if ((ext_type[0] == 's') && (ext_type_len == 1)
- && (*(p + 1) == 'x'))
+ /* Assert that the current extension specifier matches our parsing
+ class. */
+ class = riscv_get_prefix_class (p);
+ if (class != config->class
+ || class == RV_ISA_CLASS_UNKNOWN)
break;
char *subset = xstrdup (p);
;
end_of_version =
- riscv_parsing_subset_version (
- rps,
- march,
- q, &major_version, &minor_version,
- /* default_major_version= */ 2,
- /* default_minor_version= */ 0,
- /* std_ext_p= */FALSE);
-
+ riscv_parsing_subset_version (rps, march, q,
+ &major_version,
+ &minor_version, false);
*q = '\0';
- riscv_add_subset (rps->subset_list, subset, major_version, minor_version);
- free (subset);
+ if (end_of_version == NULL)
+ {
+ free (subset);
+ return NULL;
+ }
+
+ /* Check that the prefix extension is known.
+ For 'x', anything goes but it cannot simply be 'x'.
+ For 's', it must be known from a list and cannot simply be 's'.
+ For 'h', it must be known from a list and cannot simply be 'h'.
+ For 'z', it must be known from a list and cannot simply be 'z'. */
+
+ /* Check that the extension name is well-formed. */
+ if (!config->ext_valid_p (subset))
+ {
+ rps->error_handler
+ (_("-march=%s: unknown %s ISA extension `%s'"),
+ march, config->prefix, 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
+ (_("-march=%s: duplicate %s ISA extension `%s'"),
+ march, config->prefix, subset);
+ free (subset);
+ return NULL;
+ }
+
+ /* Check that the extension is in alphabetical order. */
+ if (riscv_compare_subsets (last_name, subset) > 0)
+ {
+ rps->error_handler
+ (_("-march=%s: %s ISA extension `%s' is not in alphabetical "
+ "order. It must come before `%s'"),
+ march, config->prefix, 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 ("-march=%s: %s must seperate with _",
- march, ext_type_str);
+ rps->error_handler
+ (_("-march=%s: %s ISA extension must separate with _"),
+ march, config->prefix);
return NULL;
}
}
return p;
}
-/* Function for parsing arch string.
+/* Lists of prefixed class extensions that binutils should know about.
+ Whether or not a particular entry is in these lists will dictate if
+ gas/ld will accept its presence in the architecture string.
+
+ Please add the extensions to the lists in lower case. However, keep
+ these subsets in alphabetical order in these tables is recommended,
+ although there is no impact on the current implementation. */
+
+static const char * const riscv_std_z_ext_strtab[] =
+{
+ "zba", "zbb", "zbc", "zicsr", "zifencei", "zihintpause", NULL
+};
+
+static const char * const riscv_std_s_ext_strtab[] =
+{
+ NULL
+};
+
+static const char * const riscv_std_h_ext_strtab[] =
+{
+ NULL
+};
+
+/* For the extension `ext`, search through the list of known extensions
+ `known_exts` for a match, and return TRUE if found. */
+
+static bool
+riscv_multi_letter_ext_valid_p (const char *ext,
+ const char *const *known_exts)
+{
+ size_t i;
+
+ for (i = 0; known_exts[i]; ++i)
+ if (!strcmp (ext, known_exts[i]))
+ return true;
+
+ return false;
+}
+
+/* Predicator function for x-prefixed extensions.
+ Anything goes, except the literal 'x'. */
+
+static bool
+riscv_ext_x_valid_p (const char *arg)
+{
+ if (!strcasecmp (arg, "x"))
+ return false;
+
+ return true;
+}
+
+/* Predicator functions for z-prefixed extensions.
+ Only known z-extensions are permitted. */
+
+static bool
+riscv_ext_z_valid_p (const char *arg)
+{
+ return riscv_multi_letter_ext_valid_p (arg, riscv_std_z_ext_strtab);
+}
+
+/* Predicator function for 's' prefixed extensions.
+ Only known s-extensions are permitted. */
+
+static bool
+riscv_ext_s_valid_p (const char *arg)
+{
+ return riscv_multi_letter_ext_valid_p (arg, riscv_std_s_ext_strtab);
+}
+
+/* Predicator function for 'h' prefixed extensions.
+ Only known h-extensions are permitted. */
+
+static bool
+riscv_ext_h_valid_p (const char *arg)
+{
+ return riscv_multi_letter_ext_valid_p (arg, riscv_std_h_ext_strtab);
+}
+
+/* Parsing order of the prefixed extensions that is specified by
+ the ISA spec. */
+static const riscv_parse_config_t parse_config[] =
+{
+ {RV_ISA_CLASS_S, "s", riscv_ext_s_valid_p},
+ {RV_ISA_CLASS_H, "h", riscv_ext_h_valid_p},
+ {RV_ISA_CLASS_Z, "z", riscv_ext_z_valid_p},
+ {RV_ISA_CLASS_X, "x", riscv_ext_x_valid_p},
+ {RV_ISA_CLASS_UNKNOWN, NULL, NULL}
+};
+
+/* Init the riscv_ext_order array. */
+
+static void
+riscv_init_ext_order (void)
+{
+ static bool inited = false;
+ const char *std_base_exts = "eig";
+ const char *std_remain_exts = riscv_supported_std_ext ();
+ const char *ext;
+ unsigned int i;
+ int order;
+
+ if (inited)
+ return;
+
+ /* The orders of all standard extensions are positive. */
+ order = 1;
+
+ /* Init the standard base extensions first. */
+ for (ext = std_base_exts; *ext; ext++)
+ riscv_ext_order[(*ext - 'a')] = order++;
+
+ /* Init the standard remaining extensions. */
+ for (ext = std_remain_exts; *ext; ext++)
+ riscv_ext_order[(*ext - 'a')] = order++;
+
+ /* Init the order for prefixed keywords. The orders are negative. */
+ order = -1;
+ for (i = 0; parse_config[i].class != RV_ISA_CLASS_UNKNOWN; i++)
+ {
+ ext = parse_config[i].prefix;
+ riscv_ext_order[(*ext - 'a')] = order--;
+ }
+
+ inited = true;
+}
+
+/* Add the implicit extensions. */
+
+static void
+riscv_parse_add_implicit_subsets (riscv_parse_subset_t *rps)
+{
+ riscv_subset_t *subset = NULL;
+
+ /* Add the zicsr and zifencei only when the i's version less than 2.1. */
+ if ((riscv_lookup_subset (rps->subset_list, "i", &subset))
+ && (subset->major_version < 2
+ || (subset->major_version == 2
+ && subset->minor_version < 1)))
+ {
+ riscv_parse_add_subset (rps, "zicsr",
+ RISCV_UNKNOWN_VERSION,
+ RISCV_UNKNOWN_VERSION, true);
+ riscv_parse_add_subset (rps, "zifencei",
+ RISCV_UNKNOWN_VERSION,
+ RISCV_UNKNOWN_VERSION, true);
+ }
+
+ if ((riscv_lookup_subset (rps->subset_list, "q", &subset)))
+ {
+ riscv_parse_add_subset (rps, "d",
+ RISCV_UNKNOWN_VERSION,
+ RISCV_UNKNOWN_VERSION, true);
+ riscv_parse_add_subset (rps, "f",
+ RISCV_UNKNOWN_VERSION,
+ RISCV_UNKNOWN_VERSION, true);
+ riscv_parse_add_subset (rps, "zicsr",
+ RISCV_UNKNOWN_VERSION,
+ RISCV_UNKNOWN_VERSION, true);
+ }
+ else if ((riscv_lookup_subset (rps->subset_list, "d", &subset)))
+ {
+ riscv_parse_add_subset (rps, "f",
+ RISCV_UNKNOWN_VERSION,
+ RISCV_UNKNOWN_VERSION, true);
+ riscv_parse_add_subset (rps, "zicsr",
+ RISCV_UNKNOWN_VERSION,
+ RISCV_UNKNOWN_VERSION, true);
+ }
+ else if ((riscv_lookup_subset (rps->subset_list, "f", &subset)))
+ riscv_parse_add_subset (rps, "zicsr",
+ RISCV_UNKNOWN_VERSION,
+ RISCV_UNKNOWN_VERSION, true);
+
+ if ((riscv_lookup_subset (rps->subset_list, "g", &subset)))
+ {
+ riscv_parse_add_subset (rps, "zicsr",
+ RISCV_UNKNOWN_VERSION,
+ RISCV_UNKNOWN_VERSION, true);
+ riscv_parse_add_subset (rps, "zifencei",
+ RISCV_UNKNOWN_VERSION,
+ RISCV_UNKNOWN_VERSION, true);
+ }
+}
+
+/* Function for parsing ISA string.
Return Value:
Return TRUE on success.
Arguments:
- `rps`: Hooks and status for parsing subset.
- `arch`: Arch string. */
+ `rps`: Hooks and status for parsing extensions.
+ `arch`: Full ISA string. */
-bfd_boolean
+bool
riscv_parse_subset (riscv_parse_subset_t *rps,
const char *arch)
{
- const char *p = arch;
+ riscv_subset_t *subset = NULL;
+ const char *p;
+ size_t i;
+ bool no_conflict = true;
+
+ for (p = arch; *p != '\0'; p++)
+ {
+ if (ISUPPER (*p))
+ {
+ rps->error_handler
+ (_("-march=%s: ISA string cannot contain uppercase letters"),
+ arch);
+ return false;
+ }
+ }
- if (strncmp (p, "rv32", 4) == 0)
+ p = arch;
+ if (startswith (p, "rv32"))
{
*rps->xlen = 32;
p += 4;
}
- else if (strncmp (p, "rv64", 4) == 0)
+ else if (startswith (p, "rv64"))
{
*rps->xlen = 64;
p += 4;
}
else
{
- rps->error_handler ("-march=%s: ISA string must begin with rv32 or rv64",
- arch);
- return FALSE;
+ /* ISA string shouldn't be NULL or empty here. However,
+ it might be empty only when we failed to merge the ISA
+ string in the riscv_merge_attributes. 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 (
+ _("-march=%s: ISA string must begin with rv32 or rv64"),
+ arch);
+ return false;
}
+ /* Init the riscv_ext_order array to compare the order of extensions
+ quickly. */
+ riscv_init_ext_order ();
+
/* Parsing standard extension. */
p = riscv_parse_std_ext (rps, arch, p);
if (p == NULL)
- return FALSE;
-
- /* Parsing non-standard extension. */
- p = riscv_parse_sv_or_non_std_ext (
- rps, arch, p, "x", "non-standard extension");
-
- if (p == NULL)
- return FALSE;
-
- /* Parsing supervisor extension. */
- p = riscv_parse_sv_or_non_std_ext (
- rps, arch, p, "s", "supervisor extension");
+ return false;
- if (p == NULL)
- return FALSE;
-
- /* Parsing non-standard supervisor extension. */
- p = riscv_parse_sv_or_non_std_ext (
- rps, arch, p, "sx", "non-standard supervisor extension");
+ /* Parse the different classes of extensions in the specified order. */
+ for (i = 0; i < ARRAY_SIZE (parse_config); ++i)
+ {
+ p = riscv_parse_prefixed_ext (rps, arch, p, &parse_config[i]);
- if (p == NULL)
- return FALSE;
+ if (p == NULL)
+ return false;
+ }
if (*p != '\0')
{
- rps->error_handler ("-march=%s: unexpected ISA string at end: %s",
+ rps->error_handler (_("-march=%s: unexpected ISA string at end: %s"),
arch, p);
- return FALSE;
+ return false;
}
- if (riscv_lookup_subset (rps->subset_list, "e")
- && riscv_lookup_subset (rps->subset_list, "f"))
- {
- rps->error_handler ("-march=%s: rv32e does not support the `f' extension",
- arch);
- return FALSE;
- }
-
- if (riscv_lookup_subset (rps->subset_list, "d")
- && !riscv_lookup_subset (rps->subset_list, "f"))
- {
- rps->error_handler ("-march=%s: `d' extension requires `f' extension",
- arch);
- return FALSE;
- }
+ /* Finally add implicit extensions according to the current
+ extensions. */
+ riscv_parse_add_implicit_subsets (rps);
- if (riscv_lookup_subset (rps->subset_list, "q")
- && !riscv_lookup_subset (rps->subset_list, "d"))
+ /* Check the conflicts. */
+ if (riscv_lookup_subset (rps->subset_list, "e", &subset)
+ && riscv_lookup_subset (rps->subset_list, "f", &subset))
{
- rps->error_handler ("-march=%s: `q' extension requires `d' extension",
- arch);
- return FALSE;
+ rps->error_handler
+ (_("-march=%s: rv32e does not support the `f' extension"),
+ arch);
+ no_conflict = false;
}
-
- if (riscv_lookup_subset (rps->subset_list, "q") && *rps->xlen < 64)
+ if (riscv_lookup_subset (rps->subset_list, "q", &subset)
+ && *rps->xlen < 64)
{
- rps->error_handler ("-march=%s: rv32 does not support the `q' extension",
- arch);
- return FALSE;
+ rps->error_handler
+ (_("-march=%s: rv32 does not support the `q' extension"),
+ arch);
+ no_conflict = false;
}
- return TRUE;
+ return no_conflict;
}
-/* Add new subset to list. */
+/* Return the number of digits for the input. */
-void
-riscv_add_subset (riscv_subset_list_t *subset_list,
- const char *subset,
- int major, int minor)
+size_t
+riscv_estimate_digit (unsigned num)
{
- riscv_subset_t *s = xmalloc (sizeof *s);
+ size_t digit = 0;
+ if (num == 0)
+ return 1;
- if (subset_list->head == NULL)
- subset_list->head = s;
+ for (digit = 0; num ; num /= 10)
+ digit++;
- s->name = xstrdup (subset);
- s->major_version = major;
- s->minor_version = minor;
- s->next = NULL;
-
- if (subset_list->tail != NULL)
- subset_list->tail->next = s;
-
- subset_list->tail = s;
+ return digit;
}
-/* Find subset in list without version checking, return NULL if not found. */
+/* Auxiliary function to estimate string length of subset list. */
-riscv_subset_t *
-riscv_lookup_subset (const riscv_subset_list_t *subset_list,
- const char *subset)
+static size_t
+riscv_estimate_arch_strlen1 (const riscv_subset_t *subset)
{
- return riscv_lookup_subset_version (
- subset_list, subset,
- RISCV_DONT_CARE_VERSION,
- RISCV_DONT_CARE_VERSION);
+ 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. */;
}
-/* Find subset in list with version checking, return NULL if not found. */
+/* Estimate the string length of this subset list. */
-riscv_subset_t *
-riscv_lookup_subset_version (const riscv_subset_list_t *subset_list,
- const char *subset,
- int major, int minor)
+static size_t
+riscv_estimate_arch_strlen (const riscv_subset_list_t *subset_list)
{
- riscv_subset_t *s;
-
- for (s = subset_list->head; s != NULL; s = s->next)
- if (strcasecmp (s->name, subset) == 0)
- {
- if ((major != RISCV_DONT_CARE_VERSION)
- && (s->major_version != major))
- return NULL;
-
- if ((minor != RISCV_DONT_CARE_VERSION)
- && (s->minor_version != minor))
- return NULL;
+ return riscv_estimate_arch_strlen1 (subset_list->head);
+}
- return s;
- }
+/* Auxiliary function to convert subset info to string. */
- return NULL;
+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);
}
-/* Release subset list. */
+/* Convert subset information into string with explicit versions. */
-void
-riscv_release_subset_list (riscv_subset_list_t *subset_list)
+char *
+riscv_arch_str (unsigned xlen, const riscv_subset_list_t *subset)
{
- 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;
- }
+ size_t arch_str_len = riscv_estimate_arch_strlen (subset);
+ char *attr_str = xmalloc (arch_str_len);
+ char *buf = xmalloc (arch_str_len);
- subset_list->tail = NULL;
+ 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;
}