/* RISC-V-specific support for NN-bit ELF.
- Copyright (C) 2011-2017 Free Software Foundation, Inc.
+ Copyright (C) 2011-2018 Free Software Foundation, Inc.
Contributed by Andrew Waterman (andrew@sifive.com).
Based on TILE-Gx and MIPS targets.
#define ELF_MAXPAGESIZE 0x1000
#define ELF_COMMONPAGESIZE 0x1000
-/* The RISC-V linker needs to keep track of the number of relocs that it
- decides to copy as dynamic relocs in check_relocs for each symbol.
- This is so that it can later discard them if they are found to be
- unnecessary. We store the information in a field extending the
- regular ELF linker hash table. */
-
-struct riscv_elf_dyn_relocs
-{
- struct riscv_elf_dyn_relocs *next;
-
- /* The input section of the reloc. */
- asection *sec;
-
- /* Total number of relocs copied for the input section. */
- bfd_size_type count;
-
- /* Number of pc-relative relocs copied for the input section. */
- bfd_size_type pc_count;
-};
-
/* RISC-V ELF linker hash entry. */
struct riscv_elf_link_hash_entry
struct elf_link_hash_entry elf;
/* Track dynamic relocs copied for this symbol. */
- struct riscv_elf_dyn_relocs *dyn_relocs;
+ struct elf_dyn_relocs *dyn_relocs;
#define GOT_UNKNOWN 0
#define GOT_NORMAL 1
bfd_vma gotplt_offset_low = RISCV_PCREL_LOW_PART (gotplt_addr, addr);
/* auipc t2, %hi(.got.plt)
- sub t1, t1, t3 # shifted .got.plt offset + hdr size + 12
+ sub t1, t1, t3 # shifted .got.plt offset + hdr size + 12
l[w|d] t3, %lo(.got.plt)(t2) # _dl_runtime_resolve
addi t1, t1, -(hdr size + 12) # shifted .got.plt offset
addi t0, t2, %lo(.got.plt) # &.got.plt
srli t1, t1, log2(16/PTRSIZE) # .got.plt offset
- l[w|d] t0, PTRSIZE(t0) # link map
- jr t3 */
+ l[w|d] t0, PTRSIZE(t0) # link map
+ jr t3 */
entry[0] = RISCV_UTYPE (AUIPC, X_T2, gotplt_offset_high);
entry[1] = RISCV_RTYPE (SUB, X_T1, X_T1, X_T3);
{
if (edir->dyn_relocs != NULL)
{
- struct riscv_elf_dyn_relocs **pp;
- struct riscv_elf_dyn_relocs *p;
+ struct elf_dyn_relocs **pp;
+ struct elf_dyn_relocs *p;
/* Add reloc counts against the indirect sym to the direct sym
list. Merge any entries against the same section. */
for (pp = &eind->dyn_relocs; (p = *pp) != NULL; )
{
- struct riscv_elf_dyn_relocs *q;
+ struct elf_dyn_relocs *q;
for (q = edir->dyn_relocs; q != NULL; q = q->next)
if (q->sec == p->sec)
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;
-
- /* PR15323, ref flags aren't set for references in the same
- object. */
- h->root.non_ir_ref_regular = 1;
}
switch (r_type)
&& (h->root.type == bfd_link_hash_defweak
|| !h->def_regular)))
{
- struct riscv_elf_dyn_relocs *p;
- struct riscv_elf_dyn_relocs **head;
+ struct elf_dyn_relocs *p;
+ struct elf_dyn_relocs **head;
/* When creating a shared object, we must copy these
relocs into the output file. We create a reloc
s = sec;
vpp = &elf_section_data (s)->local_dynrel;
- head = (struct riscv_elf_dyn_relocs **) vpp;
+ head = (struct elf_dyn_relocs **) vpp;
}
p = *head;
if (p == NULL || p->sec != sec)
{
bfd_size_type amt = sizeof *p;
- p = ((struct riscv_elf_dyn_relocs *)
+ p = ((struct elf_dyn_relocs *)
bfd_alloc (htab->elf.dynobj, amt));
if (p == NULL)
return FALSE;
return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym);
}
+/* Find dynamic relocs for H that apply to read-only sections. */
+
+static asection *
+readonly_dynrelocs (struct elf_link_hash_entry *h)
+{
+ struct elf_dyn_relocs *p;
+
+ for (p = riscv_elf_hash_entry (h)->dyn_relocs; p != NULL; p = p->next)
+ {
+ asection *s = p->sec->output_section;
+
+ if (s != NULL && (s->flags & SEC_READONLY) != 0)
+ return p->sec;
+ }
+ return NULL;
+}
+
/* 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_table *htab;
struct riscv_elf_link_hash_entry * eh;
- struct riscv_elf_dyn_relocs *p;
bfd *dynobj;
asection *s, *srel;
return TRUE;
}
- eh = (struct riscv_elf_link_hash_entry *) h;
- for (p = eh->dyn_relocs; p != NULL; p = p->next)
- {
- s = p->sec->output_section;
- if (s != NULL && (s->flags & SEC_READONLY) != 0)
- break;
- }
-
- /* If we didn't find any dynamic relocs in read-only sections, then
+ /* If we don't find any dynamic relocs in read-only sections, then
we'll be keeping the dynamic relocs and avoiding the copy reloc. */
- if (p == NULL)
+ if (!readonly_dynrelocs (h))
{
h->non_got_ref = 0;
return TRUE;
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. */
+ eh = (struct riscv_elf_link_hash_entry *) h;
if (eh->tls_type & ~GOT_NORMAL)
{
s = htab->sdyntdata;
struct bfd_link_info *info;
struct riscv_elf_link_hash_table *htab;
struct riscv_elf_link_hash_entry *eh;
- struct riscv_elf_dyn_relocs *p;
+ struct elf_dyn_relocs *p;
if (h->root.type == bfd_link_hash_indirect)
return TRUE;
{
if (SYMBOL_CALLS_LOCAL (info, h))
{
- struct riscv_elf_dyn_relocs **pp;
+ struct elf_dyn_relocs **pp;
for (pp = &eh->dyn_relocs; (p = *pp) != NULL; )
{
return TRUE;
}
-/* Find any dynamic relocs that apply to read-only sections. */
+/* Set DF_TEXTREL if we find any dynamic relocs that apply to
+ read-only sections. */
static bfd_boolean
-readonly_dynrelocs (struct elf_link_hash_entry *h, void *inf)
+maybe_set_textrel (struct elf_link_hash_entry *h, void *info_p)
{
- struct riscv_elf_link_hash_entry *eh;
- struct riscv_elf_dyn_relocs *p;
+ asection *sec;
- eh = (struct riscv_elf_link_hash_entry *) h;
- for (p = eh->dyn_relocs; p != NULL; p = p->next)
+ if (h->root.type == bfd_link_hash_indirect)
+ return TRUE;
+
+ sec = readonly_dynrelocs (h);
+ if (sec != NULL)
{
- asection *s = p->sec->output_section;
+ struct bfd_link_info *info = (struct bfd_link_info *) info_p;
- if (s != NULL && (s->flags & SEC_READONLY) != 0)
- {
- ((struct bfd_link_info *) inf)->flags |= DF_TEXTREL;
- return FALSE;
- }
+ info->flags |= DF_TEXTREL;
+ info->callbacks->minfo
+ (_("%B: dynamic relocation against `%T' in read-only section `%A'\n"),
+ sec->owner, h->root.root.string, sec);
+
+ /* Not an error, just cut short the traversal. */
+ return FALSE;
}
return TRUE;
}
for (s = ibfd->sections; s != NULL; s = s->next)
{
- struct riscv_elf_dyn_relocs *p;
+ struct elf_dyn_relocs *p;
for (p = elf_section_data (s)->local_dynrel; p != NULL; p = p->next)
{
/* If any dynamic relocs apply to a read-only section,
then we need a DT_TEXTREL entry. */
if ((info->flags & DF_TEXTREL) == 0)
- elf_link_hash_traverse (&htab->elf, readonly_dynrelocs, info);
+ elf_link_hash_traverse (&htab->elf, maybe_set_textrel, info);
if (info->flags & DF_TEXTREL)
{
typedef struct riscv_pcrel_lo_reloc
{
- asection * input_section;
- struct bfd_link_info * info;
- reloc_howto_type * howto;
- const Elf_Internal_Rela * reloc;
- bfd_vma addr;
- const char * name;
- bfd_byte * contents;
- struct riscv_pcrel_lo_reloc * next;
+ asection * input_section;
+ struct bfd_link_info * info;
+ reloc_howto_type * howto;
+ const Elf_Internal_Rela * reloc;
+ bfd_vma addr;
+ const char * name;
+ bfd_byte * contents;
+ struct riscv_pcrel_lo_reloc * next;
} riscv_pcrel_lo_reloc;
typedef struct
riscv_pcrel_hi_reloc search = {r->addr, 0};
riscv_pcrel_hi_reloc *entry = htab_find (p->hi_relocs, &search);
if (entry == NULL)
- {
+ {
((*r->info->callbacks->reloc_overflow)
(r->info, NULL, r->name, r->howto->name, (bfd_vma) 0,
input_bfd, r->input_section, r->reloc->r_offset));
return TRUE;
- }
+ }
perform_relocation (r->howto, r->reloc, entry->value, r->input_section,
input_bfd, r->contents);
case R_RISCV_PCREL_LO12_I:
case R_RISCV_PCREL_LO12_S:
+ /* Addends are not allowed, because then riscv_relax_delete_bytes
+ would have to search through all relocs to update the addends.
+ Also, riscv_resolve_pcrel_lo_relocs does not support addends
+ when searching for a matching hi reloc. */
+ if (rel->r_addend)
+ {
+ r = bfd_reloc_dangerous;
+ break;
+ }
+
if (riscv_record_pcrel_lo_reloc (&pcrel_relocs, input_section, info,
howto, rel, relocation, name,
contents))
}
/* The GOT entries have not been initialized yet. Do it
- now, and emit any relocations. */
+ now, and emit any relocations. */
if ((bfd_link_pic (info) || indx != 0)
&& (h == NULL
|| ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
break;
case bfd_reloc_outofrange:
- msg = _("internal error: out of range error");
+ msg = _("%X%P: internal error: out of range error\n");
break;
case bfd_reloc_notsupported:
- msg = _("internal error: unsupported relocation error");
+ msg = _("%X%P: internal error: unsupported relocation error\n");
break;
case bfd_reloc_dangerous:
- msg = _("internal error: dangerous relocation");
+ info->callbacks->reloc_dangerous
+ (info, "%pcrel_lo with addend", input_bfd, input_section,
+ rel->r_offset);
break;
default:
- msg = _("internal error: unknown error");
+ msg = _("%X%P: internal error: unknown error\n");
break;
}
if (msg)
- info->callbacks->warning
- (info, msg, name, input_bfd, input_section, rel->r_offset);
+ info->callbacks->einfo (msg);
+
+ /* We already reported the error via a callback, so don't try to report
+ it again by returning false. That leads to spurious errors. */
+ ret = TRUE;
goto out;
}
/* Delete some bytes from a section while relaxing. */
static bfd_boolean
-riscv_relax_delete_bytes (bfd *abfd, asection *sec, bfd_vma addr, size_t count)
+riscv_relax_delete_bytes (bfd *abfd, asection *sec, bfd_vma addr, size_t count,
+ struct bfd_link_info *link_info)
{
unsigned int i, symcount;
bfd_vma toaddr = sec->size;
{
struct elf_link_hash_entry *sym_hash = sym_hashes[i];
+ /* The '--wrap SYMBOL' option is causing a pain when the object file,
+ containing the definition of __wrap_SYMBOL, includes a direct
+ call to SYMBOL as well. Since both __wrap_SYMBOL and SYMBOL reference
+ the same symbol (which is __wrap_SYMBOL), but still exist as two
+ different symbols in 'sym_hashes', we don't want to adjust
+ the global symbol __wrap_SYMBOL twice.
+ This check is only relevant when symbols are being wrapped. */
+ if (link_info->wrap_hash != NULL)
+ {
+ struct elf_link_hash_entry **cur_sym_hashes;
+
+ /* Loop only over the symbols which have already been checked. */
+ for (cur_sym_hashes = sym_hashes; cur_sym_hashes < &sym_hashes[i];
+ cur_sym_hashes++)
+ {
+ /* If the current symbol is identical to 'sym_hash', that means
+ the symbol was already adjusted (or at least checked). */
+ if (*cur_sym_hashes == sym_hash)
+ break;
+ }
+ /* Don't adjust the symbol again. */
+ if (cur_sym_hashes < &sym_hashes[i])
+ continue;
+ }
+
if ((sym_hash->root.type == bfd_link_hash_defined
|| sym_hash->root.type == bfd_link_hash_defweak)
&& sym_hash->root.u.def.section == sec)
/* Delete unnecessary JALR. */
*again = TRUE;
- return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + len, 8 - len);
+ return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + len, 8 - len,
+ link_info);
}
/* Traverse all output sections and return the max alignment. */
/* We can delete the unnecessary LUI and reloc. */
rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE);
*again = TRUE;
- return riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4);
+ return riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4,
+ link_info);
default:
abort ();
rel->r_info = ELFNN_R_INFO (ELFNN_R_SYM (rel->r_info), R_RISCV_RVC_LUI);
*again = TRUE;
- return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + 2, 2);
+ return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + 2, 2,
+ link_info);
}
return TRUE;
/* We can delete the unnecessary instruction and reloc. */
rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE);
*again = TRUE;
- return riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4);
+ return riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4, link_info);
default:
abort ();
static bfd_boolean
_bfd_riscv_relax_align (bfd *abfd, asection *sec,
asection *sym_sec,
- struct bfd_link_info *link_info ATTRIBUTE_UNUSED,
+ struct bfd_link_info *link_info,
Elf_Internal_Rela *rel,
bfd_vma symval,
bfd_vma max_alignment ATTRIBUTE_UNUSED,
/* Delete the excess bytes. */
return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + nop_bytes,
- rel->r_addend - nop_bytes);
+ rel->r_addend - nop_bytes, link_info);
}
/* Relax PC-relative references to GP-relative references. */
/* Chain the _LO relocs to their cooresponding _HI reloc to compute the
* actual target address. */
- riscv_pcgp_hi_reloc hi_reloc = {0};
+ riscv_pcgp_hi_reloc hi_reloc;
+ memset (&hi_reloc, 0, sizeof (hi_reloc));
switch (ELFNN_R_TYPE (rel->r_info))
{
case R_RISCV_PCREL_LO12_I:
/* 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;
+ return TRUE;
break;
return riscv_delete_pcgp_lo_reloc (pcgp_relocs, rel->r_offset, 4);
case R_RISCV_PCREL_HI20:
- riscv_record_pcgp_hi_reloc (pcgp_relocs,
+ riscv_record_pcgp_hi_reloc (pcgp_relocs,
rel->r_offset,
rel->r_addend,
symval,
_bfd_riscv_relax_delete (bfd *abfd,
asection *sec,
asection *sym_sec ATTRIBUTE_UNUSED,
- struct bfd_link_info *link_info ATTRIBUTE_UNUSED,
+ struct bfd_link_info *link_info,
Elf_Internal_Rela *rel,
bfd_vma symval ATTRIBUTE_UNUSED,
bfd_vma max_alignment 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))
+ if (!riscv_relax_delete_bytes(abfd, sec, rel->r_offset, rel->r_addend,
+ link_info))
return FALSE;
rel->r_info = ELFNN_R_INFO(0, R_RISCV_NONE);
return TRUE;
i++;
}
else if (info->relax_pass == 1 && type == R_RISCV_DELETE)
- relax_func = _bfd_riscv_relax_delete;
+ relax_func = _bfd_riscv_relax_delete;
else if (info->relax_pass == 2 && type == R_RISCV_ALIGN)
relax_func = _bfd_riscv_relax_align;
else
{
BFD_ASSERT (isym->st_shndx < elf_numsections (abfd));
sym_sec = elf_elfsections (abfd)[isym->st_shndx]->bfd_section;
+#if 0
+ /* The purpose of this code is unknown. It breaks linker scripts
+ for embedded development that place sections at address zero.
+ This code is believed to be unnecessary. Disabling it but not
+ yet removing it, in case something breaks. */
if (sec_addr (sym_sec) == 0)
continue;
+#endif
symval = sec_addr (sym_sec) + isym->st_value;
}
}
#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_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_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