/* RISC-V-specific support for ELF.
- Copyright 2011-2016 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 "opcode/riscv.h"
#include "libiberty.h"
#include "elfxx-riscv.h"
-#include <stdint.h>
+#include "safe-ctype.h"
+#include "cpu-riscv.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[] =
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 */
0xffffffff, /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
0xffffffff, /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
- 0x0, /* src_mask */
- 0x0, /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ 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 */
- 0x0, /* src_mask */
- 0x0, /* dst_mask */
- FALSE), /* pcrel_offset */
+ false, /* partial_inplace */
+ 0, /* src_mask */
+ 0, /* dst_mask */
+ false), /* pcrel_offset */
/* Dynamic TLS relocations. */
HOWTO (R_RISCV_TLS_DTPMOD32, /* type */
0, /* rightshift */
- 4, /* size */
+ 2, /* size */
32, /* bitsize */
- FALSE, /* pc_relative */
+ false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
- bfd_elf_generic_reloc, /* special_function */
+ bfd_elf_generic_reloc, /* special_function */
"R_RISCV_TLS_DTPMOD32", /* name */
- FALSE, /* partial_inplace */
- MINUS_ONE, /* src_mask */
- MINUS_ONE, /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
- bfd_elf_generic_reloc, /* special_function */
+ bfd_elf_generic_reloc, /* special_function */
"R_RISCV_TLS_DTPMOD64", /* name */
- FALSE, /* partial_inplace */
- MINUS_ONE, /* src_mask */
+ 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 */
+ bfd_elf_generic_reloc, /* special_function */
"R_RISCV_TLS_DTPREL32", /* name */
- TRUE, /* partial_inplace */
- MINUS_ONE, /* src_mask */
- MINUS_ONE, /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
- bfd_elf_generic_reloc, /* special_function */
+ bfd_elf_generic_reloc, /* special_function */
"R_RISCV_TLS_DTPREL64", /* name */
- TRUE, /* partial_inplace */
- MINUS_ONE, /* src_mask */
+ 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 */
+ bfd_elf_generic_reloc, /* special_function */
"R_RISCV_TLS_TPREL32", /* name */
- FALSE, /* partial_inplace */
- MINUS_ONE, /* src_mask */
- MINUS_ONE, /* dst_mask */
- FALSE), /* pcrel_offset */
+ 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 */
+ false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
- bfd_elf_generic_reloc, /* special_function */
+ bfd_elf_generic_reloc, /* special_function */
"R_RISCV_TLS_TPREL64", /* name */
- FALSE, /* partial_inplace */
- MINUS_ONE, /* src_mask */
+ 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 */
- /* This needs complex overflow
- detection, because the upper 36
- bits must match the PC + 4. */
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 */
- /* 32-bit PC-relative function call (AUIPC/JALR). */
+ /* 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. */
+ /* 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 */
- bfd_elf_generic_reloc, /* special_function */
+ 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 */
- bfd_elf_generic_reloc, /* special_function */
+ 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 */
- bfd_elf_generic_reloc, /* special_function */
+ 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 */
- bfd_elf_generic_reloc, /* special_function */
+ 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 */
- bfd_elf_generic_reloc, /* special_function */
+ 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 */
- bfd_elf_generic_reloc, /* special_function */
+ 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 */
- bfd_elf_generic_reloc, /* special_function */
+ 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 */
- bfd_elf_generic_reloc, /* special_function */
+ 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 */
- /* This needs complex overflow
- detection, because the upper 36
- bits must match the PC + 4. */
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 */
- /* High 12 bits of 32-bit load or add. */
+ /* 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 */
- /* High 12 bits of 32-bit store. */
+ /* 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 */
+
+ /* 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 */
+ 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;
{ 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_elf_rtype_to_howto (unsigned int r_type)
+riscv_elf_rtype_to_howto (bfd *abfd, unsigned int r_type)
{
if (r_type >= ARRAY_SIZE (howto_table))
{
- (*_bfd_error_handler) (_("unrecognized relocation (0x%x)"), r_type);
+ (*_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_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;
+}
+
+#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 extensions.
+ `march`: 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 *march,
+ 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
+ (_("-march=%s: expect number after `%dp'"),
+ march, 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;
+}
+
+/* Return string which contain all supported standard extensions in
+ canonical order. */
+
+const char *
+riscv_supported_std_ext (void)
+{
+ return "mafdqlcbjtpvn";
+}
+
+/* Parsing function for standard extensions.
+
+ Return Value:
+ Points to the end of extensions.
+
+ Arguments:
+ `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 *all_std_exts = riscv_supported_std_ext ();
+ const char *std_exts = all_std_exts;
+ 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 = 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 = 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);
+ return NULL;
+ }
+ break;
+
+ case 'g':
+ 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++)
+ {
+ 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 extension must be `e', `i' or `g'"),
+ march);
+ return NULL;
+ }
+
+ while (p != NULL && *p != '\0')
+ {
+ if (*p == 'x' || *p == 's' || *p == 'h' || *p == 'z')
+ break;
+
+ if (*p == '_')
+ {
+ p++;
+ continue;
+ }
+
+ /* Checking canonical order. */
+ 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: unknown standard ISA extension `%c'"),
+ march, std_ext);
+ else
+ 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);
+ }
+
+ return p;
+}
+
+/* Classify ARCH into one of riscv_isa_ext_class_t. */
+
+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;
+ }
+}
+
+/* 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 extension.
+
+ Arguments:
+ `rps`: Hooks and status for parsing extensions.
+ `march`: Full ISA string.
+ `p`: Curent parsing position.
+ `config`: What class and predicate function to use for the
+ extension. */
+
+static const char *
+riscv_parse_prefixed_ext (riscv_parse_subset_t *rps,
+ const char *march,
+ const char *p,
+ const riscv_parse_config_t *config)
+{
+ int major_version;
+ int minor_version;
+ const char *last_name;
+ riscv_isa_ext_class_t class;
+
+ while (*p)
+ {
+ if (*p == '_')
+ {
+ p++;
+ continue;
+ }
+
+ /* 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);
+ char *q = subset;
+ const char *end_of_version;
+
+ while (*++q != '\0' && *q != '_' && !ISDIGIT (*q))
+ ;
+
+ end_of_version =
+ riscv_parsing_subset_version (rps, march, q,
+ &major_version,
+ &minor_version, false);
+ *q = '\0';
+
+ 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 ISA extension must separate with _"),
+ march, config->prefix);
+ return NULL;
+ }
+ }
+
+ return p;
+}
+
+/* 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 extensions.
+ `arch`: Full ISA string. */
+
+bool
+riscv_parse_subset (riscv_parse_subset_t *rps,
+ const char *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;
+ }
+ }
+
+ 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. 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;
+
+ /* 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 != '\0')
+ {
+ rps->error_handler (_("-march=%s: unexpected ISA string at end: %s"),
+ arch, p);
+ return false;
+ }
+
+ /* Finally add implicit extensions according to the current
+ extensions. */
+ riscv_parse_add_implicit_subsets (rps);
+
+ /* 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: rv32e does not support the `f' extension"),
+ arch);
+ no_conflict = false;
+ }
+ 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);
+ no_conflict = false;
+ }
+ return no_conflict;
+}
+
+/* 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;
+}