/* RISC-V-specific support for NN-bit ELF.
- Copyright 2011-2016 Free Software Foundation, Inc.
+ Copyright (C) 2011-2017 Free Software Foundation, Inc.
Contributed by Andrew Waterman (andrew@sifive.com).
Based on TILE-Gx and MIPS targets.
#include "elf/riscv.h"
#include "opcode/riscv.h"
+/* Internal relocations used exclusively by the relaxation pass. */
+#define R_RISCV_DELETE (R_RISCV_max + 1)
+
#define ARCH_SIZE NN
#define MINUS_ONE ((bfd_vma)0 - 1)
struct elf_link_hash_table elf;
/* Short-cuts to get to dynamic linker sections. */
- asection *sdynbss;
- asection *srelbss;
asection *sdyntdata;
/* Small local sym to section mapping cache. */
struct sym_cache sym_cache;
+
+ /* The max alignment of output sections. */
+ bfd_vma max_alignment;
};
return NULL;
}
+ ret->max_alignment = (bfd_vma) -1;
return &ret->elf.root;
}
if (!_bfd_elf_create_dynamic_sections (dynobj, info))
return FALSE;
- htab->sdynbss = bfd_get_linker_section (dynobj, ".dynbss");
if (!bfd_link_pic (info))
{
- htab->srelbss = bfd_get_linker_section (dynobj, ".rela.bss");
htab->sdyntdata =
bfd_make_section_anyway_with_flags (dynobj, ".tdata.dyn",
SEC_ALLOC | SEC_THREAD_LOCAL);
}
- if (!htab->elf.splt || !htab->elf.srelplt || !htab->sdynbss
- || (!bfd_link_pic (info) && (!htab->srelbss || !htab->sdyntdata)))
+ if (!htab->elf.splt || !htab->elf.srelplt || !htab->elf.sdynbss
+ || (!bfd_link_pic (info) && (!htab->elf.srelbss || !htab->sdyntdata)))
abort ();
return TRUE;
for (rel = relocs; rel < relocs + sec->reloc_count; rel++)
{
unsigned int r_type;
- unsigned long r_symndx;
+ unsigned int r_symndx;
struct elf_link_hash_entry *h;
r_symndx = ELFNN_R_SYM (rel->r_info);
/* PR15323, ref flags aren't set for references in the same
object. */
- h->root.non_ir_ref = 1;
+ h->root.non_ir_ref_regular = 1;
}
switch (r_type)
return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym);
}
-/* Update the got entry reference counts for the section being removed. */
-
-static bfd_boolean
-riscv_elf_gc_sweep_hook (bfd *abfd,
- struct bfd_link_info *info,
- asection *sec,
- const Elf_Internal_Rela *relocs)
-{
- const Elf_Internal_Rela *rel, *relend;
- Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (abfd);
- struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (abfd);
- bfd_signed_vma *local_got_refcounts = elf_local_got_refcounts (abfd);
-
- if (bfd_link_relocatable (info))
- return TRUE;
-
- elf_section_data (sec)->local_dynrel = NULL;
-
- for (rel = relocs, relend = relocs + sec->reloc_count; rel < relend; rel++)
- {
- unsigned long r_symndx;
- struct elf_link_hash_entry *h = NULL;
-
- r_symndx = ELFNN_R_SYM (rel->r_info);
- if (r_symndx >= symtab_hdr->sh_info)
- {
- struct riscv_elf_link_hash_entry *eh;
- struct riscv_elf_dyn_relocs **pp;
- struct riscv_elf_dyn_relocs *p;
-
- h = sym_hashes[r_symndx - symtab_hdr->sh_info];
- while (h->root.type == bfd_link_hash_indirect
- || h->root.type == bfd_link_hash_warning)
- h = (struct elf_link_hash_entry *) h->root.u.i.link;
- eh = (struct riscv_elf_link_hash_entry *) h;
- for (pp = &eh->dyn_relocs; (p = *pp) != NULL; pp = &p->next)
- if (p->sec == sec)
- {
- /* Everything must go for SEC. */
- *pp = p->next;
- break;
- }
- }
-
- switch (ELFNN_R_TYPE (rel->r_info))
- {
- case R_RISCV_GOT_HI20:
- case R_RISCV_TLS_GOT_HI20:
- case R_RISCV_TLS_GD_HI20:
- if (h != NULL)
- {
- if (h->got.refcount > 0)
- h->got.refcount--;
- }
- else
- {
- if (local_got_refcounts &&
- local_got_refcounts[r_symndx] > 0)
- local_got_refcounts[r_symndx]--;
- }
- break;
-
- case R_RISCV_HI20:
- case R_RISCV_PCREL_HI20:
- case R_RISCV_COPY:
- case R_RISCV_JUMP_SLOT:
- case R_RISCV_RELATIVE:
- case R_RISCV_64:
- case R_RISCV_32:
- case R_RISCV_BRANCH:
- case R_RISCV_CALL:
- case R_RISCV_JAL:
- case R_RISCV_RVC_BRANCH:
- case R_RISCV_RVC_JUMP:
- if (bfd_link_pic (info))
- break;
- /* Fall through. */
-
- case R_RISCV_CALL_PLT:
- if (h != NULL)
- {
- if (h->plt.refcount > 0)
- h->plt.refcount--;
- }
- break;
-
- default:
- break;
- }
- }
-
- return TRUE;
-}
-
/* Adjust a symbol defined by a dynamic object and referenced by a
regular object. The current definition is in some section of the
dynamic object, but we're not including those sections. We have to
struct riscv_elf_link_hash_entry * eh;
struct riscv_elf_dyn_relocs *p;
bfd *dynobj;
- asection *s;
+ asection *s, *srel;
htab = riscv_elf_hash_table (info);
BFD_ASSERT (htab != NULL);
to copy the initial value out of the dynamic object and into the
runtime process image. We need to remember the offset into the
.rel.bss section we are going to use. */
+ if (eh->tls_type & ~GOT_NORMAL)
+ {
+ s = htab->sdyntdata;
+ srel = htab->elf.srelbss;
+ }
+ else if ((h->root.u.def.section->flags & SEC_READONLY) != 0)
+ {
+ s = htab->elf.sdynrelro;
+ srel = htab->elf.sreldynrelro;
+ }
+ else
+ {
+ s = htab->elf.sdynbss;
+ srel = htab->elf.srelbss;
+ }
if ((h->root.u.def.section->flags & SEC_ALLOC) != 0 && h->size != 0)
{
- htab->srelbss->size += sizeof (ElfNN_External_Rela);
+ srel->size += sizeof (ElfNN_External_Rela);
h->needs_copy = 1;
}
- if (eh->tls_type & ~GOT_NORMAL)
- return _bfd_elf_adjust_dynamic_copy (info, h, htab->sdyntdata);
-
- return _bfd_elf_adjust_dynamic_copy (info, h, htab->sdynbss);
+ return _bfd_elf_adjust_dynamic_copy (info, h, s);
}
/* Allocate space in .plt, .got and associated reloc sections for
if (s == htab->elf.splt
|| s == htab->elf.sgot
|| s == htab->elf.sgotplt
- || s == htab->sdynbss)
+ || s == htab->elf.sdynbss
+ || s == htab->elf.sdynrelro)
{
/* Strip this section if we don't need it; see the
comment below. */
{
struct bfd_link_hash_entry *h;
- h = bfd_link_hash_lookup (info->hash, "_gp", FALSE, FALSE, TRUE);
+ h = bfd_link_hash_lookup (info->hash, RISCV_GP_SYMBOL, FALSE, FALSE, TRUE);
if (h == NULL || h->type != bfd_link_hash_defined)
return 0;
case R_RISCV_SET8:
case R_RISCV_SET16:
case R_RISCV_SET32:
+ case R_RISCV_32_PCREL:
case R_RISCV_TLS_DTPREL32:
case R_RISCV_TLS_DTPREL64:
break;
+ case R_RISCV_DELETE:
+ return bfd_reloc_ok;
+
default:
return bfd_reloc_notsupported;
}
}
static bfd_boolean
-riscv_record_pcrel_hi_reloc (riscv_pcrel_relocs *p, bfd_vma addr, bfd_vma value)
+riscv_zero_pcrel_hi_reloc (Elf_Internal_Rela *rel,
+ struct bfd_link_info *info,
+ bfd_vma pc,
+ bfd_vma addr,
+ bfd_byte *contents,
+ const reloc_howto_type *howto,
+ bfd *input_bfd)
{
- riscv_pcrel_hi_reloc entry = {addr, value - addr};
+ /* 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};
riscv_pcrel_hi_reloc **slot =
(riscv_pcrel_hi_reloc **) htab_find_slot (p->hi_relocs, &entry, INSERT);
Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (input_bfd);
struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (input_bfd);
bfd_vma *local_got_offsets = elf_local_got_offsets (input_bfd);
+ bfd_boolean absolute;
if (!riscv_init_pcrel_relocs (&pcrel_relocs))
return FALSE;
case R_RISCV_SET8:
case R_RISCV_SET16:
case R_RISCV_SET32:
+ case R_RISCV_32_PCREL:
+ case R_RISCV_DELETE:
/* These require no special handling beyond perform_relocation. */
break;
}
}
relocation = sec_addr (htab->elf.sgot) + off;
- if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc, relocation))
+ absolute = riscv_zero_pcrel_hi_reloc (rel,
+ info,
+ pc,
+ relocation,
+ contents,
+ howto,
+ input_bfd);
+ r_type = ELFNN_R_TYPE (rel->r_info);
+ howto = riscv_elf_rtype_to_howto (r_type);
+ if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
+ relocation, absolute))
r = bfd_reloc_overflow;
break;
}
case R_RISCV_PCREL_HI20:
+ absolute = riscv_zero_pcrel_hi_reloc (rel,
+ info,
+ pc,
+ relocation,
+ contents,
+ howto,
+ input_bfd);
+ r_type = ELFNN_R_TYPE (rel->r_info);
+ howto = riscv_elf_rtype_to_howto (r_type);
if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
- relocation + rel->r_addend))
+ relocation + rel->r_addend,
+ absolute))
r = bfd_reloc_overflow;
break;
BFD_ASSERT (off < (bfd_vma) -2);
relocation = sec_addr (htab->elf.sgot) + off + (is_ie ? ie_off : 0);
- if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc, relocation))
+ if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
+ relocation, FALSE))
r = bfd_reloc_overflow;
unresolved_reloc = FALSE;
break;
rel->r_offset) != (bfd_vma) -1)
{
(*_bfd_error_handler)
- (_("%B(%A+0x%lx): unresolvable %s relocation against symbol `%s'"),
+ (_("%B(%A+%#Lx): unresolvable %s relocation against symbol `%s'"),
input_bfd,
input_section,
- (long) rel->r_offset,
+ rel->r_offset,
howto->name,
h->root.root.string);
continue;
if (h->needs_copy)
{
Elf_Internal_Rela rela;
+ asection *s;
/* This symbols needs a copy reloc. Set it up. */
BFD_ASSERT (h->dynindx != -1);
rela.r_offset = sec_addr (h->root.u.def.section) + h->root.u.def.value;
rela.r_info = ELFNN_R_INFO (h->dynindx, R_RISCV_COPY);
rela.r_addend = 0;
- riscv_elf_append_rela (output_bfd, htab->srelbss, &rela);
+ if (h->root.u.def.section == htab->elf.sdynrelro)
+ s = htab->elf.sreldynrelro;
+ else
+ s = htab->elf.srelbss;
+ riscv_elf_append_rela (output_bfd, s, &rela);
}
/* Mark some specially defined symbols as absolute. */
ret = riscv_finish_dyn (output_bfd, info, dynobj, sdyn);
- if (ret != TRUE)
+ if (!ret)
return ret;
/* Fill in the head and tail entries in the procedure linkage table. */
for (i = 0; i < PLT_HEADER_INSNS; i++)
bfd_put_32 (output_bfd, plt_header[i], splt->contents + 4*i);
- }
- elf_section_data (splt->output_section)->this_hdr.sh_entsize
- = PLT_ENTRY_SIZE;
+ elf_section_data (splt->output_section)->this_hdr.sh_entsize
+ = PLT_ENTRY_SIZE;
+ }
}
if (htab->elf.sgotplt)
return TRUE;
}
+/* A second format for recording PC-relative hi relocations. This stores the
+ information required to relax them to GP-relative addresses. */
+
+typedef struct riscv_pcgp_hi_reloc riscv_pcgp_hi_reloc;
+struct riscv_pcgp_hi_reloc
+{
+ bfd_vma hi_sec_off;
+ bfd_vma hi_addend;
+ bfd_vma hi_addr;
+ unsigned hi_sym;
+ asection *sym_sec;
+ riscv_pcgp_hi_reloc *next;
+};
+
+typedef struct riscv_pcgp_lo_reloc riscv_pcgp_lo_reloc;
+struct riscv_pcgp_lo_reloc
+{
+ bfd_vma hi_sec_off;
+ riscv_pcgp_lo_reloc *next;
+};
+
+typedef struct
+{
+ riscv_pcgp_hi_reloc *hi;
+ riscv_pcgp_lo_reloc *lo;
+} riscv_pcgp_relocs;
+
+static bfd_boolean
+riscv_init_pcgp_relocs (riscv_pcgp_relocs *p)
+{
+ p->hi = NULL;
+ p->lo = NULL;
+ return TRUE;
+}
+
+static void
+riscv_free_pcgp_relocs (riscv_pcgp_relocs *p,
+ bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec ATTRIBUTE_UNUSED)
+{
+ riscv_pcgp_hi_reloc *c;
+ riscv_pcgp_lo_reloc *l;
+
+ for (c = p->hi; c != NULL;)
+ {
+ riscv_pcgp_hi_reloc *next = c->next;
+ free (c);
+ c = next;
+ }
+
+ for (l = p->lo; l != NULL;)
+ {
+ riscv_pcgp_lo_reloc *next = l->next;
+ free (l);
+ l = next;
+ }
+}
+
+static bfd_boolean
+riscv_record_pcgp_hi_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off,
+ bfd_vma hi_addend, bfd_vma hi_addr,
+ unsigned hi_sym, asection *sym_sec)
+{
+ riscv_pcgp_hi_reloc *new = bfd_malloc (sizeof(*new));
+ if (!new)
+ return FALSE;
+ new->hi_sec_off = hi_sec_off;
+ new->hi_addend = hi_addend;
+ new->hi_addr = hi_addr;
+ new->hi_sym = hi_sym;
+ new->sym_sec = sym_sec;
+ new->next = p->hi;
+ p->hi = new;
+ return TRUE;
+}
+
+static riscv_pcgp_hi_reloc *
+riscv_find_pcgp_hi_reloc(riscv_pcgp_relocs *p, bfd_vma hi_sec_off)
+{
+ riscv_pcgp_hi_reloc *c;
+
+ for (c = p->hi; c != NULL; c = c->next)
+ if (c->hi_sec_off == hi_sec_off)
+ return c;
+ return NULL;
+}
+
+static bfd_boolean
+riscv_delete_pcgp_hi_reloc(riscv_pcgp_relocs *p, bfd_vma hi_sec_off)
+{
+ bfd_boolean out = FALSE;
+ riscv_pcgp_hi_reloc *c;
+
+ for (c = p->hi; c != NULL; c = c->next)
+ if (c->hi_sec_off == hi_sec_off)
+ out = TRUE;
+
+ return out;
+}
+
+static bfd_boolean
+riscv_use_pcgp_hi_reloc(riscv_pcgp_relocs *p, bfd_vma hi_sec_off)
+{
+ bfd_boolean out = FALSE;
+ riscv_pcgp_hi_reloc *c;
+
+ for (c = p->hi; c != NULL; c = c->next)
+ if (c->hi_sec_off == hi_sec_off)
+ out = TRUE;
+
+ return out;
+}
+
+static bfd_boolean
+riscv_record_pcgp_lo_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off)
+{
+ riscv_pcgp_lo_reloc *new = bfd_malloc (sizeof(*new));
+ if (!new)
+ return FALSE;
+ new->hi_sec_off = hi_sec_off;
+ new->next = p->lo;
+ p->lo = new;
+ return TRUE;
+}
+
+static bfd_boolean
+riscv_find_pcgp_lo_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off)
+{
+ riscv_pcgp_lo_reloc *c;
+
+ for (c = p->lo; c != NULL; c = c->next)
+ if (c->hi_sec_off == hi_sec_off)
+ return TRUE;
+ return FALSE;
+}
+
+static bfd_boolean
+riscv_delete_pcgp_lo_reloc (riscv_pcgp_relocs *p ATTRIBUTE_UNUSED,
+ bfd_vma lo_sec_off ATTRIBUTE_UNUSED,
+ size_t bytes ATTRIBUTE_UNUSED)
+{
+ return TRUE;
+}
+
typedef bfd_boolean (*relax_func_t) (bfd *, asection *, asection *,
struct bfd_link_info *,
Elf_Internal_Rela *,
- bfd_vma, bfd_vma, bfd_vma, bfd_boolean *);
+ bfd_vma, bfd_vma, bfd_vma, bfd_boolean *,
+ riscv_pcgp_relocs *);
/* Relax AUIPC + JALR into JAL. */
bfd_vma symval,
bfd_vma max_alignment,
bfd_vma reserve_size ATTRIBUTE_UNUSED,
- bfd_boolean *again)
+ bfd_boolean *again,
+ riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED)
{
bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
bfd_signed_vma foff = symval - (sec_addr (sec) + rel->r_offset);
bfd_vma symval,
bfd_vma max_alignment,
bfd_vma reserve_size,
- bfd_boolean *again)
+ bfd_boolean *again,
+ riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED)
{
bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
bfd_vma gp = riscv_global_pointer_value (link_info);
BFD_ASSERT (rel->r_offset + 4 <= sec->size);
+ if (gp)
+ {
+ /* If gp and the symbol are in the same output section, then
+ consider only that section's alignment. */
+ struct bfd_link_hash_entry *h =
+ bfd_link_hash_lookup (link_info->hash, RISCV_GP_SYMBOL, FALSE, FALSE,
+ TRUE);
+ if (h->u.def.section->output_section == sym_sec->output_section)
+ max_alignment = (bfd_vma) 1 << sym_sec->output_section->alignment_power;
+ }
+
/* Is the reference in range of x0 or gp?
Valid gp range conservatively because of alignment issue. */
if (VALID_ITYPE_IMM (symval)
&& VALID_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (symval))
&& VALID_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (symval + ELF_MAXPAGESIZE)))
{
- /* Replace LUI with C.LUI if legal (i.e., rd != x2/sp). */
+ /* Replace LUI with C.LUI if legal (i.e., rd != x0 and rd != x2/sp). */
bfd_vma lui = bfd_get_32 (abfd, contents + rel->r_offset);
- if (((lui >> OP_SH_RD) & OP_MASK_RD) == X_SP)
+ unsigned rd = ((unsigned)lui >> OP_SH_RD) & OP_MASK_RD;
+ if (rd == 0 || rd == X_SP)
return TRUE;
lui = (lui & (OP_MASK_RD << OP_SH_RD)) | MATCH_C_LUI;
bfd_vma symval,
bfd_vma max_alignment ATTRIBUTE_UNUSED,
bfd_vma reserve_size ATTRIBUTE_UNUSED,
- bfd_boolean *again)
+ bfd_boolean *again,
+ riscv_pcgp_relocs *prcel_relocs ATTRIBUTE_UNUSED)
{
/* See if this symbol is in range of tp. */
if (RISCV_CONST_HIGH_PART (tpoff (link_info, symval)) != 0)
static bfd_boolean
_bfd_riscv_relax_align (bfd *abfd, asection *sec,
- asection *sym_sec ATTRIBUTE_UNUSED,
+ asection *sym_sec,
struct bfd_link_info *link_info ATTRIBUTE_UNUSED,
Elf_Internal_Rela *rel,
bfd_vma symval,
bfd_vma max_alignment ATTRIBUTE_UNUSED,
bfd_vma reserve_size ATTRIBUTE_UNUSED,
- bfd_boolean *again ATTRIBUTE_UNUSED)
+ bfd_boolean *again ATTRIBUTE_UNUSED,
+ riscv_pcgp_relocs *pcrel_relocs ATTRIBUTE_UNUSED)
{
bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
bfd_vma alignment = 1, pos;
/* Make sure there are enough NOPs to actually achieve the alignment. */
if (rel->r_addend < nop_bytes)
- return FALSE;
+ {
+ (*_bfd_error_handler)
+ (_("%B(%A+0x%lx): %d bytes required for alignment"
+ "to %d-byte boundary, but only %d present"),
+ abfd, sym_sec, rel->r_offset, nop_bytes, alignment, rel->r_addend);
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
/* Delete the reloc. */
rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE);
rel->r_addend - nop_bytes);
}
-/* Relax a section. Pass 0 shortens code sequences unless disabled.
- Pass 1, which cannot be disabled, handles code alignment directives. */
+/* Relax PC-relative references to GP-relative references. */
+
+static bfd_boolean
+_bfd_riscv_relax_pc (bfd *abfd,
+ asection *sec,
+ asection *sym_sec,
+ struct bfd_link_info *link_info,
+ Elf_Internal_Rela *rel,
+ bfd_vma symval,
+ bfd_vma max_alignment,
+ bfd_vma reserve_size,
+ bfd_boolean *again ATTRIBUTE_UNUSED,
+ riscv_pcgp_relocs *pcgp_relocs)
+{
+ bfd_vma gp = riscv_global_pointer_value (link_info);
+
+ BFD_ASSERT (rel->r_offset + 4 <= sec->size);
+
+ /* Chain the _LO relocs to their cooresponding _HI reloc to compute the
+ * actual target address. */
+ riscv_pcgp_hi_reloc hi_reloc = {0};
+ switch (ELFNN_R_TYPE (rel->r_info))
+ {
+ case R_RISCV_PCREL_LO12_I:
+ case R_RISCV_PCREL_LO12_S:
+ {
+ riscv_pcgp_hi_reloc *hi = riscv_find_pcgp_hi_reloc (pcgp_relocs,
+ symval - sec_addr(sym_sec));
+ if (hi == NULL)
+ {
+ riscv_record_pcgp_lo_reloc (pcgp_relocs, symval - sec_addr(sym_sec));
+ return TRUE;
+ }
+
+ hi_reloc = *hi;
+ symval = hi_reloc.hi_addr;
+ sym_sec = hi_reloc.sym_sec;
+ if (!riscv_use_pcgp_hi_reloc(pcgp_relocs, hi->hi_sec_off))
+ (*_bfd_error_handler)
+ (_("%B(%A+0x%lx): Unable to clear RISCV_PCREL_HI20 reloc"
+ "for cooresponding RISCV_PCREL_LO12 reloc"),
+ abfd, sec, rel->r_offset);
+ }
+ break;
+
+ case R_RISCV_PCREL_HI20:
+ /* Mergeable symbols and code might later move out of range. */
+ if (sym_sec->flags & (SEC_MERGE | SEC_CODE))
+ return TRUE;
+
+ /* If the cooresponding lo relocation has already been seen then it's not
+ * safe to relax this relocation. */
+ if (riscv_find_pcgp_lo_reloc (pcgp_relocs, rel->r_offset))
+ return TRUE;
+
+ break;
+
+ default:
+ abort ();
+ }
+
+ if (gp)
+ {
+ /* If gp and the symbol are in the same output section, then
+ consider only that section's alignment. */
+ struct bfd_link_hash_entry *h =
+ bfd_link_hash_lookup (link_info->hash, RISCV_GP_SYMBOL, FALSE, FALSE, TRUE);
+ if (h->u.def.section->output_section == sym_sec->output_section)
+ max_alignment = (bfd_vma) 1 << sym_sec->output_section->alignment_power;
+ }
+
+ /* Is the reference in range of x0 or gp?
+ Valid gp range conservatively because of alignment issue. */
+ if (VALID_ITYPE_IMM (symval)
+ || (symval >= gp
+ && VALID_ITYPE_IMM (symval - gp + max_alignment + reserve_size))
+ || (symval < gp
+ && VALID_ITYPE_IMM (symval - gp - max_alignment - reserve_size)))
+ {
+ unsigned sym = hi_reloc.hi_sym;
+ switch (ELFNN_R_TYPE (rel->r_info))
+ {
+ case R_RISCV_PCREL_LO12_I:
+ rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_I);
+ rel->r_addend += hi_reloc.hi_addend;
+ return riscv_delete_pcgp_lo_reloc (pcgp_relocs, rel->r_offset, 4);
+
+ case R_RISCV_PCREL_LO12_S:
+ rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_S);
+ rel->r_addend += hi_reloc.hi_addend;
+ return riscv_delete_pcgp_lo_reloc (pcgp_relocs, rel->r_offset, 4);
+
+ case R_RISCV_PCREL_HI20:
+ riscv_record_pcgp_hi_reloc (pcgp_relocs,
+ rel->r_offset,
+ rel->r_addend,
+ symval,
+ ELFNN_R_SYM(rel->r_info),
+ sym_sec);
+ /* We can delete the unnecessary AUIPC and reloc. */
+ rel->r_info = ELFNN_R_INFO (0, R_RISCV_DELETE);
+ rel->r_addend = 4;
+ return riscv_delete_pcgp_hi_reloc (pcgp_relocs, rel->r_offset);
+
+ default:
+ abort ();
+ }
+ }
+
+ return TRUE;
+}
+
+/* Relax PC-relative references to GP-relative references. */
+
+static bfd_boolean
+_bfd_riscv_relax_delete (bfd *abfd,
+ asection *sec,
+ asection *sym_sec ATTRIBUTE_UNUSED,
+ struct bfd_link_info *link_info ATTRIBUTE_UNUSED,
+ Elf_Internal_Rela *rel,
+ bfd_vma symval ATTRIBUTE_UNUSED,
+ bfd_vma max_alignment ATTRIBUTE_UNUSED,
+ bfd_vma reserve_size ATTRIBUTE_UNUSED,
+ bfd_boolean *again ATTRIBUTE_UNUSED,
+ riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED)
+{
+ if (!riscv_relax_delete_bytes(abfd, sec, rel->r_offset, rel->r_addend))
+ return FALSE;
+ rel->r_info = ELFNN_R_INFO(0, R_RISCV_NONE);
+ return TRUE;
+}
+
+/* Relax a section. Pass 0 shortens code sequences unless disabled. Pass 1
+ deletes the bytes that pass 0 made obselete. Pass 2, which cannot be
+ disabled, handles code alignment directives. */
static bfd_boolean
_bfd_riscv_relax_section (bfd *abfd, asection *sec,
bfd_boolean ret = FALSE;
unsigned int i;
bfd_vma max_alignment, reserve_size = 0;
+ riscv_pcgp_relocs pcgp_relocs;
*again = FALSE;
&& info->relax_pass == 0))
return TRUE;
+ riscv_init_pcgp_relocs (&pcgp_relocs);
+
/* Read this BFD's relocs if we haven't done so already. */
if (data->relocs)
relocs = data->relocs;
info->keep_memory)))
goto fail;
- max_alignment = _bfd_riscv_get_max_alignment (sec);
+ if (htab)
+ {
+ max_alignment = htab->max_alignment;
+ if (max_alignment == (bfd_vma) -1)
+ {
+ max_alignment = _bfd_riscv_get_max_alignment (sec);
+ htab->max_alignment = max_alignment;
+ }
+ }
+ else
+ max_alignment = _bfd_riscv_get_max_alignment (sec);
/* Examine and consider relaxing each reloc. */
for (i = 0; i < sec->reloc_count; i++)
int type = ELFNN_R_TYPE (rel->r_info);
bfd_vma symval;
+ relax_func = NULL;
if (info->relax_pass == 0)
{
if (type == R_RISCV_CALL || type == R_RISCV_CALL_PLT)
|| type == R_RISCV_LO12_I
|| type == R_RISCV_LO12_S)
relax_func = _bfd_riscv_relax_lui;
+ else if (!bfd_link_pic(info)
+ && (type == R_RISCV_PCREL_HI20
+ || type == R_RISCV_PCREL_LO12_I
+ || type == R_RISCV_PCREL_LO12_S))
+ relax_func = _bfd_riscv_relax_pc;
else if (type == R_RISCV_TPREL_HI20
|| type == R_RISCV_TPREL_ADD
|| type == R_RISCV_TPREL_LO12_I
/* Skip over the R_RISCV_RELAX. */
i++;
}
- else if (type == R_RISCV_ALIGN)
+ else if (info->relax_pass == 1 && type == R_RISCV_DELETE)
+ relax_func = _bfd_riscv_relax_delete;
+ else if (info->relax_pass == 2 && type == R_RISCV_ALIGN)
relax_func = _bfd_riscv_relax_align;
else
continue;
symval += rel->r_addend;
if (!relax_func (abfd, sec, sym_sec, info, rel, symval,
- max_alignment, reserve_size, again))
+ max_alignment, reserve_size, again,
+ &pcgp_relocs))
goto fail;
}
fail:
if (relocs != data->relocs)
free (relocs);
+ riscv_free_pcgp_relocs(&pcgp_relocs, abfd, sec);
return ret;
}
return TRUE;
}
+/* Set the right mach type. */
+static bfd_boolean
+riscv_elf_object_p (bfd *abfd)
+{
+ /* There are only two mach types in RISCV currently. */
+ if (strcmp (abfd->xvec->name, "elf32-littleriscv") == 0)
+ bfd_default_set_arch_mach (abfd, bfd_arch_riscv, bfd_mach_riscv32);
+ else
+ bfd_default_set_arch_mach (abfd, bfd_arch_riscv, bfd_mach_riscv64);
+
+ return TRUE;
+}
+
#define TARGET_LITTLE_SYM riscv_elfNN_vec
#define TARGET_LITTLE_NAME "elfNN-littleriscv"
#define elf_backend_finish_dynamic_symbol riscv_elf_finish_dynamic_symbol
#define elf_backend_finish_dynamic_sections riscv_elf_finish_dynamic_sections
#define elf_backend_gc_mark_hook riscv_elf_gc_mark_hook
-#define elf_backend_gc_sweep_hook riscv_elf_gc_sweep_hook
#define elf_backend_plt_sym_val riscv_elf_plt_sym_val
#define elf_backend_grok_prstatus riscv_elf_grok_prstatus
#define elf_backend_grok_psinfo riscv_elf_grok_psinfo
+#define elf_backend_object_p riscv_elf_object_p
#define elf_info_to_howto_rel NULL
#define elf_info_to_howto riscv_info_to_howto_rela
#define bfd_elfNN_bfd_relax_section _bfd_riscv_relax_section
#define elf_backend_plt_alignment 4
#define elf_backend_want_plt_sym 1
#define elf_backend_got_header_size (ARCH_SIZE / 8)
+#define elf_backend_want_dynrelro 1
#define elf_backend_rela_normal 1
#define elf_backend_default_execstack 0