+ /* We may need to reference low addreses in PC-relative modes even when the
+ * PC is far away from these addresses. For example, undefweak references
+ * need to produce the address 0 when linked. As 0 is far from the arbitrary
+ * addresses that we can link PC-relative programs at, the linker can't
+ * actually relocate references to those symbols. In order to allow these
+ * programs to work we simply convert the PC-relative auipc sequences to
+ * 0-relative lui sequences. */
+ if (bfd_link_pic (info))
+ return FALSE;
+
+ /* If it's possible to reference the symbol using auipc we do so, as that's
+ * more in the spirit of the PC-relative relocations we're processing. */
+ bfd_vma offset = addr - pc;
+ if (ARCH_SIZE == 32 || VALID_UTYPE_IMM (RISCV_CONST_HIGH_PART (offset)))
+ return FALSE;
+
+ /* If it's impossible to reference this with a LUI-based offset then don't
+ * bother to convert it at all so users still see the PC-relative relocation
+ * in the truncation message. */
+ if (ARCH_SIZE > 32 && !VALID_UTYPE_IMM (RISCV_CONST_HIGH_PART (addr)))
+ return FALSE;
+
+ rel->r_info = ELFNN_R_INFO(addr, R_RISCV_HI20);
+
+ bfd_vma insn = bfd_get(howto->bitsize, input_bfd, contents + rel->r_offset);
+ insn = (insn & ~MASK_AUIPC) | MATCH_LUI;
+ bfd_put(howto->bitsize, input_bfd, insn, contents + rel->r_offset);
+ return TRUE;
+}
+
+static bfd_boolean
+riscv_record_pcrel_hi_reloc (riscv_pcrel_relocs *p, bfd_vma addr,
+ bfd_vma value, bfd_boolean absolute)
+{
+ bfd_vma offset = absolute ? value : value - addr;
+ riscv_pcrel_hi_reloc entry = {addr, offset};