#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)
/* 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;
}
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
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 ((h->root.u.def.section->flags & SEC_READONLY) != 0)
+ 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;
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, s);
}
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)
+{
+ /* 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)
{
- riscv_pcrel_hi_reloc entry = {addr, value - addr};
+ 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;
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. */
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);
&& 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;
}
#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